[fix]修改视频相关
This commit is contained in:
parent
99b07e32fb
commit
b8702f827c
@ -19,6 +19,13 @@ import com.wzj.soopin.member.service.IAccountBillService;
|
||||
import com.wzj.soopin.member.service.IMemberAccountService;
|
||||
import com.wzj.soopin.member.service.IMemberBankService;
|
||||
import com.wzj.soopin.member.service.IMemberService;
|
||||
import com.wzj.soopin.transaction.convert.ChargeConvert;
|
||||
import com.wzj.soopin.transaction.convert.WithdrawConvert;
|
||||
import com.wzj.soopin.transaction.domain.bo.ChargeBO;
|
||||
import com.wzj.soopin.transaction.domain.bo.WithdrawBO;
|
||||
import com.wzj.soopin.transaction.enums.WithdrawType;
|
||||
import com.wzj.soopin.transaction.service.IChargeService;
|
||||
import com.wzj.soopin.transaction.service.IWithdrawService;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
@ -30,6 +37,8 @@ import org.dromara.common.core.domain.model.SocialLoginBody;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.ValidatorUtils;
|
||||
import org.dromara.common.json.utils.JsonUtils;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.common.social.config.properties.SocialProperties;
|
||||
import org.dromara.common.social.utils.SocialUtils;
|
||||
@ -56,7 +65,11 @@ public class AppMemberController {
|
||||
|
||||
private final IMemberBankService memberBankService;
|
||||
private final MemberBankConvert memberBankConvert;
|
||||
private final IChargeService chargeService;
|
||||
private final ChargeConvert chargeConvert;
|
||||
|
||||
private final IWithdrawService withdrawService;
|
||||
private final WithdrawConvert withdrawConvert;
|
||||
|
||||
@Tag(name = "获取会员账户信息详细信息")
|
||||
@GetMapping(value = "/info")
|
||||
@ -145,4 +158,35 @@ public class AppMemberController {
|
||||
return R.ok(authUserData.getToken());
|
||||
}
|
||||
|
||||
@Tag(name = "充值")
|
||||
@Log(title = "新增 ", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/charge")
|
||||
public R charge(@RequestBody ChargeBO bo) {
|
||||
|
||||
//获取用户信息
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
if (loginUser == null) {
|
||||
throw new ServiceException("用户未登录");
|
||||
}
|
||||
Long memberId = loginUser.getUserId();
|
||||
bo.setMemberId(memberId);
|
||||
|
||||
return R.ok(chargeService.charge(chargeConvert.toPo(bo)));
|
||||
}
|
||||
@Tag(name = "提现")
|
||||
@Log(title = "提现 ", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/withdraw")
|
||||
public R withdraw(@RequestBody WithdrawBO bo) {
|
||||
//获取用户信息
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
if (loginUser == null) {
|
||||
throw new ServiceException("用户未登录");
|
||||
}
|
||||
Long memberId = loginUser.getUserId();
|
||||
bo.setMemberId(memberId);
|
||||
bo.setType(WithdrawType.WALLET.getCode());
|
||||
return R.ok(withdrawService.withdrawWallet(withdrawConvert.toPo(bo)));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,76 @@
|
||||
package org.dromara.app;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.wzj.soopin.content.domain.bo.IndexListBO;
|
||||
import com.wzj.soopin.content.domain.bo.MyListBO;
|
||||
import com.wzj.soopin.content.domain.bo.SimpleListBO;
|
||||
import com.wzj.soopin.content.domain.bo.VlogBO;
|
||||
import com.wzj.soopin.content.service.VlogService;
|
||||
import com.wzj.soopin.content.service.VlogUploadService;
|
||||
import com.wzj.soopin.content.utils.PagedGridResult;
|
||||
import com.wzj.soopin.content.utils.QcCloud;
|
||||
import com.wzj.soopin.content.utils.RedisOperator;
|
||||
import com.wzj.soopin.order.domain.bo.OrderBo;
|
||||
import com.wzj.soopin.order.domain.entity.Order;
|
||||
import com.wzj.soopin.order.domain.vo.OrderVO;
|
||||
import com.wzj.soopin.order.service.OrderItemService;
|
||||
import com.wzj.soopin.order.service.impl.OrderServiceImpl;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.domain.model.LoginUser;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.wzj.soopin.content.domain.base.BaseInfoProperties.*;
|
||||
|
||||
|
||||
@Slf4j
|
||||
@Api(tags = "订单相关接口")
|
||||
@RequestMapping("/app/order")
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class AppOrderController {
|
||||
|
||||
private final OrderServiceImpl orderService;
|
||||
|
||||
private final OrderItemService orderItemService;
|
||||
|
||||
|
||||
public RedisOperator redis;
|
||||
@Tag(name ="查询订单列表")
|
||||
@PostMapping("/page")
|
||||
public R<IPage<OrderVO>> list(@RequestBody OrderBo query, Page<Order> page){
|
||||
return R.ok(orderService.getlist(page,query));
|
||||
}
|
||||
|
||||
@Tag(name ="新增订单表")
|
||||
@Log(title = "订单表", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/add")
|
||||
public R add(@RequestBody Order order) {
|
||||
R<Order> result = orderService.insert(order);
|
||||
|
||||
if (result != null) {
|
||||
// 订单创建成功,发送消息
|
||||
orderService.sendMessage(order);
|
||||
log.info("订单创建成功,消息已发送,订单ID: {}", order.getId());
|
||||
} else {
|
||||
log.warn("订单创建失败: {}", "返回结果为null");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
105
ruoyi-admin/src/main/java/org/dromara/app/AppPayController.java
Normal file
105
ruoyi-admin/src/main/java/org/dromara/app/AppPayController.java
Normal file
@ -0,0 +1,105 @@
|
||||
package org.dromara.app;
|
||||
|
||||
import com.wzj.soopin.transaction.enums.PaymentClientEnum;
|
||||
import com.wzj.soopin.transaction.enums.PaymentMethodEnum;
|
||||
import com.wzj.soopin.transaction.kit.CashierSupport;
|
||||
import com.wzj.soopin.transaction.kit.dto.PayParam;
|
||||
import com.wzj.soopin.transaction.kit.params.dto.CashierParam;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.constant.ResultCode;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
|
||||
/**
|
||||
* 买家端,收银台接口
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020-12-18 16:59
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@Api(tags = "买家端,收银台接口")
|
||||
@RequestMapping("/app/payment")
|
||||
@RequiredArgsConstructor
|
||||
public class AppPayController {
|
||||
|
||||
|
||||
private final CashierSupport cashierSupport;
|
||||
|
||||
|
||||
// @ApiImplicitParams({
|
||||
// @ApiImplicitParam(name = "client", value = "客户端类型", paramType = "path", allowableValues = "PC,H5,WECHAT_MP,APP")
|
||||
// })
|
||||
@PostMapping(value = "/tradeDetail")
|
||||
@ApiOperation(value = "获取支付详情")
|
||||
public R paymentParams(@RequestBody @Validated PayParam payParam) {
|
||||
|
||||
CashierParam cashierParam = cashierSupport.cashierParam(payParam);
|
||||
return R.ok( cashierParam);
|
||||
}
|
||||
|
||||
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "paymentMethod", value = "支付方式", paramType = "path", allowableValues = "WECHAT,ALIPAY"),
|
||||
@ApiImplicitParam(name = "paymentClient", value = "调起方式", paramType = "path", allowableValues = "APP,NATIVE,JSAPI,H5,MP")
|
||||
})
|
||||
|
||||
@PostMapping(value = "/pay")
|
||||
@ApiOperation(value = "支付")
|
||||
public R payment(
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
@RequestBody @Validated PayParam payParam) {
|
||||
PaymentMethodEnum paymentMethodEnum = PaymentMethodEnum.valueOf(payParam.getPaymentMethod());
|
||||
PaymentClientEnum paymentClientEnum = PaymentClientEnum.valueOf(payParam.getPaymentClient());
|
||||
|
||||
try {
|
||||
return cashierSupport.payment(paymentMethodEnum, paymentClientEnum, request, response, payParam);
|
||||
} catch (ServiceException se) {
|
||||
log.info("支付异常", se);
|
||||
throw se;
|
||||
} catch (Exception e) {
|
||||
log.error("收银台支付错误", e);
|
||||
}
|
||||
return null;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ApiOperation(value = "支付回调")
|
||||
@RequestMapping(value = "/callback/{paymentMethod}", method = {RequestMethod.GET, RequestMethod.POST})
|
||||
public R<Object> callback(HttpServletRequest request, @PathVariable String paymentMethod) {
|
||||
|
||||
PaymentMethodEnum paymentMethodEnum = PaymentMethodEnum.valueOf(paymentMethod);
|
||||
|
||||
cashierSupport.callback(paymentMethodEnum, request);
|
||||
|
||||
return R.ok(ResultCode.PAY_SUCCESS);
|
||||
}
|
||||
|
||||
@ApiOperation(value = "支付异步通知")
|
||||
@RequestMapping(value = "/notify/{paymentMethod}", method = {RequestMethod.GET, RequestMethod.POST})
|
||||
public void notify(HttpServletRequest request, @PathVariable String paymentMethod) {
|
||||
|
||||
PaymentMethodEnum paymentMethodEnum = PaymentMethodEnum.valueOf(paymentMethod);
|
||||
|
||||
cashierSupport.notify(paymentMethodEnum, request);
|
||||
|
||||
}
|
||||
|
||||
@ApiOperation(value = "查询支付结果")
|
||||
@GetMapping(value = "/result")
|
||||
public R<Boolean> paymentResult(PayParam payParam) {
|
||||
return R.ok(cashierSupport.paymentResult(payParam));
|
||||
}
|
||||
}
|
@ -14,9 +14,11 @@ import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Tag(name ="商品分类接口列表")
|
||||
@RestController
|
||||
@RequestMapping("/app/productCategory")
|
||||
@RequestMapping("/app/product/category")
|
||||
@RequiredArgsConstructor
|
||||
public class AppProductCategoryController {
|
||||
|
||||
@ -30,5 +32,11 @@ public class AppProductCategoryController {
|
||||
Page<ProductCategory> productCategoryPage = service.page(page,query.toWrapper());
|
||||
return R.ok(convert.toVO(productCategoryPage));
|
||||
}
|
||||
@Tag(name = "查询列表")
|
||||
@PostMapping("/tree")
|
||||
public R<List<ProductCategoryVO>> tree(@RequestBody ProductCategoryBo bo ) {
|
||||
List<ProductCategoryVO> articleList = service.tree( bo.toWrapper());
|
||||
return R.ok( articleList);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,44 @@
|
||||
package org.dromara.app;
|
||||
|
||||
import com.wzj.soopin.transaction.convert.WithdrawConvert;
|
||||
import com.wzj.soopin.transaction.domain.bo.WithdrawBO;
|
||||
import com.wzj.soopin.transaction.enums.WithdrawType;
|
||||
import com.wzj.soopin.transaction.service.IWithdrawService;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.domain.model.LoginUser;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Tag(name = "租户")
|
||||
@RestController
|
||||
@RequestMapping("/app/tenant")
|
||||
@RequiredArgsConstructor
|
||||
public class AppTenantController {
|
||||
|
||||
private final IWithdrawService withdrawService;
|
||||
private final WithdrawConvert withdrawConvert;
|
||||
@Tag(name = "提现")
|
||||
@Log(title = "提现 ", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/withdraw")
|
||||
public R withdraw(@RequestBody WithdrawBO bo) {
|
||||
//获取用户信息
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
if (loginUser == null) {
|
||||
throw new ServiceException("用户未登录");
|
||||
}
|
||||
Long memberId = loginUser.getUserId();
|
||||
bo.setMemberId(memberId);
|
||||
bo.setType(WithdrawType.REVENUE.getCode());
|
||||
return R.ok(withdrawService.withdrawRevenue(withdrawConvert.toPo(bo)));
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -2,10 +2,9 @@ package org.dromara.app;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.wzj.soopin.content.domain.bo.IndexListBO;
|
||||
import com.wzj.soopin.content.domain.bo.MyListBO;
|
||||
import com.wzj.soopin.content.domain.bo.SimpleListBO;
|
||||
import com.wzj.soopin.content.domain.bo.VlogBO;
|
||||
import com.wzj.soopin.content.domain.bo.*;
|
||||
import com.wzj.soopin.content.domain.po.MyLikedVlog;
|
||||
import com.wzj.soopin.content.domain.vo.IndexVlogVO;
|
||||
import com.wzj.soopin.content.service.VlogService;
|
||||
import com.wzj.soopin.content.service.VlogUploadService;
|
||||
import com.wzj.soopin.content.utils.PagedGridResult;
|
||||
@ -45,60 +44,58 @@ public class AppVlogController {
|
||||
public RedisOperator redis;
|
||||
@Tag(name = "首页视频列表")
|
||||
@PostMapping("/indexList")
|
||||
public R<PagedGridResult> indexList(@RequestBody IndexListBO bo, @RequestBody Page page) {
|
||||
PagedGridResult pages = vlogService.getIndexVlogList(bo, page);
|
||||
public R<Page<IndexVlogVO>> indexList(@RequestBody IndexListBO bo, @RequestBody Page page) {
|
||||
Page<IndexVlogVO> pages = vlogService.getIndexVlogList(bo, page);
|
||||
return R.ok(pages);
|
||||
}
|
||||
@GetMapping("/detail")
|
||||
public R<Object> detail(@RequestParam(defaultValue = "") String userId,
|
||||
@RequestParam String vlogId) {
|
||||
return R.ok(vlogService.getVlogDetailById(userId, vlogId));
|
||||
@GetMapping("/detail/{vlogId}")
|
||||
public R<Object> detail( @PathVariable String vlogId) {
|
||||
|
||||
return R.ok(vlogService.getVlogDetailById(vlogId));
|
||||
}
|
||||
|
||||
|
||||
@Tag(name = "我的私密视频列表")
|
||||
@PostMapping("/myPrivateList")
|
||||
public R<PagedGridResult> myPrivateList(@RequestBody MyListBO bo, @RequestBody Page page) {
|
||||
public R<Page<IndexVlogVO>> myPrivateList(@RequestBody MyListBO bo, @RequestBody Page page) {
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
if (loginUser == null) {
|
||||
throw new ServiceException("用户未登录");
|
||||
}
|
||||
bo.setUserId(String.valueOf(loginUser.getUserId()));
|
||||
PagedGridResult pages = vlogService.queryMyVlogList(bo, page);
|
||||
Page<IndexVlogVO> pages = vlogService.queryMyVlogList(bo, page);
|
||||
return R.ok(pages);
|
||||
}
|
||||
|
||||
@Tag(name = "我点赞的视频列表")
|
||||
@PostMapping("/myLikedList")
|
||||
public R<PagedGridResult> myLikedList(@RequestBody MyListBO bo, @RequestBody Page page) {
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
if (loginUser == null) {
|
||||
throw new ServiceException("用户未登录");
|
||||
}
|
||||
bo.setUserId(String.valueOf(loginUser.getUserId()));
|
||||
PagedGridResult pages = vlogService.getMyLikedVlogList(bo, page);
|
||||
public R<Page<IndexVlogVO>> myLikedList(@RequestBody MyLikedVlogBO bo, @RequestBody Page page) {
|
||||
|
||||
Page<IndexVlogVO> pages = vlogService.getMyLikedVlogList(page);
|
||||
return R.ok(pages);
|
||||
}
|
||||
|
||||
@Tag(name = "我关注的视频列表")
|
||||
@Tag(name = "我关注的人的视频列表")
|
||||
@PostMapping("/followList")
|
||||
public R<PagedGridResult> followList(@RequestBody SimpleListBO bo, @RequestBody Page page) {
|
||||
public R<Page<IndexVlogVO>> followList(@RequestBody SimpleListBO bo, @RequestBody Page page) {
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
if (loginUser == null) {
|
||||
throw new ServiceException("用户未登录");
|
||||
}
|
||||
bo.setMyId(String.valueOf(loginUser.getUserId()));
|
||||
PagedGridResult pages = vlogService.getMyFollowVlogList(bo, page);
|
||||
Page<IndexVlogVO> pages = vlogService.getMyFollowVlogList( page);
|
||||
return R.ok(pages);
|
||||
}
|
||||
|
||||
@Tag(name = "好友视频列表")
|
||||
@PostMapping("/friendList")
|
||||
public R<PagedGridResult> friendList(@RequestBody SimpleListBO bo, @RequestBody Page page) {
|
||||
public R<Page<IndexVlogVO>> friendList(@RequestBody SimpleListBO bo, @RequestBody Page page) {
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
if (loginUser == null) {
|
||||
throw new ServiceException("用户未登录");
|
||||
}
|
||||
bo.setMyId(String.valueOf(loginUser.getUserId()));
|
||||
PagedGridResult pages = vlogService.getMyFriendVlogList(bo, page);
|
||||
Page<IndexVlogVO> pages = vlogService.getMyFriendVlogList( page);
|
||||
return R.ok(pages);
|
||||
}
|
||||
@PostMapping("publish")
|
||||
@ -122,8 +119,8 @@ public class AppVlogController {
|
||||
}
|
||||
@Tag(name = "我的公开视频列表")
|
||||
@PostMapping("/myPublicList")
|
||||
public R<PagedGridResult> myPublicList(@RequestBody MyListBO bo, @RequestBody Page page) {
|
||||
PagedGridResult pages = vlogService.queryMyVlogList(bo, page);
|
||||
public R<Page<IndexVlogVO>> myPublicList(@RequestBody MyListBO bo, @RequestBody Page page) {
|
||||
Page<IndexVlogVO> pages = vlogService.queryMyVlogList(bo, page);
|
||||
return R.ok(pages);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,30 @@
|
||||
package org.dromara.app;
|
||||
|
||||
import io.swagger.annotations.Api;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 买家端,退款回调
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020-12-18 16:59
|
||||
*/
|
||||
@Api(tags = "买家端,退款回调")
|
||||
@RestController
|
||||
@RequestMapping("/buyer/payment/cashierRefund")
|
||||
@RequiredArgsConstructor
|
||||
public class CashierRefundController {
|
||||
|
||||
|
||||
// private final RefundSupport refundSupport;
|
||||
//
|
||||
//
|
||||
// @ApiOperation(value = "退款通知")
|
||||
// @RequestMapping(value = "/notify/{paymentMethod}", method = {RequestMethod.GET, RequestMethod.POST})
|
||||
// public void notify(HttpServletRequest request, @PathVariable String paymentMethod) {
|
||||
// PaymentMethodEnum paymentMethodEnum = PaymentMethodEnum.valueOf(paymentMethod);
|
||||
// refundSupport.notify(paymentMethodEnum, request);
|
||||
// }
|
||||
}
|
@ -99,7 +99,7 @@ sa-token:
|
||||
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
|
||||
is-concurrent: true
|
||||
# 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
|
||||
is-share: false
|
||||
is-share: true
|
||||
# jwt秘钥
|
||||
jwt-secret-key: abcdefghijklmnopqrstuvwxyz
|
||||
|
||||
@ -293,6 +293,8 @@ xss:
|
||||
# 排除链接(多个用逗号分隔)
|
||||
excludeUrls:
|
||||
- /system/notice
|
||||
- /pms/product/add
|
||||
- /pms/product/update
|
||||
|
||||
# 全局线程池相关配置
|
||||
# 如使用JDK21请直接使用虚拟线程 不要开启此配置
|
||||
|
@ -46,7 +46,7 @@ public class SaTokenExceptionHandler {
|
||||
public R<Void> handleNotLoginException(NotLoginException e, HttpServletRequest request) {
|
||||
String requestURI = request.getRequestURI();
|
||||
log.error("请求地址'{}',认证失败'{}',无法访问系统资源", requestURI, e.getMessage());
|
||||
return R.fail(HttpStatus.HTTP_UNAUTHORIZED, "认证失败,无法访问系统资源");
|
||||
return R.fail(HttpStatus.HTTP_UNAUTHORIZED, "认证失败,无法访问系统资源:" + e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -115,6 +115,7 @@ public class TenantSaTokenDao extends PlusSaTokenDao {
|
||||
*/
|
||||
@Override
|
||||
public long getObjectTimeout(String key) {
|
||||
|
||||
return super.getObjectTimeout(GlobalConstants.GLOBAL_REDIS_KEY + key);
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,8 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
@SpringBootApplication
|
||||
public class SnailJobServerApplication {
|
||||
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(com.aizuda.snailjob.server.SnailJobServerApplication.class, args);
|
||||
}
|
||||
|
@ -0,0 +1,37 @@
|
||||
package com.wzj.soopin.content.domain.bo;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.wzj.soopin.content.domain.po.MyLikedVlog;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.dromara.common.core.domain.BaseBO;
|
||||
import org.dromara.common.core.domain.model.BaseAudit;
|
||||
|
||||
@TableName("cont_my_liked_vlog")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class MyLikedVlogBO extends BaseBO<MyLikedVlog> {
|
||||
@TableId
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 喜欢的短视频id
|
||||
*/
|
||||
@TableField("vlog_id")
|
||||
private String vlogId;
|
||||
|
||||
|
||||
@Override
|
||||
public LambdaQueryWrapper<MyLikedVlog> toWrapper() {
|
||||
return super.toWrapper().eq(userId!=null,MyLikedVlog::getUserId,userId)
|
||||
.eq(vlogId!=null,MyLikedVlog::getVlogId,vlogId);
|
||||
}
|
||||
}
|
@ -1,15 +1,20 @@
|
||||
package com.wzj.soopin.content.domain.bo;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.wzj.soopin.content.domain.po.Vlog;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.dromara.common.core.domain.BaseBO;
|
||||
|
||||
public class MyListBO {
|
||||
@Data
|
||||
public class MyListBO extends BaseBO<Vlog> {
|
||||
@Schema(description = "用户ID")
|
||||
private String userId;
|
||||
@Schema(description = "我的ID")
|
||||
private String myId;
|
||||
|
||||
@Schema(description = "是否公开(1公开,0私密)")
|
||||
private Integer yesOrNo;
|
||||
private Integer privateFlag;
|
||||
|
||||
@Schema(description = "页码", defaultValue = "1")
|
||||
private Long pageNum = 1L;
|
||||
@ -17,18 +22,5 @@ public class MyListBO {
|
||||
@Schema(description = "每页大小", defaultValue = "10")
|
||||
private Long pageSize = 10L;
|
||||
|
||||
// getter/setter
|
||||
public String getUserId() { return userId; }
|
||||
public void setUserId(String userId) { this.userId = userId; }
|
||||
public String getMyId() { return myId; }
|
||||
public void setMyId(String myId) { this.myId = myId; }
|
||||
|
||||
public Integer getYesOrNo() { return yesOrNo; }
|
||||
public void setYesOrNo(Integer yesOrNo) { this.yesOrNo = yesOrNo; }
|
||||
|
||||
public Long getPageNum() { return pageNum; }
|
||||
public void setPageNum(Long pageNum) { this.pageNum = pageNum; }
|
||||
|
||||
public Long getPageSize() { return pageSize; }
|
||||
public void setPageSize(Long pageSize) { this.pageSize = pageSize; }
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
package com.wzj.soopin.content.domain.bo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class SimpleListBO {
|
||||
@Schema(description = "我的ID")
|
||||
private String myId;
|
||||
// getter/setter
|
||||
public String getMyId() { return myId; }
|
||||
public void setMyId(String myId) { this.myId = myId; }
|
||||
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package com.wzj.soopin.content.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.wzj.soopin.content.domain.po.Vlog;
|
||||
import com.wzj.soopin.content.domain.vo.IndexVlogVO;
|
||||
import com.wzj.soopin.content.domain.vo.VlogerVO;
|
||||
@ -15,14 +16,23 @@ import java.util.Map;
|
||||
@InterceptorIgnore(tenantLine = "true")
|
||||
public interface VlogMapperCustom extends BaseMapperPlus<Vlog, VlogerVO> {
|
||||
|
||||
public List<IndexVlogVO> getIndexVlogList(@Param("paramMap")Map<String, Object> map);
|
||||
List<IndexVlogVO> getIndexVlogList(@Param("paramMap") Map<String, Object> map);
|
||||
|
||||
public List<IndexVlogVO> getVlogDetailById(@Param("paramMap")Map<String, Object> map);
|
||||
Page<IndexVlogVO> getIndexVlogList(@Param("paramMap") Map<String, Object> map, Page<IndexVlogVO> page);
|
||||
|
||||
public List<IndexVlogVO> getMyLikedVlogList(@Param("paramMap")Map<String, Object> map);
|
||||
List<IndexVlogVO> getVlogDetailById(@Param("paramMap") Map<String, Object> map);
|
||||
|
||||
public List<IndexVlogVO> getMyFollowVlogList(@Param("paramMap")Map<String, Object> map);
|
||||
List<IndexVlogVO> getMyLikedVlogList(@Param("paramMap") Map<String, Object> map);
|
||||
|
||||
public List<IndexVlogVO> getMyFriendVlogList(@Param("paramMap")Map<String, Object> map);
|
||||
Page<IndexVlogVO> getMyLikedVlogList(@Param("paramMap") Map<String, Object> map, Page<IndexVlogVO> page);
|
||||
|
||||
List<IndexVlogVO> getMyFollowVlogList(@Param("paramMap") Map<String, Object> map);
|
||||
|
||||
|
||||
Page<IndexVlogVO> getMyFollowVlogList(@Param("paramMap") Map<String, Object> map, Page<IndexVlogVO> page);
|
||||
|
||||
List<IndexVlogVO> getMyFriendVlogList(@Param("paramMap") Map<String, Object> map);
|
||||
|
||||
Page<IndexVlogVO> getMyFriendVlogList(@Param("paramMap") Map<String, Object> map, Page<IndexVlogVO> page);
|
||||
|
||||
}
|
||||
|
@ -2,13 +2,10 @@ package com.wzj.soopin.content.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.wzj.soopin.content.domain.bo.VlogBO;
|
||||
import com.wzj.soopin.content.domain.bo.*;
|
||||
import com.wzj.soopin.content.domain.po.Vlog;
|
||||
import com.wzj.soopin.content.domain.vo.IndexVlogVO;
|
||||
import com.wzj.soopin.content.utils.PagedGridResult;
|
||||
import com.wzj.soopin.content.domain.bo.SimpleListBO;
|
||||
import com.wzj.soopin.content.domain.bo.MyListBO;
|
||||
import com.wzj.soopin.content.domain.bo.IndexListBO;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -31,12 +28,12 @@ public interface VlogService {
|
||||
/**
|
||||
* 查询首页/搜索的vlog列表
|
||||
*/
|
||||
public PagedGridResult getIndexVlogList(IndexListBO bo, Page page);
|
||||
public Page<IndexVlogVO> getIndexVlogList(IndexListBO bo, Page page);
|
||||
|
||||
/**
|
||||
* 根据视频主键查询vlog
|
||||
*/
|
||||
public IndexVlogVO getVlogDetailById(String userId, String vlogId);
|
||||
public IndexVlogVO getVlogDetailById( String vlogId);
|
||||
|
||||
/**
|
||||
* 用户把视频改为公开/私密的视频
|
||||
@ -51,7 +48,7 @@ public interface VlogService {
|
||||
/**
|
||||
* 查询用的公开/私密的视频列表
|
||||
*/
|
||||
public PagedGridResult queryMyVlogList(MyListBO bo, Page page);
|
||||
public Page<IndexVlogVO> queryMyVlogList(MyListBO bo, Page page);
|
||||
|
||||
/**
|
||||
* 用户点赞/喜欢视频
|
||||
@ -71,17 +68,17 @@ public interface VlogService {
|
||||
/**
|
||||
* 查询用户点赞过的短视频
|
||||
*/
|
||||
public PagedGridResult getMyLikedVlogList(MyListBO bo, Page page);
|
||||
public Page<IndexVlogVO> getMyLikedVlogList( Page page);
|
||||
|
||||
/**
|
||||
* 查询用户关注的博主发布的短视频列表
|
||||
*/
|
||||
public PagedGridResult getMyFollowVlogList(SimpleListBO bo, Page page);
|
||||
public Page<IndexVlogVO> getMyFollowVlogList( Page page);
|
||||
|
||||
/**
|
||||
* 查询朋友发布的短视频列表
|
||||
*/
|
||||
public PagedGridResult getMyFriendVlogList(SimpleListBO bo, Page page);
|
||||
public Page<IndexVlogVO> getMyFriendVlogList( Page page);
|
||||
|
||||
/**
|
||||
* 根据主键查询vlog
|
||||
|
@ -9,10 +9,7 @@ import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.wzj.soopin.content.domain.base.BaseInfoProperties;
|
||||
|
||||
import com.wzj.soopin.content.domain.bo.VlogBO;
|
||||
import com.wzj.soopin.content.domain.bo.SimpleListBO;
|
||||
import com.wzj.soopin.content.domain.bo.MyListBO;
|
||||
import com.wzj.soopin.content.domain.bo.IndexListBO;
|
||||
import com.wzj.soopin.content.domain.bo.*;
|
||||
import com.wzj.soopin.content.domain.po.MyLikedVlog;
|
||||
import com.wzj.soopin.content.domain.po.Vlog;
|
||||
import com.wzj.soopin.content.domain.po.Users;
|
||||
@ -35,6 +32,7 @@ import com.wzj.soopin.member.service.IFansService;
|
||||
import com.wzj.soopin.content.convert.VlogConvert;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dromara.common.core.domain.model.LoginUser;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@ -146,7 +144,7 @@ public class VlogServiceImpl extends BaseInfoProperties implements VlogService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public PagedGridResult getIndexVlogList(IndexListBO bo, Page page) {
|
||||
public Page<IndexVlogVO> getIndexVlogList(IndexListBO bo, Page page) {
|
||||
String userId = bo.getMemberId();
|
||||
String search = bo.getSearch();
|
||||
String cityCode = bo.getCityCode();
|
||||
@ -240,11 +238,11 @@ public class VlogServiceImpl extends BaseInfoProperties implements VlogService {
|
||||
}
|
||||
|
||||
// 封装分页结果
|
||||
PagedGridResult gridResult = new PagedGridResult();
|
||||
gridResult.setRows(voList); // 当前页数据列表
|
||||
gridResult.setPage(current); // 当前页码
|
||||
gridResult.setRecords(vlogPage.getTotal()); // 总记录数
|
||||
gridResult.setTotal(vlogPage.getPages()); // 总页数
|
||||
Page<IndexVlogVO> gridResult = new Page<IndexVlogVO>();
|
||||
gridResult.setRecords(voList); // 当前页数据列表
|
||||
gridResult.setCurrent(current); // 当前页码
|
||||
gridResult.setTotal(vlogPage.getTotal()); // 总记录数
|
||||
gridResult.setSize(vlogPage.getPages()); // 总页数
|
||||
|
||||
|
||||
return gridResult;
|
||||
@ -274,17 +272,21 @@ public class VlogServiceImpl extends BaseInfoProperties implements VlogService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndexVlogVO getVlogDetailById(String userId, String vlogId) {
|
||||
public IndexVlogVO getVlogDetailById( String vlogId) {
|
||||
Vlog vlog = vlogMapper.selectById(vlogId);
|
||||
if (vlog == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
IndexVlogVO vo = MapstructUtils.convert(vlog, IndexVlogVO.class);
|
||||
|
||||
if (StringUtils.isNotBlank(userId)) {
|
||||
vo.setDoIFollowVloger(fansService.queryDoIFollowVloger(userId, vlog.getMemberId()));
|
||||
vo.setDoILikeThisVlog(doILikeVlog(userId, vlogId));
|
||||
//获取用户信息
|
||||
try{
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
if (loginUser != null) {
|
||||
vo.setDoIFollowVloger(fansService.queryDoIFollowVloger(loginUser.getUserId()+"", vlog.getMemberId()));
|
||||
vo.setDoILikeThisVlog(doILikeVlog(loginUser.getUserId()+"", vlogId));
|
||||
}
|
||||
}catch (Exception e){
|
||||
log.error(e.getMessage());
|
||||
}
|
||||
|
||||
vo.setLikeCounts(getVlogBeLikedCounts(vlogId));
|
||||
@ -321,7 +323,7 @@ public class VlogServiceImpl extends BaseInfoProperties implements VlogService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public PagedGridResult queryMyVlogList(MyListBO bo, Page page) {
|
||||
public Page<IndexVlogVO> queryMyVlogList(MyListBO bo, Page page) {
|
||||
int current = (int) page.getCurrent();
|
||||
int size = (int) page.getSize();
|
||||
Page<Vlog> pageParam = new Page<>(current, size);
|
||||
@ -329,12 +331,15 @@ public class VlogServiceImpl extends BaseInfoProperties implements VlogService {
|
||||
|
||||
if(bo.getUserId()==null){
|
||||
LoginUser user= LoginHelper.getLoginUser();
|
||||
if(user==null){
|
||||
throw new ServiceException("用户未登录");
|
||||
}
|
||||
queryWrapper.eq(Vlog::getMemberId, user.getUserId());
|
||||
}else{
|
||||
queryWrapper.eq(Vlog::getMemberId, bo.getUserId());
|
||||
|
||||
}
|
||||
queryWrapper.eq(Vlog::getIsPrivate, bo.getYesOrNo());
|
||||
queryWrapper.eq(bo.getPrivateFlag()!=null,Vlog::getIsPrivate, bo.getPrivateFlag());
|
||||
Page<Vlog> vlogPage = vlogMapper.selectPage(pageParam, queryWrapper);
|
||||
List<Vlog> vlogList = vlogPage.getRecords();
|
||||
|
||||
@ -362,11 +367,11 @@ public class VlogServiceImpl extends BaseInfoProperties implements VlogService {
|
||||
return vo;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
PagedGridResult gridResult = new PagedGridResult();
|
||||
gridResult.setRows(voList);
|
||||
gridResult.setPage(current);
|
||||
gridResult.setRecords(vlogPage.getTotal());
|
||||
gridResult.setTotal(vlogPage.getPages());
|
||||
Page<IndexVlogVO> gridResult = new Page<IndexVlogVO>();
|
||||
gridResult.setRecords(voList);
|
||||
gridResult.setSize(vlogPage.getSize());
|
||||
gridResult.setCurrent(current);
|
||||
gridResult.setTotal(vlogPage.getTotal());
|
||||
return gridResult;
|
||||
}
|
||||
|
||||
@ -444,118 +449,48 @@ public class VlogServiceImpl extends BaseInfoProperties implements VlogService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public PagedGridResult getMyLikedVlogList(MyListBO bo, Page page) {
|
||||
int current = (int) page.getCurrent();
|
||||
int size = (int) page.getSize();
|
||||
Page<MyLikedVlog> pageParam = new Page<>(current, size);
|
||||
LambdaQueryWrapper<MyLikedVlog> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(MyLikedVlog::getUserId, bo.getUserId());
|
||||
Page<MyLikedVlog> likedPage = myLikedVlogMapper.selectPage(pageParam, queryWrapper);
|
||||
List<MyLikedVlog> likedList = likedPage.getRecords();
|
||||
// 组装返回的 VO 列表
|
||||
String uid = bo.getUserId();
|
||||
if (StringUtils.isBlank(uid)) {
|
||||
public Page<IndexVlogVO> getMyLikedVlogList( Page page) {
|
||||
|
||||
LoginUser user= LoginHelper.getLoginUser();
|
||||
uid = user != null ? String.valueOf(user.getUserId()) : null;
|
||||
if(user==null){
|
||||
throw new ServiceException("用户未登录");
|
||||
}
|
||||
final String finalUid = uid;
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("myId", user.getUserId());
|
||||
Page<IndexVlogVO> likedPage = vlogMapperCustom.getMyLikedVlogList(map,page);
|
||||
|
||||
List<IndexVlogVO> voList = likedList.stream()
|
||||
.map(liked -> {
|
||||
Vlog vlog = vlogMapper.selectById(liked.getVlogId());
|
||||
if (vlog == null) {
|
||||
return null;
|
||||
}
|
||||
IndexVlogVO vo = MapstructUtils.convert(vlog, IndexVlogVO.class);
|
||||
if (StringUtils.isNotBlank(finalUid)) {
|
||||
vo.setDoIFollowVloger(fansService.queryDoIFollowVloger(finalUid, vlog.getMemberId()));
|
||||
vo.setDoILikeThisVlog(true);
|
||||
}
|
||||
vo.setLikeCounts(getVlogBeLikedCounts(vlog.getId()));
|
||||
vo.setCommentsCounts(getVlogComment(vlog.getId()));
|
||||
|
||||
// 补充用户信息(头像/昵称)
|
||||
Member author = memberMapper.selectById(vlog.getMemberId());
|
||||
if (author != null) {
|
||||
vo.setAvatar(author.getAvatar());
|
||||
vo.setNickname(author.getNickname());
|
||||
}
|
||||
|
||||
return vo;
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
PagedGridResult gridResult = new PagedGridResult();
|
||||
gridResult.setRows(voList);
|
||||
gridResult.setPage(current);
|
||||
gridResult.setRecords(likedPage.getTotal());
|
||||
gridResult.setTotal(likedPage.getPages());
|
||||
return gridResult;
|
||||
likedPage.getRecords().stream().forEach(
|
||||
liked -> {
|
||||
liked.setDoIFollowVloger(true);
|
||||
liked.setDoILikeThisVlog(true);
|
||||
});
|
||||
return likedPage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PagedGridResult getMyFollowVlogList(SimpleListBO bo, Page page) {
|
||||
int current = (int) page.getCurrent();
|
||||
int size = (int) page.getSize();
|
||||
public Page<IndexVlogVO> getMyFollowVlogList(Page page) {
|
||||
LoginUser user= LoginHelper.getLoginUser();
|
||||
if(user==null){
|
||||
throw new ServiceException("用户未登录");
|
||||
}
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("myId", bo.getMyId());
|
||||
List<IndexVlogVO> voList = vlogMapperCustom.getMyFollowVlogList(map);
|
||||
map.put("myId", user.getUserId());
|
||||
Page<IndexVlogVO> voPage = vlogMapperCustom.getMyFollowVlogList(map,page);
|
||||
|
||||
// 补充用户信息(头像/昵称)
|
||||
voList.forEach(vo -> {
|
||||
if (StringUtils.isNotBlank(vo.getMemberId())) {
|
||||
Member author = memberMapper.selectById(vo.getMemberId());
|
||||
if (author != null) {
|
||||
vo.setAvatar(author.getAvatar());
|
||||
vo.setNickname(author.getNickname());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 手动分页
|
||||
int startIndex = (current - 1) * size;
|
||||
int endIndex = Math.min(startIndex + size, voList.size());
|
||||
List<IndexVlogVO> pagedList = voList.subList(startIndex, endIndex);
|
||||
|
||||
PagedGridResult gridResult = new PagedGridResult();
|
||||
gridResult.setRows(pagedList);
|
||||
gridResult.setPage(current);
|
||||
gridResult.setRecords(voList.size());
|
||||
gridResult.setTotal((int) Math.ceil((double) voList.size() / size));
|
||||
return gridResult;
|
||||
return voPage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PagedGridResult getMyFriendVlogList(SimpleListBO bo, Page page) {
|
||||
int current = (int) page.getCurrent();
|
||||
int size = (int) page.getSize();
|
||||
public Page<IndexVlogVO> getMyFriendVlogList( Page page) {
|
||||
LoginUser user= LoginHelper.getLoginUser();
|
||||
if(user==null){
|
||||
throw new ServiceException("用户未登录");
|
||||
}
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("myId", bo.getMyId());
|
||||
List<IndexVlogVO> voList = vlogMapperCustom.getMyFriendVlogList(map);
|
||||
map.put("myId", user.getUserId());
|
||||
Page<IndexVlogVO> voPage = vlogMapperCustom.getMyFriendVlogList(map,page);
|
||||
|
||||
// 补充用户信息(头像/昵称)
|
||||
voList.forEach(vo -> {
|
||||
if (StringUtils.isNotBlank(vo.getMemberId())) {
|
||||
Member author = memberMapper.selectById(vo.getMemberId());
|
||||
if (author != null) {
|
||||
vo.setAvatar(author.getAvatar());
|
||||
vo.setNickname(author.getNickname());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 手动分页
|
||||
int startIndex = (current - 1) * size;
|
||||
int endIndex = Math.min(startIndex + size, voList.size());
|
||||
List<IndexVlogVO> pagedList = voList.subList(startIndex, endIndex);
|
||||
|
||||
PagedGridResult gridResult = new PagedGridResult();
|
||||
gridResult.setRows(pagedList);
|
||||
gridResult.setPage(current);
|
||||
gridResult.setRecords(voList.size());
|
||||
gridResult.setTotal((int) Math.ceil((double) voList.size() / size));
|
||||
return gridResult;
|
||||
return voPage;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -117,6 +117,7 @@
|
||||
v.id as Id,
|
||||
m.avatar as avatar,
|
||||
m.nickname as nickname
|
||||
|
||||
FROM
|
||||
cont_vlog v
|
||||
LEFT JOIN
|
||||
@ -128,7 +129,7 @@
|
||||
ON
|
||||
v.member_id = m.id
|
||||
WHERE
|
||||
mlv.user_id = #{paramMap.userId}
|
||||
mlv.user_id = #{paramMap.myId}
|
||||
AND v.status = 1
|
||||
AND v.first_frame_img IS NOT NULL
|
||||
AND v.is_private = 0
|
||||
@ -151,35 +152,29 @@
|
||||
|
||||
<select id="getMyFollowVlogList" parameterType="map" resultType="com.wzj.soopin.content.domain.vo.IndexVlogVO">
|
||||
SELECT
|
||||
v.id as vlogId,
|
||||
v.member_id as memberId,
|
||||
v.title as title,
|
||||
v.title as content,
|
||||
v.url as url,
|
||||
v.cover as cover,
|
||||
v.width as width,
|
||||
v.height as height,
|
||||
v.like_counts as likeCounts,
|
||||
v.comments_counts as commentsCounts,
|
||||
v.is_private as isPrivate,
|
||||
v.city_code as cityCode,
|
||||
v.reason as reason,
|
||||
v.status as status,
|
||||
v.file_id as fileId,
|
||||
v.first_frame_img as firstFrameImg,
|
||||
v.id as Id,
|
||||
m.avatar as avatar,
|
||||
m.nickname as nickname
|
||||
v.id AS vlogId,
|
||||
v.member_id AS memberId,
|
||||
v.title AS title,
|
||||
v.title AS content,
|
||||
v.url AS url,
|
||||
v.cover AS cover,
|
||||
v.width AS width,
|
||||
v.height AS height,
|
||||
v.like_counts AS likeCounts,
|
||||
v.comments_counts AS commentsCounts,
|
||||
v.is_private AS isPrivate,
|
||||
v.city_code AS cityCode,
|
||||
v.reason AS reason,
|
||||
v.STATUS AS STATUS,
|
||||
v.file_id AS fileId,
|
||||
v.first_frame_img AS firstFrameImg,
|
||||
v.id AS Id,
|
||||
m.avatar AS avatar,
|
||||
m.nickname AS nickname
|
||||
FROM
|
||||
cont_vlog v
|
||||
LEFT JOIN
|
||||
cont_fans f
|
||||
ON
|
||||
v.member_id = f.vloger_id
|
||||
LEFT JOIN
|
||||
ums_member m
|
||||
ON
|
||||
v.member_id = m.id
|
||||
LEFT JOIN ums_fans f ON v.member_id = f.vlogger_id
|
||||
LEFT JOIN ums_member m ON v.member_id = m.id
|
||||
WHERE
|
||||
v.is_private = 0
|
||||
AND v.status = 1
|
||||
@ -227,9 +222,9 @@
|
||||
AND v.status = 1
|
||||
AND v.first_frame_img IS NOT NULL
|
||||
AND
|
||||
f.vloger_id = #{paramMap.myId}
|
||||
f.fan_id = #{paramMap.myId}
|
||||
AND
|
||||
f.is_fan_friend_of_mine = 1
|
||||
f.friend_flag = 1
|
||||
ORDER BY
|
||||
v.create_time
|
||||
DESC
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.wzj.soopin.goods.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.wzj.soopin.goods.convert.ProductCategoryConvert;
|
||||
import com.wzj.soopin.goods.domain.bo.ProductCategoryBo;
|
||||
@ -25,7 +26,7 @@ import java.util.List;
|
||||
*/
|
||||
@Tag(name ="商品分类接口列表")
|
||||
@RestController
|
||||
@RequestMapping("/pms/productCategory")
|
||||
@RequestMapping("/pms/product/category")
|
||||
@RequiredArgsConstructor
|
||||
public class ProductCategoryController extends BaseController {
|
||||
|
||||
@ -34,6 +35,18 @@ public class ProductCategoryController extends BaseController {
|
||||
private final ProductCategoryServiceImpl service;
|
||||
private final ProductCategoryConvert convert;
|
||||
|
||||
@Tag(name = "查询列表")
|
||||
@PostMapping("/tree")
|
||||
public R< List<ProductCategoryVO>> tree(@RequestBody ProductCategoryBo bo ) {
|
||||
List<ProductCategoryVO> articleList = service.tree( bo.toWrapper());
|
||||
return R.ok( articleList);
|
||||
}
|
||||
@Tag(name = "查询列表")
|
||||
@PostMapping("/page")
|
||||
public R<IPage<ProductCategoryVO>> page(@RequestBody ProductCategoryBo bo, @RequestBody Page page) {
|
||||
Page<ProductCategory> pages = service.page(page, bo.toWrapper());
|
||||
return R.ok(convert.toVO(pages));
|
||||
}
|
||||
@Tag(name ="查询商品分类列表")
|
||||
@PostMapping("list")
|
||||
public R<List<ProductCategoryVO>> list(@RequestBody ProductCategoryBo query) {
|
||||
@ -67,4 +80,6 @@ public class ProductCategoryController extends BaseController {
|
||||
public R remove(@PathVariable Long id) {
|
||||
return R.ok(service.removeById(id));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -58,8 +58,6 @@ public class SkuController extends BaseController {
|
||||
@Log(title = "sku信息", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/add")
|
||||
public R add(@RequestBody Sku sku) {
|
||||
Long tenantId = Long.valueOf(LoginHelper.getTenantId());
|
||||
sku.setTenantId(tenantId);
|
||||
return R.ok(service.save(sku));
|
||||
}
|
||||
|
||||
|
@ -11,11 +11,11 @@ import java.util.List;
|
||||
@Data
|
||||
public class ProductDTO {
|
||||
|
||||
@Schema(description = "BRAND_ID")
|
||||
@Schema(description = "品牌")
|
||||
@Excel(name = "BRAND_ID")
|
||||
private Long brandId;
|
||||
|
||||
@Schema(description = "CATEGORY_ID")
|
||||
@Schema(description = "分类")
|
||||
@Excel(name = "CATEGORY_ID")
|
||||
private Long categoryId;
|
||||
|
||||
@ -43,8 +43,8 @@ public class ProductDTO {
|
||||
@Excel(name = "排序")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "PRICE")
|
||||
@Excel(name = "PRICE")
|
||||
@Schema(description = "价格")
|
||||
@Excel(name = "价格")
|
||||
private BigDecimal price;
|
||||
|
||||
@Schema(description = "单位")
|
||||
@ -81,7 +81,7 @@ public class ProductDTO {
|
||||
|
||||
@Schema(description = "店铺id")
|
||||
@Excel(name = "店铺id")
|
||||
private Long tenantId;
|
||||
private String tenantId;
|
||||
|
||||
@Schema(description = "审核状态 1.待审核;->2.审核通过;3->审核驳回")
|
||||
@Excel(name = "审核状态 1.待审核;->2.审核通过;3->审核驳回")
|
||||
|
@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.dromara.common.core.domain.model.BaseAudit;
|
||||
import org.dromara.common.excel.annotation.Excel;
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@ -16,7 +17,7 @@ import java.math.BigDecimal;
|
||||
@Schema(description = "商品信息对象")
|
||||
@Data
|
||||
@TableName("pms_product")
|
||||
public class Product extends BaseAudit {
|
||||
public class Product extends BaseEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "ID")
|
||||
@ -92,7 +93,7 @@ public class Product extends BaseAudit {
|
||||
|
||||
@Schema(description = "店铺id")
|
||||
@Excel(name = "店铺id")
|
||||
private Long tenantId;
|
||||
private String tenantId;
|
||||
|
||||
@Schema(description = "审核状态 1.待审核;->2.审核通过;3->审核驳回")
|
||||
@Excel(name = "审核状态 1.待审核;->2.审核通过;3->审核驳回")
|
||||
|
@ -50,5 +50,5 @@ public class Sku extends BaseAudit {
|
||||
|
||||
@Schema(description = "租户id")
|
||||
@Excel(name = "租户id")
|
||||
private Long tenantId;
|
||||
private String tenantId;
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ public class ProductVO extends BaseAudit {
|
||||
private String contactPhone;
|
||||
|
||||
@Schema(description = "店铺id")
|
||||
private Long tenantId;
|
||||
private String tenantId;
|
||||
|
||||
@Schema(description = "审核状态 1.待审核、2.审核通过、3.审核驳回")
|
||||
private String authFlag;
|
||||
|
@ -1,7 +1,12 @@
|
||||
package com.wzj.soopin.goods.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.wzj.soopin.goods.domain.entity.ProductCategory;
|
||||
import com.wzj.soopin.goods.domain.vo.ProductCategoryVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ProductCategoryService extends IService<ProductCategory> {
|
||||
List<ProductCategoryVO> tree(Wrapper<ProductCategory> wrapper);
|
||||
}
|
||||
|
@ -1,10 +1,19 @@
|
||||
package com.wzj.soopin.goods.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.wzj.soopin.goods.convert.ProductCategoryConvert;
|
||||
import com.wzj.soopin.goods.domain.entity.ProductCategory;
|
||||
import com.wzj.soopin.goods.domain.vo.ProductCategoryVO;
|
||||
import com.wzj.soopin.goods.mapper.ProductCategoryMapper;
|
||||
import com.wzj.soopin.goods.service.ProductCategoryService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
/**
|
||||
@ -13,6 +22,26 @@ import org.springframework.stereotype.Service;
|
||||
* @author zcc
|
||||
*/
|
||||
@Service
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@RequiredArgsConstructor
|
||||
public class ProductCategoryServiceImpl extends ServiceImpl<ProductCategoryMapper, ProductCategory> implements ProductCategoryService {
|
||||
|
||||
private final ProductCategoryConvert convert;
|
||||
|
||||
@Override
|
||||
public List<ProductCategoryVO> tree(Wrapper<ProductCategory> wrapper) {
|
||||
List<ProductCategory> list = this.list(wrapper);
|
||||
return list.stream().filter(item -> item.getParentId() == 0).map(item -> {
|
||||
ProductCategoryVO productCategoryVO = convert.toVO(item);
|
||||
productCategoryVO.setChildren(
|
||||
list.stream().filter(child -> Objects.equals(child.getParentId(), item.getId()))
|
||||
.map(convert::toVO)
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
|
||||
return productCategoryVO;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.domain.model.LoginUser;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.common.tenant.helper.TenantHelper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
@ -126,17 +127,12 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
|
||||
product.setProductAttr(dto.getProductAttr());
|
||||
product.setDetailHtml(dto.getDetailHtml());
|
||||
product.setDetailMobileHtml(dto.getDetailMobileHtml());
|
||||
if (loginUser != null) {
|
||||
product.setTenantId(Long.valueOf(loginUser.getTenantId()));
|
||||
log.info("登录租户id:{}", loginUser.getTenantId());
|
||||
}
|
||||
product.setAuthFlag(1);
|
||||
product.setPublishStatus(dto.getPublishStatus());
|
||||
product.setBrandName(dto.getBrandName());
|
||||
product.setProductCategoryName(dto.getProductCategoryName());
|
||||
product.setType(1);
|
||||
product.setSales("0");
|
||||
product.setTenantId(dto.getTenantId());
|
||||
return product;
|
||||
}
|
||||
|
||||
@ -162,7 +158,13 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
|
||||
return productMapper.deleteById(id) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 不带租户的获取商品信息
|
||||
* @param page
|
||||
* @param query
|
||||
* @return
|
||||
*/
|
||||
public IPage<ProductVO> getProduct(Page<Product> page, ProductBo query) {
|
||||
return productMapper.getProduct(page, query);
|
||||
return TenantHelper.ignore(() -> productMapper.getProduct(page, query));
|
||||
}
|
||||
}
|
||||
|
@ -3,12 +3,13 @@ package com.wzj.soopin.member.enums;
|
||||
public enum AccountBillSourceEnum {
|
||||
|
||||
WITHDRAW(1, "提现"),
|
||||
CHARGE(2, "充值"),
|
||||
WITHDRAW_LOCK(2, "提现锁定"),
|
||||
RECHARGE(3, "充值"),
|
||||
RECHARGE_REFUND(4, "充值退款"),
|
||||
WITHDRAW_REFUND(5, "提现退款"),
|
||||
RECHARGE_REFUND_REFUND(6, "充值退款退款"),
|
||||
WITHDRAW_REFUND_REFUND(7, "提现退款退款"),
|
||||
PAYMENT(8, "支付"),
|
||||
RECHARGE_REFUND_REFUND_REFUND(9, "充值退款退款退款"),
|
||||
RED_PACKAGE_RECEIVE(10, "红包接收"),
|
||||
RED_PACKAGE_SEND(11, "红包发送"),
|
||||
|
@ -6,8 +6,11 @@ import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.wzj.soopin.member.domain.bo.MemberAccountBO;
|
||||
import com.wzj.soopin.member.domain.po.MemberAccount;
|
||||
import com.wzj.soopin.member.domain.vo.MemberAccountVO;
|
||||
import com.wzj.soopin.member.enums.AccountBillSourceEnum;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public interface IMemberAccountService extends IService<MemberAccount> {
|
||||
|
||||
MemberAccount getMemberAccount(Long memberId);
|
||||
@ -15,4 +18,8 @@ public interface IMemberAccountService extends IService<MemberAccount> {
|
||||
IPage<MemberAccountVO> pageWithMember(Page<?> page, MemberAccountBO bo);
|
||||
|
||||
Object getCount();
|
||||
|
||||
boolean addMoney(BigDecimal money, Long memberId, AccountBillSourceEnum type, String remark);
|
||||
|
||||
boolean reduceMoney(BigDecimal money, Long memberId, AccountBillSourceEnum type, String remark);
|
||||
}
|
||||
|
@ -4,11 +4,16 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.wzj.soopin.member.domain.po.AccountBill;
|
||||
import com.wzj.soopin.member.domain.po.MemberAccount;
|
||||
import com.wzj.soopin.member.domain.bo.MemberAccountBO;
|
||||
import com.wzj.soopin.member.domain.vo.MemberAccountVO;
|
||||
import com.wzj.soopin.member.enums.AccountBillChangeTypeEnum;
|
||||
import com.wzj.soopin.member.enums.AccountBillSourceEnum;
|
||||
import com.wzj.soopin.member.mapper.AccountBillMapper;
|
||||
import com.wzj.soopin.member.mapper.MemberAccountMapper;
|
||||
import com.wzj.soopin.member.service.IMemberAccountService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@ -20,8 +25,10 @@ import java.math.BigDecimal;
|
||||
* @author zcc
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class MemberAccountServiceImpl extends ServiceImpl<MemberAccountMapper,MemberAccount> implements IMemberAccountService {
|
||||
|
||||
private final AccountBillMapper accountBillMapper;
|
||||
|
||||
@Override
|
||||
public MemberAccount getMemberAccount(Long memberId) {
|
||||
@ -50,4 +57,64 @@ public class MemberAccountServiceImpl extends ServiceImpl<MemberAccountMapper,Me
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean addMoney(BigDecimal money, Long memberId, AccountBillSourceEnum source, String remark) {
|
||||
MemberAccount memberAccount = this.getMemberAccount(memberId);
|
||||
if (memberAccount == null) {
|
||||
throw new RuntimeException("用户不存在");
|
||||
}
|
||||
//检查当前用于的账户余额是否充足
|
||||
BigDecimal balance = memberAccount.getWallet();
|
||||
|
||||
|
||||
BigDecimal newBalance = balance.add(money);
|
||||
//锁定用户余额
|
||||
this.updateById(memberAccount.toBuilder().wallet(newBalance).build());
|
||||
|
||||
//生成账单
|
||||
AccountBill memberAccountChangeRecord = AccountBill.builder()
|
||||
.accountId(memberAccount.getId())
|
||||
.changeAmount(money)
|
||||
.moneyBalance(newBalance)
|
||||
.beforeBalance(balance)
|
||||
.afterBalance(newBalance)
|
||||
.changeType(AccountBillChangeTypeEnum.IN.getCode())
|
||||
.changeDesc(remark)
|
||||
.source(source.getCode())
|
||||
.build();
|
||||
accountBillMapper.insert(memberAccountChangeRecord);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean reduceMoney(BigDecimal money, Long memberId, AccountBillSourceEnum source, String remark) {
|
||||
MemberAccount memberAccount = this.getMemberAccount(memberId);
|
||||
if (memberAccount == null) {
|
||||
throw new RuntimeException("用户不存在");
|
||||
}
|
||||
//检查当前用于的账户余额是否充足
|
||||
BigDecimal balance = memberAccount.getWallet();
|
||||
if (balance.compareTo(money) < 0) {
|
||||
throw new RuntimeException("用户余额不足");
|
||||
}
|
||||
|
||||
BigDecimal newBalance = balance.subtract(money);
|
||||
//锁定用户余额
|
||||
this.updateById(memberAccount.toBuilder().wallet(newBalance).build());
|
||||
|
||||
//生成账单
|
||||
AccountBill memberAccountChangeRecord = AccountBill.builder()
|
||||
.accountId(memberAccount.getId())
|
||||
.changeAmount(money)
|
||||
.moneyBalance(newBalance)
|
||||
.beforeBalance(balance)
|
||||
.afterBalance(newBalance)
|
||||
.changeType(AccountBillChangeTypeEnum.OUT.getCode())
|
||||
.changeDesc(remark)
|
||||
.source(source.getCode())
|
||||
.build();
|
||||
accountBillMapper.insert(memberAccountChangeRecord);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ import java.util.Map;
|
||||
* 红包功能控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/red-packet")
|
||||
@RequestMapping("/api/packet")
|
||||
@Api(tags = "红包功能接口")
|
||||
public class RedPacketController {
|
||||
|
||||
|
@ -0,0 +1,26 @@
|
||||
package com.wzj.soopin.order.emum;
|
||||
|
||||
/**
|
||||
* 支付方式:0->未支付;1->支付宝;2->微信 精确匹配
|
||||
*/
|
||||
public enum OrderPayTypeEnum {
|
||||
UNPAID(0, "未支付"),
|
||||
ALIPAY(1, "支付宝"),
|
||||
WECHAT(2, "微信");
|
||||
|
||||
private final Integer value;
|
||||
private final String info;
|
||||
|
||||
OrderPayTypeEnum(Integer value, String info) {
|
||||
this.value = value;
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getInfo() {
|
||||
return info;
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package com.wzj.soopin.order.emum;
|
||||
|
||||
/**
|
||||
* 订单状态:0->待付款;1->待发货;2->已发货;3->已完成;4->已关闭;5->无效订单
|
||||
*/
|
||||
public enum OrderStatusEnum {
|
||||
UNPAID(0, "待付款"),
|
||||
WAITING_FOR_DELIVERY(1, "待发货"),
|
||||
DELIVERED(2, "已发货"),
|
||||
COMPLETED(3, "已完成"),
|
||||
CLOSED(4, "已关闭"),
|
||||
INVALID(5, "无效订单");
|
||||
|
||||
private final Integer value;
|
||||
private final String info;
|
||||
|
||||
OrderStatusEnum(Integer value, String info) {
|
||||
this.value = value;
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getInfo() {
|
||||
return info;
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package com.wzj.soopin.order.service.impl;
|
||||
|
||||
import com.wzj.soopin.order.service.OrderService;
|
||||
import com.wzj.soopin.order.task.OrderCancelTask;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.concurrent.DelayQueue;
|
||||
|
||||
/**
|
||||
* 订单延迟取消服务
|
||||
*/
|
||||
@Service
|
||||
public class OrderDelayCancelService {
|
||||
private final DelayQueue<OrderCancelTask> delayQueue = new DelayQueue<>();
|
||||
@Autowired
|
||||
private OrderService orderService; // 订单服务,用于修改订单状态
|
||||
|
||||
public OrderDelayCancelService() {
|
||||
// 启动一个守护线程处理延迟任务
|
||||
Thread daemonThread = new Thread(this::processDelayQueue);
|
||||
daemonThread.setDaemon(true);
|
||||
daemonThread.start();
|
||||
}
|
||||
|
||||
// 添加订单到延迟队列
|
||||
public void addOrderToCancelQueue(String orderSn, long delayTime) {
|
||||
delayQueue.put(new OrderCancelTask(orderSn, delayTime));
|
||||
}
|
||||
|
||||
// 处理延迟队列中的任务
|
||||
private void processDelayQueue() {
|
||||
while (true) {
|
||||
try {
|
||||
// 取出到期的任务
|
||||
OrderCancelTask task = delayQueue.take();
|
||||
// 执行订单取消逻辑
|
||||
// orderService.updateOrderStatus(task.getOrderSn(), "CANCELLED");
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
// 记录异常,继续处理下一个任务
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@ import com.wzj.soopin.order.domain.form.DeliverProductForm;
|
||||
import com.wzj.soopin.order.domain.form.ManagerOrderQueryForm;
|
||||
import com.wzj.soopin.order.domain.query.OrderH5Query;
|
||||
import com.wzj.soopin.order.domain.vo.*;
|
||||
import com.wzj.soopin.order.emum.OrderStatusEnum;
|
||||
import com.wzj.soopin.order.mapper.*;
|
||||
import com.wzj.soopin.order.service.OrderService;
|
||||
import com.wzj.soopin.order.service.VerificationCodeService;
|
||||
@ -199,7 +200,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
||||
order.setTenantId(tenantId);
|
||||
order.setCreateTime(LocalDateTime.now());
|
||||
order.setWithdrawStatus(1);
|
||||
order.setStatus(0);
|
||||
order.setStatus(OrderStatusEnum.UNPAID.getValue());
|
||||
int insert = orderMapper.insert(order);
|
||||
if (insert>1){
|
||||
return R.fail("订单创建失败");
|
||||
|
@ -0,0 +1,31 @@
|
||||
package com.wzj.soopin.order.task;
|
||||
|
||||
import java.util.concurrent.Delayed;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 订单延迟取消任务
|
||||
*/
|
||||
public class OrderCancelTask implements Delayed {
|
||||
private final String orderSn; // 订单编号
|
||||
private final long executeTime; // 执行时间(毫秒)
|
||||
|
||||
public OrderCancelTask(String orderSn, long delayTime) {
|
||||
this.orderSn = orderSn;
|
||||
this.executeTime = System.currentTimeMillis() + delayTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDelay(TimeUnit unit) {
|
||||
return unit.convert(executeTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Delayed other) {
|
||||
return Long.compare(this.executeTime, ((OrderCancelTask) other).executeTime);
|
||||
}
|
||||
|
||||
public String getOrderSn() {
|
||||
return orderSn;
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package org.dromara.system.domain;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import org.dromara.common.core.domain.model.BaseAudit;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
@ -18,4 +18,6 @@ public interface ISysTenantAccountService extends IService<SysTenantAccount> {
|
||||
boolean save(SysTenantAccount po);
|
||||
boolean updateById(SysTenantAccount po);
|
||||
boolean removeById(Long id);
|
||||
|
||||
SysTenantAccount getByTenantId(Long tenantId);
|
||||
}
|
||||
|
@ -64,4 +64,9 @@ public class SysTenantAccountServiceImpl extends ServiceImpl<SysTenantAccountMa
|
||||
// 可根据实际业务补充更多条件
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SysTenantAccount getByTenantId(Long tenantId) {
|
||||
return mapper.selectOne(new LambdaQueryWrapper<SysTenantAccount>().eq(SysTenantAccount::getTenantId, tenantId));
|
||||
}
|
||||
}
|
||||
|
@ -69,6 +69,12 @@ public class WithdrawBO extends BaseBO<Withdraw> {
|
||||
*/
|
||||
@Schema(description ="类型")
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 银行名称
|
||||
*/
|
||||
@Schema(description ="银行名称")
|
||||
private String bankId;
|
||||
/**
|
||||
* 审核人
|
||||
*/
|
||||
|
@ -0,0 +1,73 @@
|
||||
package com.wzj.soopin.transaction.domain.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.common.core.domain.model.BaseAudit;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 退款日志
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2021/1/28 09:21
|
||||
*/
|
||||
@Data
|
||||
@TableName("li_refund_log")
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ApiModel(value = "退款日志")
|
||||
public class RefundLog extends BaseAudit {
|
||||
|
||||
@ApiModelProperty(value = "会员ID")
|
||||
private String memberId;
|
||||
|
||||
@ApiModelProperty(value = "退单编号")
|
||||
private String afterSaleNo;
|
||||
|
||||
@ApiModelProperty(value = "订单编号")
|
||||
private String orderSn;
|
||||
|
||||
@ApiModelProperty(value = "金额")
|
||||
private Double totalAmount;
|
||||
|
||||
@ApiModelProperty(value = "改笔交易支付金额")
|
||||
private Double payPrice;
|
||||
|
||||
@ApiModelProperty(value = "是否已退款")
|
||||
private Boolean isRefund;
|
||||
|
||||
@ApiModelProperty(value = "退款方式")
|
||||
private String paymentName;
|
||||
|
||||
|
||||
@ApiModelProperty(value = "支付第三方付款流水")
|
||||
private String paymentReceivableNo;
|
||||
|
||||
@ApiModelProperty(value = "退款请求流水")
|
||||
private String outOrderNo;
|
||||
|
||||
|
||||
@ApiModelProperty(value = "第三方退款流水号")
|
||||
private String receivableNo;
|
||||
|
||||
@ApiModelProperty(value = "退款理由")
|
||||
private String refundReason;
|
||||
|
||||
@ApiModelProperty(value = "退款失败原因")
|
||||
private String errorMessage;
|
||||
|
||||
|
||||
}
|
@ -69,6 +69,13 @@ public class Withdraw extends BaseAudit {
|
||||
*/
|
||||
private Long auditBy;
|
||||
|
||||
|
||||
/**
|
||||
* 银行名称
|
||||
*/
|
||||
@Schema(description ="银行名称")
|
||||
private String bankId;
|
||||
|
||||
/**
|
||||
* 审核时间
|
||||
*/
|
||||
|
@ -0,0 +1,23 @@
|
||||
package com.wzj.soopin.transaction.enums;
|
||||
|
||||
/**
|
||||
* 订单类型
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020-12-19 11:50
|
||||
*/
|
||||
public enum CashierEnum {
|
||||
|
||||
/**
|
||||
* 订单
|
||||
*/
|
||||
ORDER,
|
||||
/**
|
||||
* 交易
|
||||
*/
|
||||
TRADE,
|
||||
/**
|
||||
* 充值
|
||||
*/
|
||||
RECHARGE
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.wzj.soopin.transaction.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum ChargeStatus {
|
||||
WAITING(0, "待验证"),
|
||||
PENDING(1, "充值成功"),
|
||||
SUCCESS(2, "充值失败");
|
||||
|
||||
private Integer code;
|
||||
private String message;
|
||||
ChargeStatus(Integer code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package com.wzj.soopin.transaction.enums;
|
||||
|
||||
|
||||
/**
|
||||
* 客户端类型
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020/12/8 9:46
|
||||
*/
|
||||
|
||||
public enum ClientTypeEnum {
|
||||
|
||||
/**
|
||||
* "移动端"
|
||||
*/
|
||||
H5("移动端"),
|
||||
/**
|
||||
* "PC端"
|
||||
*/
|
||||
PC("PC端"),
|
||||
/**
|
||||
* "小程序端"
|
||||
*/
|
||||
WECHAT_MP("小程序端"),
|
||||
/**
|
||||
* "移动应用端"
|
||||
*/
|
||||
APP("移动应用端"),
|
||||
/**
|
||||
* "未知"
|
||||
*/
|
||||
UNKNOWN("未知");
|
||||
|
||||
private final String clientName;
|
||||
|
||||
ClientTypeEnum(String des) {
|
||||
this.clientName = des;
|
||||
}
|
||||
|
||||
public String clientName() {
|
||||
return this.clientName;
|
||||
}
|
||||
|
||||
public String value() {
|
||||
return this.name();
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package com.wzj.soopin.transaction.enums;
|
||||
|
||||
/**
|
||||
* 支付方式枚举
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020/12/18 18:08
|
||||
*/
|
||||
public enum PaymentClientEnum {
|
||||
|
||||
/**
|
||||
* app支付
|
||||
**/
|
||||
APP,
|
||||
/**
|
||||
* 展示二维码扫描支付
|
||||
*/
|
||||
NATIVE,
|
||||
/**
|
||||
* 公众号内部调用支付
|
||||
*/
|
||||
JSAPI,
|
||||
/**
|
||||
* 普通移动网页调用支付app
|
||||
*/
|
||||
H5,
|
||||
/**
|
||||
* 小程序,通常指微信小程序
|
||||
*/
|
||||
MP
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package com.wzj.soopin.transaction.enums;
|
||||
|
||||
/**
|
||||
* 支付方式枚举
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020/12/18 18:08
|
||||
*/
|
||||
public enum PaymentMethodEnum {
|
||||
|
||||
/**
|
||||
* 微信
|
||||
*/
|
||||
WECHAT("wechatPlugin", "微信"),
|
||||
/**
|
||||
* 支付宝
|
||||
*/
|
||||
ALIPAY("aliPayPlugin", "支付宝"),
|
||||
/**
|
||||
* 余额支付
|
||||
*/
|
||||
WALLET("walletPlugin", "余额支付"),
|
||||
/**
|
||||
* 线下转账
|
||||
*/
|
||||
BANK_TRANSFER("bankTransferPlugin", "线下转账"),
|
||||
/**
|
||||
* 易生支付
|
||||
*/
|
||||
EASY_PAY("easyPayPlugin", "易生支付");
|
||||
|
||||
/**
|
||||
* 插件id 调用对象,需要实现payment接口
|
||||
*/
|
||||
private final String plugin;
|
||||
/**
|
||||
* 支付名称
|
||||
*/
|
||||
private final String paymentName;
|
||||
|
||||
public String getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
public String paymentName() {
|
||||
return paymentName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据支付方式名称返回对象
|
||||
*
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public static PaymentMethodEnum paymentNameOf(String name) {
|
||||
for (PaymentMethodEnum value : PaymentMethodEnum.values()) {
|
||||
if (value.name().equals(name)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
PaymentMethodEnum(String plugin, String paymentName) {
|
||||
this.plugin = plugin;
|
||||
this.paymentName = paymentName;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package com.wzj.soopin.transaction.enums;
|
||||
|
||||
public enum SystemConfigEnum {
|
||||
ORDER_AUTO_CANCEL("order_auto_cancel", "订单自动取消时间"),
|
||||
ORDER_AUTO_CANCEL_MINUTE("order_auto_cancel_minute", "订单自动取消时间(分钟)") ;
|
||||
private final String key;
|
||||
private final String name;
|
||||
|
||||
SystemConfigEnum(String key, String name) {
|
||||
this.key = key;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.wzj.soopin.transaction.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum WithdrawType {
|
||||
WALLET(1, "钱包"),
|
||||
REVENUE(2, "营收");
|
||||
|
||||
private Integer code;
|
||||
private String message;
|
||||
WithdrawType(Integer code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,200 @@
|
||||
package com.wzj.soopin.transaction.kit;
|
||||
|
||||
import com.wzj.soopin.member.service.IMemberAccountService;
|
||||
import com.wzj.soopin.order.service.impl.OrderDelayCancelService;
|
||||
import com.wzj.soopin.transaction.enums.ClientTypeEnum;
|
||||
import com.wzj.soopin.transaction.enums.PaymentClientEnum;
|
||||
import com.wzj.soopin.transaction.enums.PaymentMethodEnum;
|
||||
import com.wzj.soopin.transaction.kit.dto.PayParam;
|
||||
import com.wzj.soopin.transaction.kit.params.CashierExecute;
|
||||
import com.wzj.soopin.transaction.kit.params.dto.CashierParam;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.constant.ResultCode;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.dromara.system.service.ISysConfigService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.time.temporal.TemporalField;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 收银台工具
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020-12-19 09:25
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class CashierSupport {
|
||||
/**
|
||||
* 收银台
|
||||
*/
|
||||
private final List<CashierExecute> cashierExecuteList;
|
||||
/**
|
||||
* 预存款
|
||||
*/
|
||||
private final IMemberAccountService memberWalletService;
|
||||
/**
|
||||
* 配置
|
||||
*/
|
||||
private final ISysConfigService sysConfigService;
|
||||
|
||||
private final OrderDelayCancelService orderDelayCancelService;
|
||||
|
||||
public final
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 支付
|
||||
*
|
||||
* @param paymentMethodEnum 支付渠道枚举
|
||||
* @param paymentClientEnum 支付方式枚举
|
||||
* @return 支付消息
|
||||
*/
|
||||
R<Object> payment(PaymentMethodEnum paymentMethodEnum, PaymentClientEnum paymentClientEnum,
|
||||
HttpServletRequest request, HttpServletResponse response,
|
||||
PayParam payParam) {
|
||||
if (paymentClientEnum == null || paymentMethodEnum == null) {
|
||||
throw new ServiceException(ResultCode.PAY_NOT_SUPPORT);
|
||||
}
|
||||
//获取支付插件
|
||||
Payment payment = (Payment) SpringUtils.getBean( paymentMethodEnum.getPlugin());
|
||||
log.info("支付请求:客户端:{},支付类型:{},请求:{}", paymentClientEnum.name(), paymentMethodEnum.name(), payParam.toString());
|
||||
|
||||
//支付方式调用
|
||||
switch (paymentClientEnum) {
|
||||
case H5:
|
||||
return payment.h5pay(request, response, payParam);
|
||||
case APP:
|
||||
return payment.appPay(request, payParam);
|
||||
case JSAPI:
|
||||
return payment.jsApiPay(request, payParam);
|
||||
case NATIVE:
|
||||
return payment.nativePay(request, payParam);
|
||||
case MP:
|
||||
return payment.mpPay(request, payParam);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付 支持的支付方式
|
||||
*
|
||||
* @param client 客户端类型
|
||||
* @return 支持的支付方式
|
||||
*/
|
||||
public List<String> support(String client) {
|
||||
|
||||
ClientTypeEnum clientTypeEnum;
|
||||
try {
|
||||
clientTypeEnum = ClientTypeEnum.valueOf(client);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new ServiceException(ResultCode.PAY_CLIENT_TYPE_ERROR);
|
||||
}
|
||||
//支付方式 循环获取
|
||||
// Setting setting = settingService.get(SettingEnum.PAYMENT_SUPPORT.name());
|
||||
// PaymentSupportSetting paymentSupportSetting = JSONUtil.toBean(setting.getSettingValue(), PaymentSupportSetting.class);
|
||||
// for (PaymentSupportItem paymentSupportItem : paymentSupportSetting.getPaymentSupportItems()) {
|
||||
// if (paymentSupportItem.getClient().equals(clientTypeEnum.name())) {
|
||||
// return paymentSupportItem.getSupports();
|
||||
// }
|
||||
// }
|
||||
|
||||
//从系统配置中获取支付方式 默认为易生支付
|
||||
return Arrays.asList(PaymentMethodEnum.EASY_PAY.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付回调
|
||||
*
|
||||
* @param paymentMethodEnum 支付渠道枚举
|
||||
* @return 回调消息
|
||||
*/
|
||||
public void callback(PaymentMethodEnum paymentMethodEnum,
|
||||
HttpServletRequest request) {
|
||||
|
||||
log.info("支付回调:支付类型:{}", paymentMethodEnum.name());
|
||||
|
||||
//获取支付插件
|
||||
Payment payment = (Payment) SpringUtils.getBean((paymentMethodEnum.getPlugin()));
|
||||
payment.callBack(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付通知
|
||||
*
|
||||
* @param paymentMethodEnum 支付渠道
|
||||
*/
|
||||
public void notify(PaymentMethodEnum paymentMethodEnum,
|
||||
HttpServletRequest request) {
|
||||
|
||||
log.info("支付异步通知:支付类型:{}", paymentMethodEnum.name());
|
||||
|
||||
//获取支付插件
|
||||
Payment payment = (Payment) SpringUtils.getBean((paymentMethodEnum.getPlugin()));
|
||||
payment.notify(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取收银台参数
|
||||
*
|
||||
* @param payParam 支付请求参数
|
||||
* @return 收银台参数
|
||||
*/
|
||||
public CashierParam cashierParam(PayParam payParam) {
|
||||
for (CashierExecute paramInterface : cashierExecuteList) {
|
||||
CashierParam cashierParam = paramInterface.getPaymentParams(payParam);
|
||||
log.info("订单编号", payParam.getSn());
|
||||
//如果为空,则表示收银台参数初始化不匹配,继续匹配下一条
|
||||
if (cashierParam == null) {
|
||||
continue;
|
||||
}
|
||||
//如果订单不需要付款,则抛出异常,直接返回
|
||||
if (cashierParam.getPrice() ==null) {
|
||||
throw new ServiceException(ResultCode.PAY_UN_WANTED);
|
||||
}
|
||||
cashierParam.setSupport(support(payParam.getClientType()));
|
||||
//获取订单的memberid
|
||||
//获取当前余额
|
||||
// cashierParam.setWalletValue(memberWalletService.getMemberWallet(UserContext.getCurrentUser().getId()).getMemberWallet());
|
||||
|
||||
// 设置自动取消时间戳
|
||||
long cancelTime = cashierParam.getCreateTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() + 30 * 1000; // 30秒
|
||||
cashierParam.setAutoCancel(30l);
|
||||
|
||||
// 添加到延迟队列
|
||||
long delayMillis = cancelTime - System.currentTimeMillis();
|
||||
orderDelayCancelService.addOrderToCancelQueue(payParam.getSn(), delayMillis);
|
||||
|
||||
return cashierParam;
|
||||
}
|
||||
log.error("错误的支付请求:{}", payParam.toString());
|
||||
throw new ServiceException(ResultCode.PAY_CASHIER_ERROR);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 支付结果
|
||||
*
|
||||
* @param payParam
|
||||
* @return
|
||||
*/
|
||||
public Boolean paymentResult(PayParam payParam) {
|
||||
for (CashierExecute cashierExecute : cashierExecuteList) {
|
||||
if (cashierExecute.cashierEnum().name().equals(payParam.getOrderType())) {
|
||||
return cashierExecute.paymentResult(payParam);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,156 @@
|
||||
package com.wzj.soopin.transaction.kit;
|
||||
|
||||
|
||||
import com.wzj.soopin.transaction.domain.po.RefundLog;
|
||||
import com.wzj.soopin.transaction.enums.PaymentMethodEnum;
|
||||
import com.wzj.soopin.transaction.kit.dto.PayParam;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.dromara.common.core.constant.ResultCode;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
|
||||
/**
|
||||
* 支付接口
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020-12-21 09:32
|
||||
*/
|
||||
public interface Payment {
|
||||
/**
|
||||
* 普通移动网页调用支付app
|
||||
*
|
||||
* @param request HttpServletRequest
|
||||
* @param response HttpServletResponse
|
||||
* @param payParam api参数
|
||||
* @return 移动支付所需参数
|
||||
*/
|
||||
default R<Object> h5pay(HttpServletRequest request, HttpServletResponse response, PayParam payParam) {
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* 公众号内部调用支付
|
||||
*
|
||||
* @param request HttpServletRequest
|
||||
* @param payParam api参数
|
||||
* @return 公众号内部支付参数
|
||||
*/
|
||||
default R<Object> jsApiPay(HttpServletRequest request, PayParam payParam) {
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* app支付
|
||||
*
|
||||
* @param request HttpServletRequest
|
||||
* @param payParam 支付参数
|
||||
* @return app支付所需参数
|
||||
*/
|
||||
default R<Object> appPay(HttpServletRequest request, PayParam payParam) {
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* 展示二维码扫描支付
|
||||
*
|
||||
* @param request HttpServletRequest
|
||||
* @param payParam 支付参数
|
||||
* @return 二维码内容
|
||||
*/
|
||||
default R<Object> nativePay(HttpServletRequest request, PayParam payParam) {
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* 小程序支付
|
||||
*
|
||||
* @param request HttpServletRequest
|
||||
* @param payParam 支付参数
|
||||
* @return 二维码内容
|
||||
*/
|
||||
default R<Object> mpPay(HttpServletRequest request, PayParam payParam) {
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 退款
|
||||
*
|
||||
* @param refundLog 退款请求参数
|
||||
*/
|
||||
default void refund(RefundLog refundLog) {
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 取消支付订单
|
||||
*
|
||||
* @param refundLog 支付参数
|
||||
*/
|
||||
default void cancel(RefundLog refundLog) {
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 回调
|
||||
*
|
||||
* @param request HttpServletRequest
|
||||
*/
|
||||
default void callBack(HttpServletRequest request) {
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步通知
|
||||
*
|
||||
* @param request HttpServletRequest
|
||||
*/
|
||||
default void notify(HttpServletRequest request) {
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款异步通知
|
||||
*
|
||||
* @param request HttpServletRequest
|
||||
*/
|
||||
default void refundNotify(HttpServletRequest request) {
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付回调地址
|
||||
*
|
||||
* @param api api地址
|
||||
* @param paymentMethodEnum 支付类型
|
||||
* @return 回调地址
|
||||
*/
|
||||
default String callbackUrl(String api, PaymentMethodEnum paymentMethodEnum) {
|
||||
return api + "/buyer/payment/cashier/callback/" + paymentMethodEnum.name();
|
||||
}
|
||||
/**
|
||||
* 支付异步通知地址
|
||||
*
|
||||
* @param api api地址
|
||||
* @param paymentMethodEnum 支付类型
|
||||
* @return 异步通知地址
|
||||
*/
|
||||
default String notifyUrl(String api, PaymentMethodEnum paymentMethodEnum) {
|
||||
return api + "/buyer/payment/cashier/notify/" + paymentMethodEnum.name();
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款支付异步通知地址
|
||||
*
|
||||
* @param api api地址
|
||||
* @param paymentMethodEnum 支付类型
|
||||
* @return 异步通知地址
|
||||
*/
|
||||
default String refundNotifyUrl(String api, PaymentMethodEnum paymentMethodEnum) {
|
||||
return api + "/buyer/payment/cashierRefund/notify/" + paymentMethodEnum.name();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
package com.wzj.soopin.transaction.kit;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 退款支持
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020-12-19 09:25
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class RefundSupport {
|
||||
// /**
|
||||
// * 店铺流水
|
||||
// */
|
||||
// @Autowired
|
||||
// private StoreFlowService storeFlowService;
|
||||
// /**
|
||||
// * 订单
|
||||
// */
|
||||
// @Autowired
|
||||
// private OrderService orderService;
|
||||
// /**
|
||||
// * 子订单
|
||||
// */
|
||||
// @Autowired
|
||||
// private OrderItemService orderItemService;
|
||||
//
|
||||
// /**
|
||||
// * 售后退款
|
||||
// *
|
||||
// * @param afterSale
|
||||
// */
|
||||
// public void refund(AfterSale afterSale) {
|
||||
// Order order = orderService.getBySn(afterSale.getOrderSn());
|
||||
// RefundLog refundLog = RefundLog.builder()
|
||||
// .isRefund(false)
|
||||
// .totalAmount(afterSale.getActualRefundPrice())
|
||||
// .payPrice(afterSale.getActualRefundPrice())
|
||||
// .memberId(afterSale.getMemberId())
|
||||
// .paymentName(order.getPaymentMethod())
|
||||
// .afterSaleNo(afterSale.getSn())
|
||||
// .paymentReceivableNo(order.getReceivableNo())
|
||||
// .outOrderNo("AF" + SnowFlake.getIdStr())
|
||||
// .orderSn(afterSale.getOrderSn())
|
||||
// .refundReason(afterSale.getReason())
|
||||
// .build();
|
||||
// PaymentMethodEnum paymentMethodEnum = PaymentMethodEnum.paymentNameOf(order.getPaymentMethod());
|
||||
// Payment payment = (Payment) SpringContextUtil.getBean(paymentMethodEnum.getPlugin());
|
||||
// payment.refund(refundLog);
|
||||
//
|
||||
// this.updateReturnGoodsNumber(afterSale);
|
||||
//
|
||||
// //记录退款流水
|
||||
// storeFlowService.refundOrder(afterSale);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 功能描述: 修改子订单中已售后退款商品数量
|
||||
// *
|
||||
// * @return void
|
||||
// * @Author ftyy
|
||||
// * @Description //TODO
|
||||
// * @Date 17:33 2021/11/18
|
||||
// * @Param [afterSale]
|
||||
// **/
|
||||
// private void updateReturnGoodsNumber(AfterSale afterSale) {
|
||||
// //根据商品id及订单sn获取子订单
|
||||
// OrderItem orderItem = orderItemService.getByOrderSnAndSkuId(afterSale.getOrderSn(), afterSale.getSkuId());
|
||||
//
|
||||
// orderItem.setReturnGoodsNumber(afterSale.getNum() + orderItem.getReturnGoodsNumber());
|
||||
//
|
||||
// //修改子订单订单中的退货数量
|
||||
// orderItemService.updateById(orderItem);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 订单取消
|
||||
// *
|
||||
// * @param afterSale
|
||||
// */
|
||||
// public void cancel(AfterSale afterSale) {
|
||||
////
|
||||
//// Order order = orderService.getBySn(afterSale.getOrderSn());
|
||||
//// RefundLog refundLog = RefundLog.builder()
|
||||
//// .isRefund(false)
|
||||
//// .totalAmount(afterSale.getActualRefundPrice())
|
||||
//// .payPrice(afterSale.getActualRefundPrice())
|
||||
//// .memberId(afterSale.getMemberId())
|
||||
//// .paymentName(order.getPaymentMethod())
|
||||
//// .afterSaleNo(afterSale.getSn())
|
||||
//// .paymentReceivableNo(order.getReceivableNo())
|
||||
//// .outOrderNo("AF" + SnowFlake.getIdStr())
|
||||
//// .orderSn(afterSale.getOrderSn())
|
||||
//// .refundReason(afterSale.getReason())
|
||||
//// .build();
|
||||
//// PaymentMethodEnum paymentMethodEnum = PaymentMethodEnum.paymentNameOf(order.getPaymentMethod());
|
||||
//// Payment payment = (Payment) SpringContextUtil.getBean(paymentMethodEnum.getPlugin());
|
||||
//// payment.refund(refundLog);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// /**
|
||||
// * 退款通知
|
||||
// *
|
||||
// * @param paymentMethodEnum 支付渠道
|
||||
// */
|
||||
// public void notify(PaymentMethodEnum paymentMethodEnum,
|
||||
// HttpServletRequest request) {
|
||||
//
|
||||
// //获取支付插件
|
||||
// Payment payment = (Payment) SpringContextUtil.getBean(paymentMethodEnum.getPlugin());
|
||||
// payment.refundNotify(request);
|
||||
// }
|
||||
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
|
||||
package com.wzj.soopin.transaction.kit.core;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.map.CaseInsensitiveMap;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 支付接口响应
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020/12/18 15:13
|
||||
*/
|
||||
public class PaymentHttpResponse implements Serializable {
|
||||
private static final long serialVersionUID = 6089103955998013402L;
|
||||
private String body;
|
||||
private int status;
|
||||
private Map<String, List<String>> headers;
|
||||
|
||||
public String getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
public void setBody(String body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
public int getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(int status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
public void setHeaders(Map<String, List<String>> headers) {
|
||||
this.headers = headers;
|
||||
}
|
||||
|
||||
public String getHeader(String name) {
|
||||
List<String> values = this.headerList(name);
|
||||
return CollectionUtil.isEmpty(values) ? null : values.get(0);
|
||||
}
|
||||
|
||||
private List<String> headerList(String name) {
|
||||
if (StrUtil.isBlank(name)) {
|
||||
return null;
|
||||
} else {
|
||||
CaseInsensitiveMap<String, List<String>> headersIgnoreCase = new CaseInsensitiveMap<>(getHeaders());
|
||||
return headersIgnoreCase.get(name.trim());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "IJPayHttpResponse{" +
|
||||
"body='" + body + '\'' +
|
||||
", status=" + status +
|
||||
", headers=" + headers +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,249 @@
|
||||
package com.wzj.soopin.transaction.kit.core;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* xpath解析xml
|
||||
*
|
||||
* <pre>
|
||||
* 文档地址:
|
||||
* http://www.w3school.com.cn/xpath/index.asp
|
||||
* </pre>
|
||||
* @author L.cm
|
||||
*/
|
||||
public class XmlHelper {
|
||||
private final XPath path;
|
||||
private final Document doc;
|
||||
|
||||
private XmlHelper(InputSource inputSource) throws ParserConfigurationException, SAXException, IOException {
|
||||
DocumentBuilderFactory dbf = getDocumentBuilderFactory();
|
||||
//This is the PRIMARY defense. If DTDs (doctypes) are disallowed, almost all
|
||||
//XML entity attacks are prevented
|
||||
//Xerces 2 only -
|
||||
//http://xerces.apache.org/xerces2-j/features.html#disallow-doctype-decl
|
||||
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
|
||||
//If you can't completely disable DTDs, then at least do the following:
|
||||
//Xerces 1 -
|
||||
//http://xerces.apache.org/xerces-j/features.html#external-general-entities
|
||||
//Xerces 2 -
|
||||
//http://xerces.apache.org/xerces2-j/features.html#external-general-entities
|
||||
//JDK7+ - http://xml.org/sax/features/external-general-entities
|
||||
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
|
||||
|
||||
//Xerces 1 -
|
||||
//http://xerces.apache.org/xerces-j/features.html#external-parameter-entities
|
||||
//Xerces 2 -
|
||||
//http://xerces.apache.org/xerces2-j/features.html#external-parameter-entities
|
||||
//JDK7+ - http://xml.org/sax/features/external-parameter-entities
|
||||
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
|
||||
|
||||
//Disable external DTDs as well
|
||||
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
|
||||
|
||||
//and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and
|
||||
//Entity Attacks"
|
||||
dbf.setXIncludeAware(false);
|
||||
dbf.setExpandEntityReferences(false);
|
||||
DocumentBuilder db = dbf.newDocumentBuilder();
|
||||
doc = db.parse(inputSource);
|
||||
path = getXpathFactory().newXPath();
|
||||
}
|
||||
|
||||
private static XmlHelper create(InputSource inputSource) {
|
||||
try {
|
||||
return new XmlHelper(inputSource);
|
||||
} catch (ParserConfigurationException | SAXException | IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static XmlHelper of(InputStream is) {
|
||||
InputSource inputSource = new InputSource(is);
|
||||
return create(inputSource);
|
||||
}
|
||||
|
||||
public static XmlHelper of(File file) {
|
||||
InputSource inputSource = new InputSource(file.toURI().toASCIIString());
|
||||
return create(inputSource);
|
||||
}
|
||||
|
||||
public static XmlHelper of(String xmlStr) {
|
||||
StringReader sr = new StringReader(xmlStr.trim());
|
||||
InputSource inputSource = new InputSource(sr);
|
||||
XmlHelper xmlHelper = create(inputSource);
|
||||
sr.close();
|
||||
return xmlHelper;
|
||||
}
|
||||
|
||||
private Object evalXpath(String expression, Object item, QName returnType) {
|
||||
item = null == item ? doc : item;
|
||||
try {
|
||||
return path.evaluate(expression, item, returnType);
|
||||
} catch (XPathExpressionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取String
|
||||
*
|
||||
* @param expression 路径
|
||||
* @return String
|
||||
*/
|
||||
public String getString(String expression) {
|
||||
return (String) evalXpath(expression, null, XPathConstants.STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Boolean
|
||||
*
|
||||
* @param expression 路径
|
||||
* @return String
|
||||
*/
|
||||
public Boolean getBoolean(String expression) {
|
||||
return (Boolean) evalXpath(expression, null, XPathConstants.BOOLEAN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Number
|
||||
*
|
||||
* @param expression 路径
|
||||
* @return {Number}
|
||||
*/
|
||||
public Number getNumber(String expression) {
|
||||
return (Number) evalXpath(expression, null, XPathConstants.NUMBER);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某个节点
|
||||
*
|
||||
* @param expression 路径
|
||||
* @return {Node}
|
||||
*/
|
||||
public Node getNode(String expression) {
|
||||
return (Node) evalXpath(expression, null, XPathConstants.NODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取子节点
|
||||
*
|
||||
* @param expression 路径
|
||||
* @return NodeList
|
||||
*/
|
||||
public NodeList getNodeList(String expression) {
|
||||
return (NodeList) evalXpath(expression, null, XPathConstants.NODESET);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取String
|
||||
*
|
||||
* @param node 节点
|
||||
* @param expression 相对于node的路径
|
||||
* @return String
|
||||
*/
|
||||
public String getString(Object node, String expression) {
|
||||
return (String) evalXpath(expression, node, XPathConstants.STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取
|
||||
*
|
||||
* @param node 节点
|
||||
* @param expression 相对于node的路径
|
||||
* @return String
|
||||
*/
|
||||
public Boolean getBoolean(Object node, String expression) {
|
||||
return (Boolean) evalXpath(expression, node, XPathConstants.BOOLEAN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取
|
||||
*
|
||||
* @param node 节点
|
||||
* @param expression 相对于node的路径
|
||||
* @return {Number}
|
||||
*/
|
||||
public Number getNumber(Object node, String expression) {
|
||||
return (Number) evalXpath(expression, node, XPathConstants.NUMBER);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某个节点
|
||||
*
|
||||
* @param node 节点
|
||||
* @param expression 路径
|
||||
* @return {Node}
|
||||
*/
|
||||
public Node getNode(Object node, String expression) {
|
||||
return (Node) evalXpath(expression, node, XPathConstants.NODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取子节点
|
||||
*
|
||||
* @param node 节点
|
||||
* @param expression 相对于node的路径
|
||||
* @return NodeList
|
||||
*/
|
||||
public NodeList getNodeList(Object node, String expression) {
|
||||
return (NodeList) evalXpath(expression, node, XPathConstants.NODESET);
|
||||
}
|
||||
|
||||
/**
|
||||
* 针对没有嵌套节点的简单处理
|
||||
*
|
||||
* @return map集合
|
||||
*/
|
||||
public Map<String, String> toMap() {
|
||||
Element root = doc.getDocumentElement();
|
||||
|
||||
//将节点封装成map形式
|
||||
NodeList list = root.getChildNodes();
|
||||
Map<String, String> params = new HashMap<String, String>(list.getLength());
|
||||
for (int i = 0; i < list.getLength(); i++) {
|
||||
Node node = list.item(i);
|
||||
params.put(node.getNodeName(), node.getTextContent());
|
||||
}
|
||||
//含有空白符会生成一个#text参数
|
||||
params.remove("#text");
|
||||
return params;
|
||||
}
|
||||
|
||||
private static DocumentBuilderFactory getDocumentBuilderFactory() {
|
||||
return XmlHelperHolder.documentBuilderFactory;
|
||||
}
|
||||
|
||||
private static XPathFactory getXpathFactory() {
|
||||
return XmlHelperHolder.xPathFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部类单例
|
||||
*/
|
||||
private static class XmlHelperHolder {
|
||||
private static DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
private static XPathFactory xPathFactory = XPathFactory.newInstance();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package com.wzj.soopin.transaction.kit.core.enums;
|
||||
|
||||
/**
|
||||
* HTTP 请求的方法
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2021-01-25 15:10
|
||||
*/
|
||||
public enum RequestMethodEnums {
|
||||
/**
|
||||
* 上传实质是 post 请求
|
||||
*/
|
||||
UPLOAD("POST"),
|
||||
/**
|
||||
* post 请求
|
||||
*/
|
||||
POST("POST"),
|
||||
/**
|
||||
* get 请求
|
||||
*/
|
||||
GET("GET"),
|
||||
/**
|
||||
* put 请求
|
||||
*/
|
||||
PUT("PUT"),
|
||||
/**
|
||||
* delete 请求
|
||||
*/
|
||||
DELETE("DELETE"),
|
||||
/**
|
||||
* options 请求
|
||||
*/
|
||||
OPTIONS("OPTIONS"),
|
||||
/**
|
||||
* head 请求
|
||||
*/
|
||||
HEAD("HEAD"),
|
||||
/**
|
||||
* trace 请求
|
||||
*/
|
||||
TRACE("TRACE"),
|
||||
/**
|
||||
* connect 请求
|
||||
*/
|
||||
CONNECT("CONNECT");
|
||||
|
||||
private final String method;
|
||||
|
||||
RequestMethodEnums(String method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.method;
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package com.wzj.soopin.transaction.kit.core.enums;
|
||||
|
||||
/**
|
||||
* 签名方式
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2021-01-25 15:10
|
||||
*/
|
||||
public enum SignType {
|
||||
/**
|
||||
* HMAC-SHA256 加密
|
||||
*/
|
||||
HMACSHA256("HMAC-SHA256"),
|
||||
/**
|
||||
* MD5 加密
|
||||
*/
|
||||
MD5("MD5"),
|
||||
/**
|
||||
* RSA
|
||||
*/
|
||||
RSA("RSA");
|
||||
|
||||
SignType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
private final String type;
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return type;
|
||||
}
|
||||
}
|
@ -0,0 +1,498 @@
|
||||
package com.wzj.soopin.transaction.kit.core.http;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.http.ssl.SSLSocketFactoryBuilder;
|
||||
import com.wzj.soopin.transaction.kit.core.PaymentHttpResponse;
|
||||
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyStore;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Http 代理类
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2021-01-25 15:10
|
||||
*/
|
||||
public abstract class AbstractHttpDelegate {
|
||||
|
||||
/**
|
||||
* get 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @return {@link String} 请求返回的结果
|
||||
*/
|
||||
public String get(String url) {
|
||||
return HttpUtil.get(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* get 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param paramMap 请求参数
|
||||
* @return {@link String} 请求返回的结果
|
||||
*/
|
||||
public String get(String url, Map<String, Object> paramMap) {
|
||||
return HttpUtil.get(url, paramMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* get 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param paramMap 请求参数
|
||||
* @param headers 请求头
|
||||
* @return {@link PaymentHttpResponse} 请求返回的结果
|
||||
*/
|
||||
public PaymentHttpResponse get(String url, Map<String, Object> paramMap, Map<String, String> headers) {
|
||||
PaymentHttpResponse response = new PaymentHttpResponse();
|
||||
HttpResponse httpResponse = getToResponse(url, paramMap, headers);
|
||||
response.setBody(httpResponse.body());
|
||||
response.setStatus(httpResponse.getStatus());
|
||||
response.setHeaders(httpResponse.headers());
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* post 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param data 请求参数
|
||||
* @return {@link String} 请求返回的结果
|
||||
*/
|
||||
public String post(String url, String data) {
|
||||
return HttpUtil.post(url, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* post 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param paramMap 请求参数
|
||||
* @return {@link String} 请求返回的结果
|
||||
*/
|
||||
public String post(String url, Map<String, Object> paramMap) {
|
||||
return HttpUtil.post(url, paramMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* post 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param paramMap 请求参数
|
||||
* @param headers 请求头
|
||||
* @return {@link PaymentHttpResponse} 请求返回的结果
|
||||
*/
|
||||
public PaymentHttpResponse post(String url, Map<String, Object> paramMap, Map<String, String> headers) {
|
||||
PaymentHttpResponse response = new PaymentHttpResponse();
|
||||
HttpResponse httpResponse = postToResponse(url, headers, paramMap);
|
||||
response.setBody(httpResponse.body());
|
||||
response.setStatus(httpResponse.getStatus());
|
||||
response.setHeaders(httpResponse.headers());
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* post 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param data 请求参数
|
||||
* @param headers 请求头
|
||||
* @return {@link PaymentHttpResponse} 请求返回的结果
|
||||
*/
|
||||
public PaymentHttpResponse post(String url, String data, Map<String, String> headers) {
|
||||
PaymentHttpResponse response = new PaymentHttpResponse();
|
||||
HttpResponse httpResponse = postToResponse(url, headers, data);
|
||||
response.setBody(httpResponse.body());
|
||||
response.setStatus(httpResponse.getStatus());
|
||||
response.setHeaders(httpResponse.headers());
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* patch 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param paramMap 请求参数
|
||||
* @param headers 请求头
|
||||
* @return {@link PaymentHttpResponse} 请求返回的结果
|
||||
*/
|
||||
public PaymentHttpResponse patch(String url, Map<String, Object> paramMap, Map<String, String> headers) {
|
||||
PaymentHttpResponse response = new PaymentHttpResponse();
|
||||
HttpResponse httpResponse = patchToResponse(url, headers, paramMap);
|
||||
response.setBody(httpResponse.body());
|
||||
response.setStatus(httpResponse.getStatus());
|
||||
response.setHeaders(httpResponse.headers());
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* patch 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param data 请求参数
|
||||
* @param headers 请求头
|
||||
* @return {@link PaymentHttpResponse} 请求返回的结果
|
||||
*/
|
||||
public PaymentHttpResponse patch(String url, String data, Map<String, String> headers) {
|
||||
PaymentHttpResponse response = new PaymentHttpResponse();
|
||||
HttpResponse httpResponse = patchToResponse(url, headers, data);
|
||||
response.setBody(httpResponse.body());
|
||||
response.setStatus(httpResponse.getStatus());
|
||||
response.setHeaders(httpResponse.headers());
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* delete 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param paramMap 请求参数
|
||||
* @param headers 请求头
|
||||
* @return {@link PaymentHttpResponse} 请求返回的结果
|
||||
*/
|
||||
public PaymentHttpResponse delete(String url, Map<String, Object> paramMap, Map<String, String> headers) {
|
||||
PaymentHttpResponse response = new PaymentHttpResponse();
|
||||
HttpResponse httpResponse = deleteToResponse(url, headers, paramMap);
|
||||
response.setBody(httpResponse.body());
|
||||
response.setStatus(httpResponse.getStatus());
|
||||
response.setHeaders(httpResponse.headers());
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* delete 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param data 请求参数
|
||||
* @param headers 请求头
|
||||
* @return {@link PaymentHttpResponse} 请求返回的结果
|
||||
*/
|
||||
public PaymentHttpResponse delete(String url, String data, Map<String, String> headers) {
|
||||
PaymentHttpResponse response = new PaymentHttpResponse();
|
||||
HttpResponse httpResponse = deleteToResponse(url, headers, data);
|
||||
response.setBody(httpResponse.body());
|
||||
response.setStatus(httpResponse.getStatus());
|
||||
response.setHeaders(httpResponse.headers());
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* put 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param paramMap 请求参数
|
||||
* @param headers 请求头
|
||||
* @return {@link PaymentHttpResponse} 请求返回的结果
|
||||
*/
|
||||
public PaymentHttpResponse put(String url, Map<String, Object> paramMap, Map<String, String> headers) {
|
||||
PaymentHttpResponse response = new PaymentHttpResponse();
|
||||
HttpResponse httpResponse = putToResponse(url, headers, paramMap);
|
||||
response.setBody(httpResponse.body());
|
||||
response.setStatus(httpResponse.getStatus());
|
||||
response.setHeaders(httpResponse.headers());
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* put 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param data 请求参数
|
||||
* @param headers 请求头
|
||||
* @return {@link PaymentHttpResponse} 请求返回的结果
|
||||
*/
|
||||
public PaymentHttpResponse put(String url, String data, Map<String, String> headers) {
|
||||
PaymentHttpResponse response = new PaymentHttpResponse();
|
||||
HttpResponse httpResponse = putToResponse(url, headers, data);
|
||||
response.setBody(httpResponse.body());
|
||||
response.setStatus(httpResponse.getStatus());
|
||||
response.setHeaders(httpResponse.headers());
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param data 请求参数
|
||||
* @param certPath 证书路径
|
||||
* @param certPass 证书密码
|
||||
* @param filePath 上传文件路径
|
||||
* @param protocol 协议
|
||||
* @return {@link String} 请求返回的结果
|
||||
*/
|
||||
public String upload(String url, String data, String certPath, String certPass, String filePath, String protocol) {
|
||||
try {
|
||||
File file = FileUtil.newFile(filePath);
|
||||
return HttpRequest.post(url)
|
||||
.setSSLSocketFactory(SSLSocketFactoryBuilder
|
||||
.create()
|
||||
.setProtocol(protocol)
|
||||
.setKeyManagers(getKeyManager(certPass, certPath, null))
|
||||
.setSecureRandom(new SecureRandom())
|
||||
.build()
|
||||
)
|
||||
.header("Content-Type", "multipart/form-data;boundary=\"boundary\"")
|
||||
.form("file", file)
|
||||
.form("meta", data)
|
||||
.execute()
|
||||
.body();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param data 请求参数
|
||||
* @param certPath 证书路径
|
||||
* @param certPass 证书密码
|
||||
* @param filePath 上传文件路径
|
||||
* @return {@link String} 请求返回的结果
|
||||
*/
|
||||
public String upload(String url, String data, String certPath, String certPass, String filePath) {
|
||||
return upload(url, data, certPath, certPass, filePath, SSLSocketFactoryBuilder.TLSv1);
|
||||
}
|
||||
|
||||
/**
|
||||
* post 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param data 请求参数
|
||||
* @param certPath 证书路径
|
||||
* @param certPass 证书密码
|
||||
* @param protocol 协议
|
||||
* @return {@link String} 请求返回的结果
|
||||
*/
|
||||
public String post(String url, String data, String certPath, String certPass, String protocol) {
|
||||
try {
|
||||
return HttpRequest.post(url)
|
||||
.setSSLSocketFactory(SSLSocketFactoryBuilder
|
||||
.create()
|
||||
.setProtocol(protocol)
|
||||
.setKeyManagers(getKeyManager(certPass, certPath, null))
|
||||
.setSecureRandom(new SecureRandom())
|
||||
.build()
|
||||
)
|
||||
.body(data)
|
||||
.execute()
|
||||
.body();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* post 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param data 请求参数
|
||||
* @param certPath 证书路径
|
||||
* @param certPass 证书密码
|
||||
* @return {@link String} 请求返回的结果
|
||||
*/
|
||||
public String post(String url, String data, String certPath, String certPass) {
|
||||
return post(url, data, certPath, certPass, SSLSocketFactoryBuilder.TLSv1);
|
||||
}
|
||||
|
||||
/**
|
||||
* post 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param data 请求参数
|
||||
* @param certFile 证书文件输入流
|
||||
* @param certPass 证书密码
|
||||
* @param protocol 协议
|
||||
* @return {@link String} 请求返回的结果
|
||||
*/
|
||||
public String post(String url, String data, InputStream certFile, String certPass, String protocol) {
|
||||
try {
|
||||
return HttpRequest.post(url)
|
||||
.setSSLSocketFactory(SSLSocketFactoryBuilder
|
||||
.create()
|
||||
.setProtocol(protocol)
|
||||
.setKeyManagers(getKeyManager(certPass, null, certFile))
|
||||
.setSecureRandom(new SecureRandom())
|
||||
.build()
|
||||
)
|
||||
.body(data)
|
||||
.execute()
|
||||
.body();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* post 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param data 请求参数
|
||||
* @param certFile 证书文件输入流
|
||||
* @param certPass 证书密码
|
||||
* @return {@link String} 请求返回的结果
|
||||
*/
|
||||
public String post(String url, String data, InputStream certFile, String certPass) {
|
||||
return post(url, data, certFile, certPass, SSLSocketFactoryBuilder.TLSv1);
|
||||
}
|
||||
|
||||
/**
|
||||
* get 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param paramMap 请求参数
|
||||
* @param headers 请求头
|
||||
* @return {@link HttpResponse} 请求返回的结果
|
||||
*/
|
||||
private HttpResponse getToResponse(String url, Map<String, Object> paramMap, Map<String, String> headers) {
|
||||
return HttpRequest.get(url)
|
||||
.addHeaders(headers)
|
||||
.form(paramMap)
|
||||
.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* post 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param headers 请求头
|
||||
* @param data 请求参数
|
||||
* @return {@link HttpResponse} 请求返回的结果
|
||||
*/
|
||||
private HttpResponse postToResponse(String url, Map<String, String> headers, String data) {
|
||||
return HttpRequest.post(url)
|
||||
.addHeaders(headers)
|
||||
.body(data)
|
||||
.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* post 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param headers 请求头
|
||||
* @param paramMap 请求参数
|
||||
* @return {@link HttpResponse} 请求返回的结果
|
||||
*/
|
||||
private HttpResponse postToResponse(String url, Map<String, String> headers, Map<String, Object> paramMap) {
|
||||
return HttpRequest.post(url)
|
||||
.addHeaders(headers)
|
||||
.form(paramMap)
|
||||
.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* patch 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param headers 请求头
|
||||
* @param paramMap 请求参数
|
||||
* @return {@link HttpResponse} 请求返回的结果
|
||||
*/
|
||||
private HttpResponse patchToResponse(String url, Map<String, String> headers, Map<String, Object> paramMap) {
|
||||
return HttpRequest.patch(url)
|
||||
.addHeaders(headers)
|
||||
.form(paramMap)
|
||||
.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* patch 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param headers 请求头
|
||||
* @param data 请求参数
|
||||
* @return {@link HttpResponse} 请求返回的结果
|
||||
*/
|
||||
private HttpResponse patchToResponse(String url, Map<String, String> headers, String data) {
|
||||
return HttpRequest.patch(url)
|
||||
.addHeaders(headers)
|
||||
.body(data)
|
||||
.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* delete 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param headers 请求头
|
||||
* @param data 请求参数
|
||||
* @return {@link HttpResponse} 请求返回的结果
|
||||
*/
|
||||
private HttpResponse deleteToResponse(String url, Map<String, String> headers, String data) {
|
||||
return HttpRequest.delete(url)
|
||||
.addHeaders(headers)
|
||||
.body(data)
|
||||
.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* delete 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param headers 请求头
|
||||
* @param paramMap 请求参数
|
||||
* @return {@link HttpResponse} 请求返回的结果
|
||||
*/
|
||||
private HttpResponse deleteToResponse(String url, Map<String, String> headers, Map<String, Object> paramMap) {
|
||||
return HttpRequest.delete(url)
|
||||
.addHeaders(headers)
|
||||
.form(paramMap)
|
||||
.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* put 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param headers 请求头
|
||||
* @param data 请求参数
|
||||
* @return {@link HttpResponse} 请求返回的结果
|
||||
*/
|
||||
private HttpResponse putToResponse(String url, Map<String, String> headers, String data) {
|
||||
return HttpRequest.put(url)
|
||||
.addHeaders(headers)
|
||||
.body(data)
|
||||
.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* put 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param headers 请求头
|
||||
* @param paramMap 请求参数
|
||||
* @return {@link HttpResponse} 请求返回的结果
|
||||
*/
|
||||
private HttpResponse putToResponse(String url, Map<String, String> headers, Map<String, Object> paramMap) {
|
||||
return HttpRequest.put(url)
|
||||
.addHeaders(headers)
|
||||
.form(paramMap)
|
||||
.execute();
|
||||
}
|
||||
|
||||
|
||||
private KeyManager[] getKeyManager(String certPass, String certPath, InputStream certFile) throws Exception {
|
||||
KeyStore clientStore = KeyStore.getInstance("PKCS12");
|
||||
if (certFile != null) {
|
||||
clientStore.load(certFile, certPass.toCharArray());
|
||||
} else {
|
||||
clientStore.load(new FileInputStream(certPath), certPass.toCharArray());
|
||||
}
|
||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
kmf.init(clientStore, certPass.toCharArray());
|
||||
return kmf.getKeyManagers();
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package com.wzj.soopin.transaction.kit.core.kit;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.spec.GCMParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
/**
|
||||
* <p>工具类 AesUtil</p>
|
||||
*
|
||||
* @author 微信
|
||||
*/
|
||||
public 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 associated_data
|
||||
* @param nonce nonce
|
||||
* @param cipherText ciphertext
|
||||
* @return {String} 平台证书明文
|
||||
* @throws GeneralSecurityException 异常
|
||||
*/
|
||||
public String decryptToString(byte[] associatedData, byte[] nonce, String cipherText) throws GeneralSecurityException {
|
||||
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.decode(cipherText)), StandardCharsets.UTF_8);
|
||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
|
||||
throw new IllegalStateException(e);
|
||||
} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package com.wzj.soopin.transaction.kit.core.kit;
|
||||
|
||||
import com.wzj.soopin.transaction.kit.core.http.AbstractHttpDelegate;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>Http 工具类</p>
|
||||
*
|
||||
* @author
|
||||
*/
|
||||
@Slf4j
|
||||
public class HttpKit {
|
||||
|
||||
private static AbstractHttpDelegate delegate = new DefaultHttpKit();
|
||||
|
||||
public static AbstractHttpDelegate getDelegate() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
public static void setDelegate(AbstractHttpDelegate delegate) {
|
||||
HttpKit.delegate = delegate;
|
||||
}
|
||||
|
||||
public static String readData(HttpServletRequest request) {
|
||||
BufferedReader br = null;
|
||||
try {
|
||||
StringBuilder result = new StringBuilder();
|
||||
br = request.getReader();
|
||||
for (String line; (line = br.readLine()) != null; ) {
|
||||
if (result.length() > 0) {
|
||||
result.append("\n");
|
||||
}
|
||||
result.append(line);
|
||||
}
|
||||
return result.toString();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
if (br != null) {
|
||||
try {
|
||||
br.close();
|
||||
} catch (IOException e) {
|
||||
log.error("readData错误",e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将同步通知的参数转化为Map
|
||||
*
|
||||
* @param request {@link HttpServletRequest}
|
||||
* @return 转化后的 Map
|
||||
*/
|
||||
public static Map<String, String> toMap(HttpServletRequest request) {
|
||||
Map<String, String> params = new HashMap<>(16);
|
||||
Map<String, String[]> requestParams = request.getParameterMap();
|
||||
for (String name : requestParams.keySet()) {
|
||||
String[] values = requestParams.get(name);
|
||||
String valueStr = "";
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
|
||||
}
|
||||
params.put(name, valueStr);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 huTool 实现的 Http 工具类
|
||||
*
|
||||
* @author
|
||||
*/
|
||||
class DefaultHttpKit extends AbstractHttpDelegate {
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package com.wzj.soopin.transaction.kit.core.kit;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* @author
|
||||
*/
|
||||
public class IpKit {
|
||||
public IpKit() {
|
||||
}
|
||||
|
||||
public static String getRealIp(HttpServletRequest request) {
|
||||
String ip = request.getHeader("x-forwarded-for");
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("HTTP_CLIENT_IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getRemoteAddr();
|
||||
}
|
||||
//对于通过多个代理的情况,第一个 IP 为客户端真实 IP,多个IP按照','分割
|
||||
if (ip != null && ip.length() > 15) {
|
||||
if (ip.indexOf(",") > 0) {
|
||||
ip = ip.substring(0, ip.indexOf(","));
|
||||
}
|
||||
}
|
||||
// return ip;
|
||||
return "27.189.225.9";
|
||||
}
|
||||
}
|
@ -0,0 +1,481 @@
|
||||
package com.wzj.soopin.transaction.kit.core.kit;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.digest.HmacAlgorithm;
|
||||
import com.wzj.soopin.transaction.kit.core.XmlHelper;
|
||||
import com.wzj.soopin.transaction.kit.core.enums.RequestMethodEnums;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 支付工具
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v4.0
|
||||
* @since 2020/12/18 15:24
|
||||
*/
|
||||
@Slf4j
|
||||
public class PayKit {
|
||||
|
||||
/**
|
||||
* 生成16进制的 sha256 字符串
|
||||
*
|
||||
* @param data 数据
|
||||
* @param key 密钥
|
||||
* @return sha256 字符串
|
||||
*/
|
||||
public static String hmacSha256(String data, String key) {
|
||||
return SecureUtil.hmac(HmacAlgorithm.HmacSHA256, key).digestHex(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* SHA1加密文件,生成16进制SHA1字符串<br>
|
||||
*
|
||||
* @param dataFile 被加密文件
|
||||
* @return SHA1 字符串
|
||||
*/
|
||||
public static String sha1(File dataFile) {
|
||||
return SecureUtil.sha1(dataFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* SHA1加密,生成16进制SHA1字符串<br>
|
||||
*
|
||||
* @param data 数据
|
||||
* @return SHA1 字符串
|
||||
*/
|
||||
public static String sha1(InputStream data) {
|
||||
return SecureUtil.sha1(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* SHA1加密,生成16进制SHA1字符串<br>
|
||||
*
|
||||
* @param data 数据
|
||||
* @return SHA1 字符串
|
||||
*/
|
||||
public static String sha1(String data) {
|
||||
return SecureUtil.sha1(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成16进制 MD5 字符串
|
||||
*
|
||||
* @param data 数据
|
||||
* @return MD5 字符串
|
||||
*/
|
||||
public static String md5(String data) {
|
||||
return SecureUtil.md5(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* AES 解密
|
||||
*
|
||||
* @param base64Data 需要解密的数据
|
||||
* @param key 密钥
|
||||
* @return 解密后的数据
|
||||
*/
|
||||
public static String decryptData(String base64Data, String key) {
|
||||
return SecureUtil.aes(md5(key).toLowerCase().getBytes()).decryptStr(base64Data);
|
||||
}
|
||||
|
||||
/**
|
||||
* AES 加密
|
||||
*
|
||||
* @param data 需要加密的数据
|
||||
* @param key 密钥
|
||||
* @return 加密后的数据
|
||||
*/
|
||||
public static String encryptData(String data, String key) {
|
||||
return SecureUtil.aes(md5(key).toLowerCase().getBytes()).encryptBase64(data.getBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* 把所有元素排序
|
||||
*
|
||||
* @param params 需要排序并参与字符拼接的参数组
|
||||
* @return 拼接后字符串
|
||||
*/
|
||||
public static String createLinkString(Map<String, String> params) {
|
||||
return createLinkString(params, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param params 需要排序并参与字符拼接的参数组
|
||||
* @param encode 是否进行URLEncoder
|
||||
* @return 拼接后字符串
|
||||
*/
|
||||
public static String createLinkString(Map<String, String> params, boolean encode) {
|
||||
return createLinkString(params, "&", encode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param params 需要排序并参与字符拼接的参数组
|
||||
* @param connStr 连接符号
|
||||
* @param encode 是否进行URLEncoder
|
||||
* @return 拼接后字符串
|
||||
*/
|
||||
public static String createLinkString(Map<String, String> params, String connStr, boolean encode) {
|
||||
return createLinkString(params, connStr, encode, false);
|
||||
}
|
||||
|
||||
public static String createLinkString(Map<String, String> params, String connStr, boolean encode, boolean quotes) {
|
||||
List<String> 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 ? urlEncode(value) : value).append('"');
|
||||
} else {
|
||||
content.append(key).append("=").append(encode ? urlEncode(value) : value);
|
||||
}
|
||||
} else {
|
||||
if (quotes) {
|
||||
content.append(key).append("=").append('"').append(encode ? urlEncode(value) : value).append('"').append(connStr);
|
||||
} else {
|
||||
content.append(key).append("=").append(encode ? urlEncode(value) : value).append(connStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return content.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* URL 编码
|
||||
*
|
||||
* @param src 需要编码的字符串
|
||||
* @return 编码后的字符串
|
||||
*/
|
||||
public static String urlEncode(String src) {
|
||||
try {
|
||||
return URLEncoder.encode(src, CharsetUtil.UTF_8).replace("+", "%20");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
log.error("URL 编码错误",e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 遍历 Map 并构建 xml 数据
|
||||
*
|
||||
* @param params 需要遍历的 Map
|
||||
* @param prefix xml 前缀
|
||||
* @param suffix xml 后缀
|
||||
* @return xml 字符串
|
||||
*/
|
||||
public static StringBuffer forEachMap(Map<String, String> params, String prefix, String suffix) {
|
||||
StringBuffer xml = new StringBuffer();
|
||||
if (StrUtil.isNotEmpty(prefix)) {
|
||||
xml.append(prefix);
|
||||
}
|
||||
for (Map.Entry<String, String> entry : params.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String value = entry.getValue();
|
||||
//略过空值
|
||||
if (StrUtil.isEmpty(value)) {
|
||||
continue;
|
||||
}
|
||||
xml.append("<").append(key).append(">");
|
||||
xml.append(entry.getValue());
|
||||
xml.append("</").append(key).append(">");
|
||||
}
|
||||
if (StrUtil.isNotEmpty(suffix)) {
|
||||
xml.append(suffix);
|
||||
}
|
||||
return xml;
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信下单 map to xml
|
||||
*
|
||||
* @param params Map 参数
|
||||
* @return xml 字符串
|
||||
*/
|
||||
public static String toXml(Map<String, String> params) {
|
||||
StringBuffer xml = forEachMap(params, "<xml>", "</xml>");
|
||||
return xml.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 针对支付的 xml,没有嵌套节点的简单处理
|
||||
*
|
||||
* @param xmlStr xml 字符串
|
||||
* @return 转化后的 Map
|
||||
*/
|
||||
public static Map<String, String> xmlToMap(String xmlStr) {
|
||||
XmlHelper xmlHelper = XmlHelper.of(xmlStr);
|
||||
return xmlHelper.toMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造签名串
|
||||
*
|
||||
* @param method {@link RequestMethodEnums} GET,POST,PUT等
|
||||
* @param url 请求接口 /v3/certificates
|
||||
* @param timestamp 获取发起请求时的系统当前时间戳
|
||||
* @param nonceStr 随机字符串
|
||||
* @param body 请求报文主体
|
||||
* @return 待签名字符串
|
||||
*/
|
||||
public static String buildSignMessage(RequestMethodEnums method, String url, long timestamp, String nonceStr, String body) {
|
||||
ArrayList<String> arrayList = new ArrayList<>();
|
||||
arrayList.add(method.toString());
|
||||
arrayList.add(url);
|
||||
arrayList.add(String.valueOf(timestamp));
|
||||
arrayList.add(nonceStr);
|
||||
arrayList.add(body);
|
||||
return buildSignMessage(arrayList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造签名串
|
||||
*
|
||||
* @param timestamp 应答时间戳
|
||||
* @param nonceStr 应答随机串
|
||||
* @param body 应答报文主体
|
||||
* @return 应答待签名字符串
|
||||
*/
|
||||
public static String buildSignMessage(String timestamp, String nonceStr, String body) {
|
||||
ArrayList<String> arrayList = new ArrayList<>();
|
||||
arrayList.add(timestamp);
|
||||
arrayList.add(nonceStr);
|
||||
arrayList.add(body);
|
||||
return buildSignMessage(arrayList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造签名串
|
||||
*
|
||||
* @param signMessage 待签名的参数
|
||||
* @return 构造后带待签名串
|
||||
*/
|
||||
public static String buildSignMessage(ArrayList<String> signMessage) {
|
||||
if (signMessage == null || signMessage.size() <= 0) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder sbf = new StringBuilder();
|
||||
for (String str : signMessage) {
|
||||
sbf.append(str).append("\n");
|
||||
}
|
||||
return sbf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* v3 接口创建签名
|
||||
*
|
||||
* @param signMessage 待签名的参数
|
||||
* @param keyPath key.pem 证书路径
|
||||
* @return 生成 v3 签名
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static String createSign(ArrayList<String> signMessage, String keyPath) throws Exception {
|
||||
return createSign(buildSignMessage(signMessage), keyPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* v3 接口创建签名
|
||||
*
|
||||
* @param signMessage 待签名的参数
|
||||
* @param privateKey 商户私钥
|
||||
* @return 生成 v3 签名
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static String createSign(ArrayList<String> signMessage, PrivateKey privateKey) throws Exception {
|
||||
return createSign(buildSignMessage(signMessage), privateKey);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* v3 接口创建签名
|
||||
*
|
||||
* @param signMessage 待签名的参数
|
||||
* @param keyPath key.pem 证书路径
|
||||
* @return 生成 v3 签名
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static String createSign(String signMessage, String keyPath) throws Exception {
|
||||
if (StrUtil.isEmpty(signMessage)) {
|
||||
return null;
|
||||
}
|
||||
//获取商户私钥
|
||||
PrivateKey privateKey = PayKit.getPrivateKey(keyPath);
|
||||
//生成签名
|
||||
return RsaKit.encryptByPrivateKey(signMessage, privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* v3 接口创建签名
|
||||
*
|
||||
* @param signMessage 待签名的参数
|
||||
* @param privateKey 商户私钥
|
||||
* @return 生成 v3 签名
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static String createSign(String signMessage, PrivateKey privateKey) throws Exception {
|
||||
if (StrUtil.isEmpty(signMessage)) {
|
||||
return null;
|
||||
}
|
||||
//生成签名
|
||||
return RsaKit.encryptByPrivateKey(signMessage, privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取授权认证信息
|
||||
*
|
||||
* @param mchId 商户号
|
||||
* @param serialNo 商户API证书序列号
|
||||
* @param nonceStr 请求随机串
|
||||
* @param timestamp 时间戳
|
||||
* @param signature 签名值
|
||||
* @param authType 认证类型,目前为WECHATPAY2-SHA256-RSA2048
|
||||
* @return 请求头 Authorization
|
||||
*/
|
||||
public static String getAuthorization(String mchId, String serialNo, String nonceStr, String timestamp, String signature, String authType) {
|
||||
Map<String, String> 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 keyPath 商户私钥证书路径
|
||||
* @return {@link String} 商户私钥
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static String getPrivateKeyStr(String keyPath) throws Exception {
|
||||
return RsaKit.getPrivateKeyStr(getPrivateKey(keyPath));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取商户私钥
|
||||
*
|
||||
* @param keyPath 商户私钥证书路径
|
||||
* @return {@link PrivateKey} 商户私钥
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static PrivateKey getPrivateKey(String keyPath) throws Exception {
|
||||
String originalKey = FileUtil.readUtf8String(keyPath);
|
||||
String privateKey = originalKey
|
||||
.replace("-----BEGIN PRIVATE KEY-----", "")
|
||||
.replace("-----END PRIVATE KEY-----", "")
|
||||
.replaceAll("\\s+", "");
|
||||
|
||||
return RsaKit.loadPrivateKey(privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取证书
|
||||
*
|
||||
* @param inputStream 证书文件
|
||||
* @return {@link X509Certificate} 获取证书
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取证书
|
||||
*
|
||||
* @param publicKey 证书
|
||||
* @return {@link X509Certificate} 获取证书
|
||||
*/
|
||||
public static X509Certificate getCertificate(String publicKey) {
|
||||
try {
|
||||
X509Certificate cert = PayKit.getCertificate(new ByteArrayInputStream(publicKey.getBytes()));
|
||||
cert.checkValidity();
|
||||
return cert;
|
||||
} catch (CertificateExpiredException e) {
|
||||
throw new RuntimeException("证书已过期", e);
|
||||
} catch (CertificateNotYetValidException e) {
|
||||
throw new RuntimeException("证书尚未生效", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 公钥加密
|
||||
*
|
||||
* @param data 待加密数据
|
||||
* @param certificate 平台公钥证书
|
||||
* @return 加密后的数据
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static String rsaEncryptOAEP(String data, X509Certificate certificate) throws Exception {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey());
|
||||
|
||||
byte[] dataByte = data.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] cipherData = cipher.doFinal(dataByte);
|
||||
return Base64.encode(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 Exception 异常信息
|
||||
*/
|
||||
public static String rsaDecryptOAEP(String cipherText, PrivateKey privateKey) throws Exception {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
|
||||
cipher.init(Cipher.DECRYPT_MODE, privateKey);
|
||||
byte[] data = Base64.decode(cipherText);
|
||||
return new String(cipher.doFinal(data), StandardCharsets.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("解密失败");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
package com.wzj.soopin.transaction.kit.core.kit;
|
||||
|
||||
import com.google.zxing.*;
|
||||
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
|
||||
import com.google.zxing.client.j2se.MatrixToImageConfig;
|
||||
import com.google.zxing.client.j2se.MatrixToImageWriter;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.common.HybridBinarizer;
|
||||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p> google 开源图形码工具类</p>
|
||||
*
|
||||
* @author
|
||||
*/
|
||||
@Slf4j
|
||||
public class QrCodeKit {
|
||||
/**
|
||||
* 图形码生成工具
|
||||
*
|
||||
* @param contents 内容
|
||||
* @param barcodeFormat BarcodeFormat对象
|
||||
* @param format 图片格式,可选[png,jpg,bmp]
|
||||
* @param width 宽
|
||||
* @param height 高
|
||||
* @param margin 边框间距px
|
||||
* @param saveImgFilePath 存储图片的完整位置,包含文件名
|
||||
* @return {boolean}
|
||||
*/
|
||||
public static boolean encode(String contents, BarcodeFormat barcodeFormat, Integer margin,
|
||||
ErrorCorrectionLevel errorLevel, String format, int width, int height, String saveImgFilePath) {
|
||||
boolean bool = false;
|
||||
BufferedImage bufImg;
|
||||
Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>(3);
|
||||
//指定纠错等级
|
||||
hints.put(EncodeHintType.ERROR_CORRECTION, errorLevel);
|
||||
hints.put(EncodeHintType.MARGIN, margin);
|
||||
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
|
||||
try {
|
||||
BitMatrix bitMatrix = new MultiFormatWriter().encode(contents, barcodeFormat, width, height, hints);
|
||||
MatrixToImageConfig config = new MatrixToImageConfig(0xFF000001, 0xFFFFFFFF);
|
||||
bufImg = MatrixToImageWriter.toBufferedImage(bitMatrix, config);
|
||||
bool = writeToFile(bufImg, format, saveImgFilePath);
|
||||
} catch (Exception e) {
|
||||
log.error("图形码生成工具生成错误",e);
|
||||
}
|
||||
return bool;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param srcImgFilePath 要解码的图片地址
|
||||
* @return {Result}
|
||||
*/
|
||||
public static Result decode(String srcImgFilePath) {
|
||||
Result result = null;
|
||||
BufferedImage image;
|
||||
try {
|
||||
File srcFile = new File(srcImgFilePath);
|
||||
image = ImageIO.read(srcFile);
|
||||
if (null != image) {
|
||||
LuminanceSource source = new BufferedImageLuminanceSource(image);
|
||||
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
|
||||
|
||||
Hashtable<DecodeHintType, String> hints = new Hashtable<DecodeHintType, String>();
|
||||
hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
|
||||
result = new MultiFormatReader().decode(bitmap, hints);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Could not decode image.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("图片解码错误",e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将BufferedImage对象写入文件
|
||||
*
|
||||
* @param bufImg BufferedImage对象
|
||||
* @param format 图片格式,可选[png,jpg,bmp]
|
||||
* @param saveImgFilePath 存储图片的完整位置,包含文件名
|
||||
* @return {boolean}
|
||||
*/
|
||||
public static boolean writeToFile(BufferedImage bufImg, String format, String saveImgFilePath) {
|
||||
boolean bool = false;
|
||||
try {
|
||||
bool = ImageIO.write(bufImg, format, new File(saveImgFilePath));
|
||||
} catch (Exception e) {
|
||||
log.error("将BufferedImage对象写入文件错误",e);
|
||||
}
|
||||
return bool;
|
||||
}
|
||||
}
|
@ -0,0 +1,362 @@
|
||||
package com.wzj.soopin.transaction.kit.core.kit;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.*;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.RSAPublicKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>RSA 非对称加密工具类</p>
|
||||
*
|
||||
* @author
|
||||
*/
|
||||
@Slf4j
|
||||
public class RsaKit {
|
||||
|
||||
/**
|
||||
* RSA最大加密明文大小
|
||||
*/
|
||||
private static final int MAX_ENCRYPT_BLOCK = 117;
|
||||
|
||||
/**
|
||||
* RSA最大解密密文大小
|
||||
*/
|
||||
private static final int MAX_DECRYPT_BLOCK = 128;
|
||||
|
||||
/**
|
||||
* 加密算法RSA
|
||||
*/
|
||||
private static final String KEY_ALGORITHM = "RSA";
|
||||
|
||||
/**
|
||||
* 生成公钥和私钥
|
||||
*
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static Map<String, String> getKeys() throws Exception {
|
||||
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
|
||||
keyPairGen.initialize(1024);
|
||||
KeyPair keyPair = keyPairGen.generateKeyPair();
|
||||
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
|
||||
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
|
||||
|
||||
String publicKeyStr = getPublicKeyStr(publicKey);
|
||||
String privateKeyStr = getPrivateKeyStr(privateKey);
|
||||
|
||||
Map<String, String> map = new HashMap<String, String>(2);
|
||||
map.put("publicKey", publicKeyStr);
|
||||
map.put("privateKey", privateKeyStr);
|
||||
|
||||
System.out.println("公钥\r\n" + publicKeyStr);
|
||||
System.out.println("私钥\r\n" + privateKeyStr);
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用模和指数生成RSA公钥
|
||||
* 注意:【此代码用了默认补位方式,为RSA/None/PKCS1Padding,不同JDK默认的补位方式可能不同,如Android默认是RSA
|
||||
* /None/NoPadding】
|
||||
*
|
||||
* @param modulus 模
|
||||
* @param exponent 公钥指数
|
||||
* @return {@link RSAPublicKey}
|
||||
*/
|
||||
public static RSAPublicKey getPublicKey(String modulus, String exponent) {
|
||||
try {
|
||||
BigInteger b1 = new BigInteger(modulus);
|
||||
BigInteger b2 = new BigInteger(exponent);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
|
||||
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(b1, b2);
|
||||
return (RSAPublicKey) keyFactory.generatePublic(keySpec);
|
||||
} catch (Exception e) {
|
||||
log.error("使用模和指数生成RSA公钥错误",e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 公钥加密
|
||||
*
|
||||
* @param data 需要加密的数据
|
||||
* @param publicKey 公钥
|
||||
* @return 加密后的数据
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static String encryptByPublicKey(String data, String publicKey) throws Exception {
|
||||
return encryptByPublicKey(data, publicKey, "RSA/ECB/PKCS1Padding");
|
||||
}
|
||||
|
||||
/**
|
||||
* 公钥加密
|
||||
*
|
||||
* @param data 需要加密的数据
|
||||
* @param publicKey 公钥
|
||||
* @return 加密后的数据
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static String encryptByPublicKeyByWx(String data, String publicKey) throws Exception {
|
||||
return encryptByPublicKey(data, publicKey, "RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING");
|
||||
}
|
||||
|
||||
/**
|
||||
* 公钥加密
|
||||
*
|
||||
* @param data 需要加密的数据
|
||||
* @param publicKey 公钥
|
||||
* @param fillMode 填充模式
|
||||
* @return 加密后的数据
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static String encryptByPublicKey(String data, String publicKey, String fillMode) throws Exception {
|
||||
byte[] dataByte = data.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] keyBytes = Base64.decode(publicKey);
|
||||
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
|
||||
Key key = keyFactory.generatePublic(x509KeySpec);
|
||||
//对数据加密
|
||||
Cipher cipher = Cipher.getInstance(fillMode);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key);
|
||||
int inputLen = dataByte.length;
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
int offSet = 0;
|
||||
byte[] cache;
|
||||
int i = 0;
|
||||
//对数据分段加密
|
||||
while (inputLen - offSet > 0) {
|
||||
if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
|
||||
cache = cipher.doFinal(dataByte, offSet, MAX_ENCRYPT_BLOCK);
|
||||
} else {
|
||||
cache = cipher.doFinal(dataByte, offSet, inputLen - offSet);
|
||||
}
|
||||
out.write(cache, 0, cache.length);
|
||||
i++;
|
||||
offSet = i * MAX_ENCRYPT_BLOCK;
|
||||
}
|
||||
byte[] encryptedData = out.toByteArray();
|
||||
out.close();
|
||||
return StrUtil.str(Base64.encode(encryptedData));
|
||||
}
|
||||
|
||||
/**
|
||||
* 私钥签名
|
||||
*
|
||||
* @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);
|
||||
Signature signature = Signature.getInstance("SHA256WithRSA");
|
||||
|
||||
signature.initSign(priKey);
|
||||
signature.update(data.getBytes(StandardCharsets.UTF_8));
|
||||
byte[] signed = signature.sign();
|
||||
return StrUtil.str(Base64.encode(signed));
|
||||
}
|
||||
|
||||
/**
|
||||
* 私钥签名
|
||||
*
|
||||
* @param data 需要加密的数据
|
||||
* @param privateKey 私钥
|
||||
* @return 加密后的数据
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static String encryptByPrivateKey(String data, PrivateKey privateKey) throws Exception {
|
||||
Signature signature = Signature.getInstance("SHA256WithRSA");
|
||||
signature.initSign(privateKey);
|
||||
signature.update(data.getBytes(StandardCharsets.UTF_8));
|
||||
byte[] signed = signature.sign();
|
||||
return StrUtil.str(Base64.encode(signed));
|
||||
}
|
||||
|
||||
/**
|
||||
* 公钥验证签名
|
||||
*
|
||||
* @param data 需要加密的数据
|
||||
* @param sign 签名
|
||||
* @param publicKey 公钥
|
||||
* @return 验证结果
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static boolean checkByPublicKey(String data, String sign, String publicKey) throws Exception {
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
|
||||
byte[] encodedKey = Base64.decode(publicKey);
|
||||
PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
|
||||
Signature signature = Signature.getInstance("SHA256WithRSA");
|
||||
signature.initVerify(pubKey);
|
||||
signature.update(data.getBytes(StandardCharsets.UTF_8));
|
||||
return signature.verify(Base64.decode(sign.getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 公钥验证签名
|
||||
*
|
||||
* @param data 需要加密的数据
|
||||
* @param sign 签名
|
||||
* @param publicKey 公钥
|
||||
* @return 验证结果
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static boolean checkByPublicKey(String data, String sign, PublicKey publicKey) throws Exception {
|
||||
Signature signature = Signature.getInstance("SHA256WithRSA");
|
||||
signature.initVerify(publicKey);
|
||||
signature.update(data.getBytes(StandardCharsets.UTF_8));
|
||||
return signature.verify(Base64.decode(sign.getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 私钥解密
|
||||
*
|
||||
* @param data 需要解密的数据
|
||||
* @param privateKey 私钥
|
||||
* @return 解密后的数据
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static String decryptByPrivateKey(String data, String privateKey) throws Exception {
|
||||
return decryptByPrivateKey(data, privateKey, "RSA/ECB/PKCS1Padding");
|
||||
}
|
||||
|
||||
/**
|
||||
* 私钥解密
|
||||
*
|
||||
* @param data 需要解密的数据
|
||||
* @param privateKey 私钥
|
||||
* @return 解密后的数据
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static String decryptByPrivateKeyByWx(String data, String privateKey) throws Exception {
|
||||
return decryptByPrivateKey(data, privateKey, "RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING");
|
||||
}
|
||||
|
||||
/**
|
||||
* 私钥解密
|
||||
*
|
||||
* @param data 需要解密的数据
|
||||
* @param privateKey 私钥
|
||||
* @param fillMode 填充模式
|
||||
* @return 解密后的数据
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static String decryptByPrivateKey(String data, String privateKey, String fillMode) throws Exception {
|
||||
byte[] encryptedData = Base64.decode(data);
|
||||
byte[] keyBytes = Base64.decode(privateKey);
|
||||
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
|
||||
Key key = keyFactory.generatePrivate(pkcs8KeySpec);
|
||||
Cipher cipher = Cipher.getInstance(fillMode);
|
||||
|
||||
cipher.init(Cipher.DECRYPT_MODE, key);
|
||||
int inputLen = encryptedData.length;
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
int offSet = 0;
|
||||
byte[] cache;
|
||||
int i = 0;
|
||||
//对数据分段解密
|
||||
while (inputLen - offSet > 0) {
|
||||
if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
|
||||
cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
|
||||
} else {
|
||||
cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
|
||||
}
|
||||
out.write(cache, 0, cache.length);
|
||||
i++;
|
||||
offSet = i * MAX_DECRYPT_BLOCK;
|
||||
}
|
||||
byte[] decryptedData = out.toByteArray();
|
||||
out.close();
|
||||
return new String(decryptedData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从字符串中加载公钥
|
||||
*
|
||||
* @param publicKeyStr 公钥数据字符串
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static PublicKey loadPublicKey(String publicKeyStr) throws Exception {
|
||||
try {
|
||||
byte[] buffer = Base64.decode(publicKeyStr);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
|
||||
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
|
||||
return keyFactory.generatePublic(keySpec);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new Exception("无此算法");
|
||||
} catch (InvalidKeySpecException e) {
|
||||
throw new Exception("公钥非法");
|
||||
} catch (NullPointerException e) {
|
||||
throw new Exception("公钥数据为空");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从字符串中加载私钥<br>
|
||||
* 加载时使用的是PKCS8EncodedKeySpec(PKCS#8编码的Key指令)。
|
||||
*
|
||||
* @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("私钥数据为空");
|
||||
}
|
||||
}
|
||||
|
||||
public static String getPrivateKeyStr(PrivateKey privateKey) {
|
||||
return Base64.encode(privateKey.getEncoded());
|
||||
}
|
||||
|
||||
public static String getPublicKeyStr(PublicKey publicKey) {
|
||||
return Base64.encode(publicKey.getEncoded());
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Map<String, String> keys = getKeys();
|
||||
String publicKey = keys.get("publicKey");
|
||||
String privateKey = keys.get("privateKey");
|
||||
String content = "我是,I am ";
|
||||
String encrypt = encryptByPublicKey(content, publicKey);
|
||||
String decrypt = decryptByPrivateKey(encrypt, privateKey);
|
||||
System.out.println("加密之后:" + encrypt);
|
||||
System.out.println("解密之后:" + decrypt);
|
||||
|
||||
System.out.println("======华丽的分割线=========");
|
||||
|
||||
content = "我是,I am ";
|
||||
encrypt = encryptByPublicKeyByWx(content, publicKey);
|
||||
decrypt = decryptByPrivateKeyByWx(encrypt, privateKey);
|
||||
System.out.println("加密之后:" + encrypt);
|
||||
System.out.println("解密之后:" + decrypt);
|
||||
|
||||
//OPPO
|
||||
String sign = encryptByPrivateKey(content, privateKey);
|
||||
System.out.println("加密之后:" + sign);
|
||||
System.out.println(checkByPublicKey(content, sign, publicKey));
|
||||
}
|
||||
}
|
@ -0,0 +1,718 @@
|
||||
package com.wzj.soopin.transaction.kit.core.kit;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.wzj.soopin.transaction.kit.core.PaymentHttpResponse;
|
||||
import com.wzj.soopin.transaction.kit.core.enums.RequestMethodEnums;
|
||||
import com.wzj.soopin.transaction.kit.core.enums.SignType;
|
||||
import org.dromara.common.core.constant.ResultCode;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* <p>微信支付工具类</p>
|
||||
*
|
||||
* @author
|
||||
*/
|
||||
public class WxPayKit {
|
||||
private static final String FIELD_SIGN = "sign";
|
||||
private static final String FIELD_SIGN_TYPE = "sign_type";
|
||||
|
||||
public static String hmacSha256(String data, String key) {
|
||||
return PayKit.hmacSha256(data, key);
|
||||
}
|
||||
|
||||
public static String md5(String data) {
|
||||
return PayKit.md5(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* AES 解密
|
||||
*
|
||||
* @param base64Data 需要解密的数据
|
||||
* @param key 密钥
|
||||
* @return 解密后的数据
|
||||
*/
|
||||
public static String decryptData(String base64Data, String key) {
|
||||
return PayKit.decryptData(base64Data, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* AES 加密
|
||||
*
|
||||
* @param data 需要加密的数据
|
||||
* @param key 密钥
|
||||
* @return 加密后的数据
|
||||
*/
|
||||
public static String encryptData(String data, String key) {
|
||||
return PayKit.encryptData(data, key);
|
||||
}
|
||||
|
||||
public static String generateStr() {
|
||||
return IdUtil.fastSimpleUUID();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 支付异步通知时校验 sign
|
||||
*
|
||||
* @param params 参数
|
||||
* @param partnerKey 支付密钥
|
||||
* @return {boolean}
|
||||
*/
|
||||
public static boolean verifyNotify(Map<String, String> params, String partnerKey) {
|
||||
String sign = params.get("sign");
|
||||
String localSign = createSign(params, partnerKey, SignType.MD5);
|
||||
return sign.equals(localSign);
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付异步通知时校验 sign
|
||||
*
|
||||
* @param params 参数
|
||||
* @param partnerKey 支付密钥
|
||||
* @param signType {@link SignType}
|
||||
* @return {@link Boolean} 验证签名结果
|
||||
*/
|
||||
public static boolean verifyNotify(Map<String, String> params, String partnerKey, SignType signType) {
|
||||
String sign = params.get("sign");
|
||||
String localSign = createSign(params, partnerKey, signType);
|
||||
return sign.equals(localSign);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成签名
|
||||
*
|
||||
* @param params 需要签名的参数
|
||||
* @param partnerKey 密钥
|
||||
* @param signType 签名类型
|
||||
* @return 签名后的数据
|
||||
*/
|
||||
public static String createSign(Map<String, String> params, String partnerKey, SignType signType) {
|
||||
if (signType == null) {
|
||||
signType = SignType.MD5;
|
||||
}
|
||||
//生成签名前先去除sign
|
||||
params.remove(FIELD_SIGN);
|
||||
String tempStr = PayKit.createLinkString(params);
|
||||
String stringSignTemp = tempStr + "&key=" + partnerKey;
|
||||
if (signType == SignType.MD5) {
|
||||
return md5(stringSignTemp).toUpperCase();
|
||||
} else {
|
||||
return hmacSha256(stringSignTemp, partnerKey).toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* APP 单独生成签名
|
||||
* app 支付环境中,如果遇到签名错误,百思不得其解,则可以使用这个方法调用签名尝试解决
|
||||
*
|
||||
* @param params 需要签名的参数
|
||||
* @return 签名后的数据
|
||||
*/
|
||||
public static String createAppSign(Map<String, String> params, String privateKey) {
|
||||
|
||||
String appid = params.get("appid");
|
||||
String timestamp = params.get("timestamp");
|
||||
String noncestr = params.get("noncestr");
|
||||
String prepayid = params.get("prepayid");
|
||||
|
||||
String encrypt = appid + "\n" + timestamp + "\n" + noncestr + "\n" + prepayid + "\n";
|
||||
|
||||
try {
|
||||
return PayKit.createSign(encrypt, privateKey);
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException(ResultCode.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成签名
|
||||
*
|
||||
* @param params 需要签名的参数
|
||||
* @param secret 企业微信支付应用secret
|
||||
* @return 签名后的数据
|
||||
*/
|
||||
public static String createSign(Map<String, String> params, String secret) {
|
||||
//生成签名前先去除sign
|
||||
params.remove(FIELD_SIGN);
|
||||
String tempStr = PayKit.createLinkString(params);
|
||||
String stringSignTemp = tempStr + "&secret=" + secret;
|
||||
return md5(stringSignTemp).toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建签名
|
||||
*
|
||||
* @param params 需要签名的参数
|
||||
* @param partnerKey 密钥
|
||||
* @param signType 签名类型
|
||||
* @return 签名后的 Map
|
||||
*/
|
||||
public static Map<String, String> buildSign(Map<String, String> params, String partnerKey, SignType signType) {
|
||||
return buildSign(params, partnerKey, signType, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建签名
|
||||
*
|
||||
* @param params 需要签名的参数
|
||||
* @param partnerKey 密钥
|
||||
* @param signType 签名类型
|
||||
* @param haveSignType 签名是否包含 sign_type 字段
|
||||
* @return 签名后的 Map
|
||||
*/
|
||||
public static Map<String, String> buildSign(Map<String, String> params, String partnerKey, SignType signType, boolean haveSignType) {
|
||||
if (haveSignType) {
|
||||
params.put(FIELD_SIGN_TYPE, signType.getType());
|
||||
}
|
||||
String sign = createSign(params, partnerKey, signType);
|
||||
params.put(FIELD_SIGN, sign);
|
||||
return params;
|
||||
}
|
||||
|
||||
public static StringBuffer forEachMap(Map<String, String> params, String prefix, String suffix) {
|
||||
return PayKit.forEachMap(params, prefix, suffix);
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信下单 map to xml
|
||||
*
|
||||
* @param params Map 参数
|
||||
* @return xml 字符串
|
||||
*/
|
||||
public static String toXml(Map<String, String> params) {
|
||||
return PayKit.toXml(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 针对支付的 xml,没有嵌套节点的简单处理
|
||||
*
|
||||
* @param xmlStr xml 字符串
|
||||
* @return 转化后的 Map
|
||||
*/
|
||||
public static Map<String, String> xmlToMap(String xmlStr) {
|
||||
return PayKit.xmlToMap(xmlStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>生成二维码链接</p>
|
||||
* <p>原生支付接口模式一(扫码模式一)</p>
|
||||
*
|
||||
* @param sign 签名
|
||||
* @param appId 公众账号ID
|
||||
* @param mchId 商户号
|
||||
* @param productId 商品ID
|
||||
* @param timeStamp 时间戳
|
||||
* @param nonceStr 随机字符串
|
||||
* @return {String}
|
||||
*/
|
||||
public static String bizPayUrl(String sign, String appId, String mchId, String productId, String timeStamp, String nonceStr) {
|
||||
String rules = "weixin://wxpay/bizpayurl?sign=Temp&appid=Temp&mch_id=Temp&product_id=Temp&time_stamp=Temp&nonce_str=Temp";
|
||||
return replace(rules, "Temp", sign, appId, mchId, productId, timeStamp, nonceStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>生成二维码链接</p>
|
||||
* <p>原生支付接口模式一(扫码模式一)</p>
|
||||
*
|
||||
* @param partnerKey 密钥
|
||||
* @param appId 公众账号ID
|
||||
* @param mchId 商户号
|
||||
* @param productId 商品ID
|
||||
* @param timeStamp 时间戳
|
||||
* @param nonceStr 随机字符串
|
||||
* @param signType 签名类型
|
||||
* @return {String}
|
||||
*/
|
||||
public static String bizPayUrl(String partnerKey, String appId, String mchId, String productId, String timeStamp, String nonceStr, SignType signType) {
|
||||
HashMap<String, String> map = new HashMap<>(5);
|
||||
map.put("appid", appId);
|
||||
map.put("mch_id", mchId);
|
||||
map.put("time_stamp", StrUtil.isEmpty(timeStamp) ? Long.toString(System.currentTimeMillis() / 1000) : timeStamp);
|
||||
map.put("nonce_str", StrUtil.isEmpty(nonceStr) ? IdUtil.fastSimpleUUID() : nonceStr);
|
||||
map.put("product_id", productId);
|
||||
return bizPayUrl(createSign(map, partnerKey, signType), appId, mchId, productId, timeStamp, nonceStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>生成二维码链接</p>
|
||||
* <p>原生支付接口模式一(扫码模式一)</p>
|
||||
*
|
||||
* @param partnerKey 密钥
|
||||
* @param appId 公众账号ID
|
||||
* @param mchId 商户号
|
||||
* @param productId 商品ID
|
||||
* @return {String}
|
||||
*/
|
||||
public static String bizPayUrl(String partnerKey, String appId, String mchId, String productId) {
|
||||
String timeStamp = Long.toString(System.currentTimeMillis() / 1000);
|
||||
String nonceStr = IdUtil.fastSimpleUUID();
|
||||
HashMap<String, String> map = new HashMap<>(5);
|
||||
map.put("appid", appId);
|
||||
map.put("mch_id", mchId);
|
||||
map.put("time_stamp", timeStamp);
|
||||
map.put("nonce_str", nonceStr);
|
||||
map.put("product_id", productId);
|
||||
return bizPayUrl(createSign(map, partnerKey, null), appId, mchId, productId, timeStamp, nonceStr);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 替换url中的参数
|
||||
*
|
||||
* @param str 原始字符串
|
||||
* @param regex 表达式
|
||||
* @param args 替换字符串
|
||||
* @return {String}
|
||||
*/
|
||||
public static String replace(String str, String regex, String... args) {
|
||||
for (String arg : args) {
|
||||
str = str.replaceFirst(regex, arg);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断接口返回的 code
|
||||
*
|
||||
* @param codeValue code 值
|
||||
* @return 是否是 SUCCESS
|
||||
*/
|
||||
public static boolean codeIsOk(String codeValue) {
|
||||
return StrUtil.isNotEmpty(codeValue) && "SUCCESS".equals(codeValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>公众号支付-预付订单再次签名</p>
|
||||
* <p>注意此处签名方式需与统一下单的签名类型一致</p>
|
||||
*
|
||||
* @param prepayId 预付订单号
|
||||
* @param appId 应用编号
|
||||
* @param partnerKey API Key
|
||||
* @param signType 签名方式
|
||||
* @return 再次签名后的 Map
|
||||
*/
|
||||
public static Map<String, String> prepayIdCreateSign(String prepayId, String appId, String partnerKey, SignType signType) {
|
||||
Map<String, String> packageParams = new HashMap<>(6);
|
||||
packageParams.put("appId", appId);
|
||||
packageParams.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
|
||||
packageParams.put("nonceStr", String.valueOf(System.currentTimeMillis()));
|
||||
packageParams.put("package", "prepay_id=" + prepayId);
|
||||
if (signType == null) {
|
||||
signType = SignType.MD5;
|
||||
}
|
||||
packageParams.put("signType", signType.getType());
|
||||
String packageSign = WxPayKit.createSign(packageParams, partnerKey, signType);
|
||||
packageParams.put("paySign", packageSign);
|
||||
return packageParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* JS 调起支付签名
|
||||
*
|
||||
* @param appId 应用编号
|
||||
* @param prepayId 预付订单号
|
||||
* @param keyPath key.pem 证书路径
|
||||
* @return 唤起支付需要的参数
|
||||
* @throws Exception 错误信息
|
||||
*/
|
||||
public static Map<String, String> jsApiCreateSign(String appId, String prepayId, String keyPath) throws Exception {
|
||||
String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
|
||||
String nonceStr = String.valueOf(System.currentTimeMillis());
|
||||
String packageStr = "prepay_id=" + prepayId;
|
||||
Map<String, String> packageParams = new HashMap<>(6);
|
||||
packageParams.put("appId", appId);
|
||||
packageParams.put("timeStamp", timeStamp);
|
||||
packageParams.put("nonceStr", nonceStr);
|
||||
packageParams.put("package", packageStr);
|
||||
packageParams.put("signType", SignType.RSA.toString());
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
list.add(appId);
|
||||
list.add(timeStamp);
|
||||
list.add(nonceStr);
|
||||
list.add(packageStr);
|
||||
String packageSign = PayKit.createSign(
|
||||
PayKit.buildSignMessage(list),
|
||||
keyPath
|
||||
);
|
||||
packageParams.put("paySign", packageSign);
|
||||
return packageParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>APP 支付-预付订单再次签名</p>
|
||||
* <p>注意此处签名方式需与统一下单的签名类型一致</p>
|
||||
*
|
||||
* @param appId 应用编号
|
||||
* @param partnerId 商户号
|
||||
* @param prepayId 预付订单号
|
||||
* @param partnerKey API Key
|
||||
* @param signType 签名方式
|
||||
* @return 再次签名后的 Map
|
||||
*/
|
||||
public static Map<String, String> appPrepayIdCreateSign(String appId, String partnerId, String prepayId, String partnerKey, SignType signType) {
|
||||
Map<String, String> packageParams = new HashMap<>(8);
|
||||
packageParams.put("appid", appId);
|
||||
packageParams.put("partnerid", partnerId);
|
||||
packageParams.put("prepayid", prepayId);
|
||||
packageParams.put("package", "Sign=WXPay");
|
||||
packageParams.put("noncestr", String.valueOf(System.currentTimeMillis()));
|
||||
packageParams.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
|
||||
if (signType == null) {
|
||||
signType = SignType.MD5;
|
||||
}
|
||||
// String packageSign = createSign(packageParams, partnerKey, signType);
|
||||
// 部分微信APP支付 提示签名错误 解开下方注释 替换上边的代码就好。
|
||||
String packageSign = createAppSign(packageParams, partnerKey);
|
||||
packageParams.put("sign", packageSign);
|
||||
return packageParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* App 调起支付签名
|
||||
*
|
||||
* @param appId 应用编号
|
||||
* @param partnerId 商户编号
|
||||
* @param prepayId 预付订单号
|
||||
* @param keyPath key.pem 证书路径
|
||||
* @return 唤起支付需要的参数
|
||||
* @throws Exception 错误信息
|
||||
*/
|
||||
public static Map<String, String> appCreateSign(String appId, String partnerId, String prepayId, String keyPath) throws Exception {
|
||||
String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
|
||||
String nonceStr = String.valueOf(System.currentTimeMillis());
|
||||
Map<String, String> packageParams = new HashMap<>(8);
|
||||
packageParams.put("appId", appId);
|
||||
packageParams.put("partnerid", partnerId);
|
||||
packageParams.put("prepayid", prepayId);
|
||||
packageParams.put("package", "Sign=WXPay");
|
||||
packageParams.put("timeStamp", timeStamp);
|
||||
packageParams.put("nonceStr", nonceStr);
|
||||
packageParams.put("signType", SignType.RSA.toString());
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
list.add(appId);
|
||||
list.add(timeStamp);
|
||||
list.add(nonceStr);
|
||||
list.add(prepayId);
|
||||
String packageSign = PayKit.createSign(
|
||||
PayKit.buildSignMessage(list),
|
||||
keyPath
|
||||
);
|
||||
packageParams.put("sign", packageSign);
|
||||
return packageParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>小程序-预付订单再次签名</p>
|
||||
* <p>注意此处签名方式需与统一下单的签名类型一致</p>
|
||||
*
|
||||
* @param appId 应用编号
|
||||
* @param prepayId 预付订单号
|
||||
* @param partnerKey API Key
|
||||
* @param signType 签名方式
|
||||
* @return 再次签名后的 Map
|
||||
*/
|
||||
public static Map<String, String> miniAppPrepayIdCreateSign(String appId, String prepayId, String partnerKey, SignType signType) {
|
||||
Map<String, String> packageParams = new HashMap<>(6);
|
||||
packageParams.put("appId", appId);
|
||||
packageParams.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
|
||||
packageParams.put("nonceStr", String.valueOf(System.currentTimeMillis()));
|
||||
packageParams.put("package", "prepay_id=" + prepayId);
|
||||
if (signType == null) {
|
||||
signType = SignType.MD5;
|
||||
}
|
||||
packageParams.put("signType", signType.getType());
|
||||
String packageSign = createSign(packageParams, partnerKey, signType);
|
||||
packageParams.put("paySign", packageSign);
|
||||
return packageParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建 v3 接口所需的 Authorization
|
||||
*
|
||||
* @param method {@link RequestMethodEnums} 请求方法
|
||||
* @param urlSuffix 可通过 WxApiType 来获取,URL挂载参数需要自行拼接
|
||||
* @param mchId 商户Id
|
||||
* @param serialNo 商户 API 证书序列号
|
||||
* @param keyPath key.pem 证书路径
|
||||
* @param body 接口请求参数
|
||||
* @param nonceStr 随机字符库
|
||||
* @param timestamp 时间戳
|
||||
* @param authType 认证类型
|
||||
* @return {@link String} 返回 v3 所需的 Authorization
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static String buildAuthorization(RequestMethodEnums method, String urlSuffix, String mchId,
|
||||
String serialNo, String keyPath, String body, String nonceStr,
|
||||
long timestamp, String authType) throws Exception {
|
||||
//构建签名参数
|
||||
String buildSignMessage = PayKit.buildSignMessage(method, urlSuffix, timestamp, nonceStr, body);
|
||||
String signature = PayKit.createSign(buildSignMessage, keyPath);
|
||||
//根据平台规则生成请求头 authorization
|
||||
return PayKit.getAuthorization(mchId, serialNo, nonceStr, String.valueOf(timestamp), signature, authType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建 v3 接口所需的 Authorization
|
||||
*
|
||||
* @param method {@link RequestMethodEnums} 请求方法
|
||||
* @param urlSuffix 可通过 WxApiType 来获取,URL挂载参数需要自行拼接
|
||||
* @param mchId 商户Id
|
||||
* @param serialNo 商户 API 证书序列号
|
||||
* @param privateKey 商户私钥
|
||||
* @param body 接口请求参数
|
||||
* @param nonceStr 随机字符库
|
||||
* @param timestamp 时间戳
|
||||
* @param authType 认证类型
|
||||
* @return {@link String} 返回 v3 所需的 Authorization
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static String buildAuthorization(RequestMethodEnums method, String urlSuffix, String mchId,
|
||||
String serialNo, PrivateKey privateKey, String body, String nonceStr,
|
||||
long timestamp, String authType) throws Exception {
|
||||
//构建签名参数
|
||||
String buildSignMessage = PayKit.buildSignMessage(method, urlSuffix, timestamp, nonceStr, body);
|
||||
String signature = PayKit.createSign(buildSignMessage, privateKey);
|
||||
//根据平台规则生成请求头 authorization
|
||||
return PayKit.getAuthorization(mchId, serialNo, nonceStr, String.valueOf(timestamp), signature, authType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建 v3 接口所需的 Authorization
|
||||
*
|
||||
* @param method {@link RequestMethodEnums} 请求方法
|
||||
* @param urlSuffix 可通过 WxApiType 来获取,URL挂载参数需要自行拼接
|
||||
* @param mchId 商户Id
|
||||
* @param serialNo 商户 API 证书序列号
|
||||
* @param keyPath key.pem 证书路径
|
||||
* @param body 接口请求参数
|
||||
* @return {@link String} 返回 v3 所需的 Authorization
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static String buildAuthorization(RequestMethodEnums method, String urlSuffix, String mchId,
|
||||
String serialNo, String keyPath, String body) throws Exception {
|
||||
|
||||
long timestamp = System.currentTimeMillis() / 1000;
|
||||
String authType = "WECHATPAY2-SHA256-RSA2048";
|
||||
String nonceStr = IdUtil.fastSimpleUUID();
|
||||
|
||||
return buildAuthorization(method, urlSuffix, mchId, serialNo, keyPath, body, nonceStr, timestamp, authType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建 v3 接口所需的 Authorization
|
||||
*
|
||||
* @param method {@link RequestMethodEnums} 请求方法
|
||||
* @param urlSuffix 可通过 WxApiType 来获取,URL挂载参数需要自行拼接
|
||||
* @param mchId 商户Id
|
||||
* @param serialNo 商户 API 证书序列号
|
||||
* @param privateKey key.pem 证书路径
|
||||
* @param body 接口请求参数
|
||||
* @return {@link String} 返回 v3 所需的 Authorization
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static String buildAuthorization(RequestMethodEnums method, String urlSuffix, String mchId,
|
||||
String serialNo, PrivateKey privateKey, String body) throws Exception {
|
||||
|
||||
long timestamp = System.currentTimeMillis() / 1000;
|
||||
String authType = "WECHATPAY2-SHA256-RSA2048";
|
||||
String nonceStr = IdUtil.fastSimpleUUID();
|
||||
|
||||
return buildAuthorization(method, urlSuffix, mchId, serialNo, privateKey, body, nonceStr, timestamp, authType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证签名
|
||||
*
|
||||
* @param response 接口请求返回的 {@link PaymentHttpResponse}
|
||||
* @param certPath 平台证书路径
|
||||
* @return 签名结果
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static boolean verifySignature(PaymentHttpResponse response, String certPath) throws Exception {
|
||||
String timestamp = response.getHeader("Wechatpay-Timestamp");
|
||||
String nonceStr = response.getHeader("Wechatpay-Nonce");
|
||||
String signature = response.getHeader("Wechatpay-Signature");
|
||||
String body = response.getBody();
|
||||
return verifySignature(signature, body, nonceStr, timestamp, FileUtil.getInputStream(certPath));
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证签名
|
||||
*
|
||||
* @param response 接口请求返回的 {@link PaymentHttpResponse}
|
||||
* @param cert 平台证书
|
||||
* @return 签名结果
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static boolean verifySignature(PaymentHttpResponse response, X509Certificate cert) throws Exception {
|
||||
String timestamp = response.getHeader("Wechatpay-Timestamp");
|
||||
String nonceStr = response.getHeader("Wechatpay-Nonce");
|
||||
String signature = response.getHeader("Wechatpay-Signature");
|
||||
String body = response.getBody();
|
||||
return verifySignature(signature, body, nonceStr, timestamp, cert);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证签名
|
||||
*
|
||||
* @param signature 待验证的签名
|
||||
* @param body 应答主体
|
||||
* @param nonce 随机串
|
||||
* @param timestamp 时间戳
|
||||
* @param publicKey 微信支付平台公钥
|
||||
* @return 签名结果
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static boolean verifySignature(String signature, String body, String nonce, String timestamp, String publicKey) throws Exception {
|
||||
String buildSignMessage = PayKit.buildSignMessage(timestamp, nonce, body);
|
||||
return RsaKit.checkByPublicKey(buildSignMessage, signature, publicKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证签名
|
||||
*
|
||||
* @param signature 待验证的签名
|
||||
* @param body 应答主体
|
||||
* @param nonce 随机串
|
||||
* @param timestamp 时间戳
|
||||
* @param publicKey {@link PublicKey} 微信支付平台公钥
|
||||
* @return 签名结果
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static boolean verifySignature(String signature, String body, String nonce, String timestamp, PublicKey publicKey) throws Exception {
|
||||
String buildSignMessage = PayKit.buildSignMessage(timestamp, nonce, body);
|
||||
return RsaKit.checkByPublicKey(buildSignMessage, signature, publicKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证签名
|
||||
*
|
||||
* @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 = PayKit.buildSignMessage(timestamp, nonce, body);
|
||||
//获取证书
|
||||
X509Certificate certificate = PayKit.getCertificate(certInputStream);
|
||||
PublicKey publicKey = certificate.getPublicKey();
|
||||
return RsaKit.checkByPublicKey(buildSignMessage, signature, publicKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证签名
|
||||
*
|
||||
* @param signature 待验证的签名
|
||||
* @param body 应答主体
|
||||
* @param nonce 随机串
|
||||
* @param timestamp 时间戳
|
||||
* @param certificate 微信支付平台证书
|
||||
* @return 签名结果
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static boolean verifySignature(String signature, String body, String nonce, String timestamp, X509Certificate certificate) throws Exception {
|
||||
String buildSignMessage = PayKit.buildSignMessage(timestamp, nonce, body);
|
||||
PublicKey publicKey = certificate.getPublicKey();
|
||||
return RsaKit.checkByPublicKey(buildSignMessage, signature, publicKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* v3 支付异步通知验证签名
|
||||
*
|
||||
* @param serialNo 证书序列号
|
||||
* @param body 异步通知密文
|
||||
* @param signature 签名
|
||||
* @param nonce 随机字符串
|
||||
* @param timestamp 时间戳
|
||||
* @param key api 密钥
|
||||
* @param certPath 平台证书路径
|
||||
* @return 异步通知明文
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static String verifyNotify(String serialNo, String body, String signature, String nonce,
|
||||
String timestamp, String key, String certPath) throws Exception {
|
||||
BufferedInputStream inputStream = FileUtil.getInputStream(certPath);
|
||||
//获取平台证书序列号
|
||||
X509Certificate certificate = PayKit.getCertificate(inputStream);
|
||||
String serialNumber = certificate.getSerialNumber().toString(16).toUpperCase();
|
||||
System.out.println(serialNumber);
|
||||
//验证证书序列号
|
||||
if (serialNumber.equals(serialNo)) {
|
||||
boolean verifySignature = WxPayKit.verifySignature(signature, body, nonce, timestamp, certificate.getPublicKey());
|
||||
if (verifySignature) {
|
||||
JSONObject resultObject = JSONUtil.parseObj(body);
|
||||
JSONObject resource = resultObject.getJSONObject("resource");
|
||||
String cipherText = resource.getStr("ciphertext");
|
||||
String nonceStr = resource.getStr("nonce");
|
||||
String associatedData = resource.getStr("associated_data");
|
||||
|
||||
AesUtil aesUtil = new AesUtil(key.getBytes(StandardCharsets.UTF_8));
|
||||
//密文解密
|
||||
return aesUtil.decryptToString(
|
||||
associatedData.getBytes(StandardCharsets.UTF_8),
|
||||
nonceStr.getBytes(StandardCharsets.UTF_8),
|
||||
cipherText
|
||||
);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* v3 支付异步通知验证签名
|
||||
*
|
||||
* @param serialNo 证书序列号
|
||||
* @param body 异步通知密文
|
||||
* @param signature 签名
|
||||
* @param nonce 随机字符串
|
||||
* @param timestamp 时间戳
|
||||
* @param key api 密钥
|
||||
* @return 异步通知明文
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static String verifyNotify(String serialNo, String body, String signature, String nonce,
|
||||
String timestamp, String key, X509Certificate certificate) throws Exception {
|
||||
String serialNumber = certificate.getSerialNumber().toString(16).toUpperCase();
|
||||
//验证证书序列号
|
||||
if (serialNumber.equals(serialNo)) {
|
||||
boolean verifySignature = WxPayKit.verifySignature(signature, body, nonce, timestamp, certificate.getPublicKey());
|
||||
if (verifySignature) {
|
||||
JSONObject resultObject = JSONUtil.parseObj(body);
|
||||
JSONObject resource = resultObject.getJSONObject("resource");
|
||||
String cipherText = resource.getStr("ciphertext");
|
||||
String nonceStr = resource.getStr("nonce");
|
||||
String associatedData = resource.getStr("associated_data");
|
||||
|
||||
AesUtil aesUtil = new AesUtil(key.getBytes(StandardCharsets.UTF_8));
|
||||
//密文解密
|
||||
return aesUtil.decryptToString(
|
||||
associatedData.getBytes(StandardCharsets.UTF_8),
|
||||
nonceStr.getBytes(StandardCharsets.UTF_8),
|
||||
cipherText
|
||||
);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package com.wzj.soopin.transaction.kit.core.utils;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
/**
|
||||
* 时间工具类
|
||||
* 依赖 xk-time
|
||||
*
|
||||
* @author YunGouOS
|
||||
*/
|
||||
public class DateTimeZoneUtil implements Serializable {
|
||||
|
||||
// private static final long serialVersionUID = -1331008203306650395L;
|
||||
|
||||
/**
|
||||
* 时间转 TimeZone
|
||||
* <p>
|
||||
* 2020-08-17T16:46:37+08:00
|
||||
*
|
||||
* @param time 时间戳
|
||||
* @return {@link String} TimeZone 格式时间字符串
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static String dateToTimeZone(long time) throws Exception {
|
||||
return dateToTimeZone(new Date(time));
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间转 TimeZone
|
||||
* <p>
|
||||
* 2020-08-17T16:46:37+08:00
|
||||
*
|
||||
* @param date {@link Date}
|
||||
* @return {@link String} TimeZone 格式时间字符串
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static String dateToTimeZone(Date date) throws Exception {
|
||||
String time = "";
|
||||
if (date == null) {
|
||||
throw new Exception("date is not null");
|
||||
}
|
||||
// ZonedDateTime zonedDateTime = DateTimeConverterUtil.toZonedDateTime(date);
|
||||
// time = DateTimeFormatterUtil.format(zonedDateTime, DateTimeFormatterUtil.YYYY_MM_DD_T_HH_MM_SS_XXX_FMT);
|
||||
return time;
|
||||
}
|
||||
|
||||
/**
|
||||
* TimeZone 时间转标准时间
|
||||
* <p>
|
||||
* 2020-08-17T16:46:37+08:00 to 2020-08-17 16:46:37
|
||||
*
|
||||
* @param str TimeZone格式时间字符串
|
||||
* @return {@link String} 标准时间字符串
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static String timeZoneDateToStr(String str) throws Exception {
|
||||
String time=null;
|
||||
if (StrUtil.isBlank(str)) {
|
||||
throw new Exception("str is not null");
|
||||
}
|
||||
// ZonedDateTime zonedDateTime = DateTimeFormatterUtil.parseToZonedDateTime(str, DateTimeFormatterUtil.YYYY_MM_DD_T_HH_MM_SS_XXX_FMT);
|
||||
// if (zonedDateTime == null) {
|
||||
// throw new Exception("str to zonedDateTime fail");
|
||||
// }
|
||||
// time = zonedDateTime.format(DateTimeFormatterUtil.YYYY_MM_DD_HH_MM_SS_FMT);
|
||||
return time;
|
||||
}
|
||||
|
||||
//
|
||||
// public static void main(String[] args) throws Exception {
|
||||
// String timeZone = dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
|
||||
// String timeZone2 = dateToTimeZone(new Date());
|
||||
// System.out.println(timeZone + " " + timeZone2);
|
||||
// String date = timeZoneDateToStr(timeZone);
|
||||
// System.out.println(date);
|
||||
// }
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package com.wzj.soopin.transaction.kit.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
|
||||
|
||||
/**
|
||||
* 支付参数
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020/12/19 11:46
|
||||
*/
|
||||
@Data
|
||||
@ToString
|
||||
public class PayParam {
|
||||
|
||||
|
||||
@NotNull
|
||||
@ApiModelProperty(value = "交易类型", allowableValues = "TRADE,ORDER,RECHARGE")
|
||||
private String orderType;
|
||||
|
||||
@NotNull
|
||||
@ApiModelProperty(value = "订单号")
|
||||
private String sn;
|
||||
|
||||
@NotNull
|
||||
@ApiModelProperty(value = "客户端类型")
|
||||
private String clientType;
|
||||
|
||||
private String paymentMethod;
|
||||
|
||||
private String paymentClient;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package com.wzj.soopin.transaction.kit.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* PaymentSuccessParams
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v1.0
|
||||
* 2021-04-27 16:24
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class PaymentSuccessParams {
|
||||
|
||||
/**
|
||||
* 支付方式
|
||||
*/
|
||||
private String paymentMethod;
|
||||
/**
|
||||
* 第三方流水
|
||||
*/
|
||||
private String receivableNo;
|
||||
|
||||
/**
|
||||
* 支付金额
|
||||
*/
|
||||
private Double price;
|
||||
|
||||
/**
|
||||
* 支付参数
|
||||
*/
|
||||
private PayParam payParam;
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package com.wzj.soopin.transaction.kit.params;
|
||||
|
||||
|
||||
import com.wzj.soopin.transaction.enums.CashierEnum;
|
||||
import com.wzj.soopin.transaction.kit.dto.PayParam;
|
||||
import com.wzj.soopin.transaction.kit.dto.PaymentSuccessParams;
|
||||
import com.wzj.soopin.transaction.kit.params.dto.CashierParam;
|
||||
|
||||
/**
|
||||
* 收银台接口
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2021-01-25 19:08
|
||||
*/
|
||||
public interface CashierExecute {
|
||||
|
||||
/**
|
||||
* 获取支付参数
|
||||
*
|
||||
* @param payParam 收银台支付参数
|
||||
* @return 收银台所需支付参数
|
||||
*/
|
||||
CashierParam getPaymentParams(PayParam payParam);
|
||||
|
||||
/**
|
||||
* 支付成功
|
||||
*
|
||||
* @param paymentSuccessParams 支付回调
|
||||
*/
|
||||
void paymentSuccess(PaymentSuccessParams paymentSuccessParams);
|
||||
|
||||
/**
|
||||
* 支付结果查询
|
||||
*
|
||||
* @param payParam
|
||||
* @return
|
||||
*/
|
||||
Boolean paymentResult(PayParam payParam);
|
||||
|
||||
/**
|
||||
* 服务的枚举类型
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
CashierEnum cashierEnum();
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package com.wzj.soopin.transaction.kit.params.dto;
|
||||
|
||||
import com.wzj.soopin.order.utils.StringUtils;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 支付参数
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2021-01-25 19:09
|
||||
*/
|
||||
@Data
|
||||
@ToString
|
||||
public class CashierParam {
|
||||
|
||||
@ApiModelProperty(value = "价格")
|
||||
private BigDecimal price;
|
||||
|
||||
@ApiModelProperty(value = "支付title")
|
||||
private String title;
|
||||
|
||||
@ApiModelProperty(value = "支付详细描述")
|
||||
private String detail;
|
||||
|
||||
@ApiModelProperty(value = "订单sn集合")
|
||||
private String orderSns;
|
||||
|
||||
@ApiModelProperty(value = "支持支付方式")
|
||||
private List<String> support;
|
||||
|
||||
|
||||
@ApiModelProperty(value = "订单创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@ApiModelProperty(value = "支付自动结束时间")
|
||||
private Long autoCancel;
|
||||
|
||||
@ApiModelProperty(value = "剩余余额")
|
||||
private Double walletValue;
|
||||
|
||||
public String getDetail() {
|
||||
if (StringUtils.isEmpty(detail)) {
|
||||
return "清单详细";
|
||||
}
|
||||
return StringUtils.filterSpecialChart(detail);
|
||||
}
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
package com.wzj.soopin.transaction.kit.params.impl;
|
||||
|
||||
import com.wzj.soopin.order.domain.entity.Order;
|
||||
import com.wzj.soopin.order.domain.entity.OrderItem;
|
||||
import com.wzj.soopin.order.emum.OrderStatusEnum;
|
||||
import com.wzj.soopin.order.mapper.OrderMapper;
|
||||
import com.wzj.soopin.order.service.OrderItemService;
|
||||
import com.wzj.soopin.order.service.OrderService;
|
||||
import com.wzj.soopin.transaction.enums.CashierEnum;
|
||||
import com.wzj.soopin.transaction.kit.dto.PayParam;
|
||||
import com.wzj.soopin.transaction.kit.dto.PaymentSuccessParams;
|
||||
import com.wzj.soopin.transaction.kit.params.CashierExecute;
|
||||
import com.wzj.soopin.transaction.kit.params.dto.CashierParam;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.constant.ResultCode;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.system.service.ISysConfigService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 订单支付信息获取
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2021-01-25 20:00
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class OrderCashier implements CashierExecute {
|
||||
/**
|
||||
* 订单
|
||||
*/
|
||||
private final OrderService orderService;
|
||||
|
||||
private final OrderItemService orderItemService;
|
||||
/**
|
||||
* 设置
|
||||
*/
|
||||
private final ISysConfigService settingService;
|
||||
|
||||
@Override
|
||||
public CashierEnum cashierEnum() {
|
||||
return CashierEnum.ORDER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CashierParam getPaymentParams(PayParam payParam) {
|
||||
if (payParam.getOrderType().equals(CashierEnum.ORDER.name())) {
|
||||
//准备返回的数据
|
||||
CashierParam cashierParam = new CashierParam();
|
||||
//订单信息获取
|
||||
Order order = orderService.getByNo(payParam.getSn());
|
||||
|
||||
// //如果订单已支付,则不能发器支付
|
||||
if (order.getStatus().equals(OrderStatusEnum.WAITING_FOR_DELIVERY.name())) {
|
||||
throw new ServiceException(ResultCode.PAY_DOUBLE_ERROR);
|
||||
}
|
||||
//如果订单状态不是待付款,则抛出异常
|
||||
if (!order.getStatus().equals(OrderStatusEnum.UNPAID.getValue())) {
|
||||
throw new ServiceException(ResultCode.PAY_BAN);
|
||||
}
|
||||
cashierParam.setPrice(order.getPayAmount());
|
||||
//
|
||||
// try {
|
||||
// BaseSetting baseSetting = JSONUtil.toBean(settingService.get(SettingEnum.BASE_SETTING.name()).getSettingValue(), BaseSetting.class);
|
||||
// cashierParam.setTitle(baseSetting.getSiteName());
|
||||
// } catch (Exception e) {
|
||||
// cashierParam.setTitle("多用户商城,在线支付");
|
||||
// }
|
||||
//
|
||||
//
|
||||
List<OrderItem> orderItemList = orderItemService.findByOrderId(order.getId());
|
||||
StringBuilder subject = new StringBuilder();
|
||||
for (OrderItem orderItem : orderItemList) {
|
||||
subject.append(orderItem.getProductName()).append(";");
|
||||
}
|
||||
|
||||
cashierParam.setDetail(subject.toString());
|
||||
|
||||
cashierParam.setOrderSns(payParam.getSn());
|
||||
cashierParam.setCreateTime(order.getCreateTime());
|
||||
return cashierParam;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paymentSuccess(PaymentSuccessParams paymentSuccessParams) {
|
||||
|
||||
PayParam payParam = paymentSuccessParams.getPayParam();
|
||||
if (payParam.getOrderType().equals(CashierEnum.ORDER.name())) {
|
||||
// orderService.payOrder(payParam.getSn(),
|
||||
// paymentSuccessParams.getPaymentMethod(),
|
||||
// paymentSuccessParams.getReceivableNo());
|
||||
log.info("订单{}支付成功,金额{},方式{}", payParam.getSn(),
|
||||
paymentSuccessParams.getPaymentMethod(),
|
||||
paymentSuccessParams.getReceivableNo());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean paymentResult(PayParam payParam) {
|
||||
if (payParam.getOrderType().equals(CashierEnum.ORDER.name())) {
|
||||
// Order order = orderService.getBySn(payParam.getSn());
|
||||
// if (order != null) {
|
||||
// return PayStatusEnum.PAID.name().equals(order.getPayStatus());
|
||||
// } else {
|
||||
// throw new ServiceException(ResultCode.PAY_NOT_EXIST_ORDER);
|
||||
// }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
package com.wzj.soopin.transaction.kit.params.impl;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
|
||||
import com.wzj.soopin.transaction.enums.CashierEnum;
|
||||
import com.wzj.soopin.transaction.kit.dto.PayParam;
|
||||
import com.wzj.soopin.transaction.kit.dto.PaymentSuccessParams;
|
||||
import com.wzj.soopin.transaction.kit.params.CashierExecute;
|
||||
import com.wzj.soopin.transaction.kit.params.dto.CashierParam;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.constant.ResultCode;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 充值信息获取
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2021-01-25 20:00
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class RechargeCashier implements CashierExecute {
|
||||
/**
|
||||
* 余额
|
||||
*/
|
||||
// @Autowired
|
||||
// private RechargeService rechargeService;
|
||||
// /**
|
||||
// * 设置
|
||||
// */
|
||||
// @Autowired
|
||||
// private SettingService settingService;
|
||||
|
||||
|
||||
@Override
|
||||
public CashierEnum cashierEnum() {
|
||||
return CashierEnum.RECHARGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paymentSuccess(PaymentSuccessParams paymentSuccessParams) {
|
||||
PayParam payParam = paymentSuccessParams.getPayParam();
|
||||
if (payParam.getOrderType().equals(CashierEnum.RECHARGE.name())) {
|
||||
// rechargeService.paySuccess(payParam.getSn(), paymentSuccessParams.getReceivableNo(),paymentSuccessParams.getPaymentMethod());
|
||||
log.info("会员充值-订单号{},第三方流水:{}", payParam.getSn(), paymentSuccessParams.getReceivableNo());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public CashierParam getPaymentParams(PayParam payParam) {
|
||||
if (payParam.getOrderType().equals(CashierEnum.RECHARGE.name())) {
|
||||
//准备返回的数据
|
||||
CashierParam cashierParam = new CashierParam();
|
||||
//订单信息获取
|
||||
// Recharge recharge = rechargeService.getRecharge(payParam.getSn());
|
||||
//
|
||||
// //如果订单已支付,则不能发器支付
|
||||
// if (recharge.getPayStatus().equals(PayStatusEnum.PAID.name())) {
|
||||
// throw new ServiceException(ResultCode.PAY_DOUBLE_ERROR);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// cashierParam.setPrice(recharge.getRechargeMoney());
|
||||
//
|
||||
// try {
|
||||
// BaseSetting baseSetting = JSONUtil.toBean(settingService.get(SettingEnum.BASE_SETTING.name()).getSettingValue(), BaseSetting.class);
|
||||
// cashierParam.setTitle(baseSetting.getSiteName());
|
||||
// } catch (Exception e) {
|
||||
// cashierParam.setTitle("多用户商城,在线充值");
|
||||
// }
|
||||
// cashierParam.setDetail("余额充值");
|
||||
// cashierParam.setCreateTime(recharge.getCreateTime());
|
||||
// return cashierParam;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean paymentResult(PayParam payParam) {
|
||||
if (payParam.getOrderType().equals(CashierEnum.RECHARGE.name())) {
|
||||
// Recharge recharge = rechargeService.getRecharge(payParam.getSn());
|
||||
// if (recharge != null) {
|
||||
// return recharge.getPayStatus().equals(PayStatusEnum.PAID.name());
|
||||
// } else {
|
||||
// throw new ServiceException(ResultCode.PAY_NOT_EXIST_ORDER);
|
||||
// }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
package com.wzj.soopin.transaction.kit.params.impl;
|
||||
|
||||
import com.wzj.soopin.order.service.OrderService;
|
||||
import com.wzj.soopin.transaction.enums.CashierEnum;
|
||||
import com.wzj.soopin.transaction.kit.dto.PayParam;
|
||||
import com.wzj.soopin.transaction.kit.dto.PaymentSuccessParams;
|
||||
import com.wzj.soopin.transaction.kit.params.CashierExecute;
|
||||
import com.wzj.soopin.transaction.kit.params.dto.CashierParam;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.system.service.ISysConfigService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 整笔交易信息获取
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2021-01-25 20:00
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class TradeCashier implements CashierExecute {
|
||||
|
||||
/**
|
||||
* 交易
|
||||
*/
|
||||
// @Autowired
|
||||
// private TradeService tradeService;
|
||||
/**
|
||||
* 订单
|
||||
*/
|
||||
@Autowired
|
||||
private OrderService orderService;
|
||||
/**
|
||||
* 设置
|
||||
*/
|
||||
@Autowired
|
||||
private ISysConfigService sysConfigService;
|
||||
|
||||
|
||||
@Override
|
||||
public CashierEnum cashierEnum() {
|
||||
return CashierEnum.TRADE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CashierParam getPaymentParams(PayParam payParam) {
|
||||
if (payParam.getOrderType().equals(CashierEnum.TRADE.name())) {
|
||||
//准备返回的数据
|
||||
// CashierParam cashierParam = new CashierParam();
|
||||
// //订单信息获取
|
||||
// Trade trade = tradeService.getBySn(payParam.getSn());
|
||||
//
|
||||
// List<Order> orders = orderService.getByTradeSn(payParam.getSn());
|
||||
//
|
||||
//
|
||||
// String orderSns = orders.stream().map(Order::getSn).collect(Collectors.joining(", "));
|
||||
// cashierParam.setOrderSns(orderSns);
|
||||
//
|
||||
// for (Order order : orders) {
|
||||
// //如果订单已支付,则不能发器支付
|
||||
// if (order.getPayStatus().equals(PayStatusEnum.PAID.name())) {
|
||||
// throw new ServiceException(ResultCode.PAY_PARTIAL_ERROR);
|
||||
// }
|
||||
// //如果订单状态不是待付款,则抛出异常
|
||||
// if (!order.getOrderStatus().equals(OrderStatusEnum.UNPAID.name())) {
|
||||
// throw new ServiceException(ResultCode.PAY_BAN);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// cashierParam.setPrice(trade.getFlowPrice());
|
||||
//
|
||||
// try {
|
||||
//// BaseSetting baseSetting = JSONUtil.toBean(settingService.get(SettingEnum.BASE_SETTING.name()).getSettingValue(), BaseSetting.class);
|
||||
//// cashierParam.setTitle(baseSetting.getSiteName());
|
||||
// } catch (Exception e) {
|
||||
// cashierParam.setTitle("多用户商城,在线支付");
|
||||
// }
|
||||
// String subject = "在线支付";
|
||||
// cashierParam.setDetail(subject);
|
||||
//
|
||||
// cashierParam.setCreateTime(trade.getCreateTime());
|
||||
// return cashierParam;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void paymentSuccess(PaymentSuccessParams paymentSuccessParams) {
|
||||
if (paymentSuccessParams.getPayParam().getOrderType().equals(CashierEnum.TRADE.name())) {
|
||||
// tradeService.payTrade(paymentSuccessParams.getPayParam().getSn(),
|
||||
// paymentSuccessParams.getPaymentMethod(),
|
||||
// paymentSuccessParams.getReceivableNo());
|
||||
// log.info("交易{}支付成功,方式{},流水号{},", paymentSuccessParams.getPayParam().getSn(),
|
||||
// paymentSuccessParams.getPaymentMethod(),
|
||||
// paymentSuccessParams.getReceivableNo());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean paymentResult(PayParam payParam) {
|
||||
|
||||
// if (payParam.getOrderType().equals(CashierEnum.TRADE.name())) {
|
||||
// Trade trade = tradeService.getBySn(payParam.getSn());
|
||||
// if (trade != null) {
|
||||
// return PayStatusEnum.PAID.name().equals(trade.getPayStatus());
|
||||
// } else {
|
||||
// throw new ServiceException(ResultCode.PAY_NOT_EXIST_ORDER);
|
||||
// }
|
||||
// }
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,681 @@
|
||||
package com.wzj.soopin.transaction.kit.plugin.easypay;
|
||||
|
||||
import cn.hutool.core.net.URLEncoder;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.wzj.soopin.transaction.domain.po.RefundLog;
|
||||
import com.wzj.soopin.transaction.enums.PaymentMethodEnum;
|
||||
import com.wzj.soopin.transaction.kit.CashierSupport;
|
||||
import com.wzj.soopin.transaction.kit.Payment;
|
||||
import com.wzj.soopin.transaction.kit.core.PaymentHttpResponse;
|
||||
import com.wzj.soopin.transaction.kit.core.kit.HttpKit;
|
||||
import com.wzj.soopin.transaction.kit.core.kit.WxPayKit;
|
||||
import com.wzj.soopin.transaction.kit.dto.PayParam;
|
||||
import com.wzj.soopin.transaction.kit.params.dto.CashierParam;
|
||||
import com.wzj.soopin.transaction.kit.plugin.wechat.model.Amount;
|
||||
import com.wzj.soopin.transaction.kit.plugin.wechat.model.UnifiedOrderModel;
|
||||
import com.wzj.soopin.transaction.util.SnowFlake;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.constant.ResultCode;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.redis.redis.RedisCache;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
/**
|
||||
* 微信支付
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020/12/21 17:44
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class EasyPayPlugin implements Payment {
|
||||
|
||||
/**
|
||||
* 收银台
|
||||
*/
|
||||
@Autowired
|
||||
|
||||
private CashierSupport cashierSupport;
|
||||
// /**
|
||||
// * 支付日志
|
||||
// */
|
||||
// @Autowired
|
||||
// private PaymentService paymentService;
|
||||
/**
|
||||
* 缓存
|
||||
*/
|
||||
@Autowired
|
||||
private RedisCache cache;
|
||||
// /**
|
||||
// * 退款日志
|
||||
// */
|
||||
// @Autowired
|
||||
// te RefundLogService refundLogService;
|
||||
/**
|
||||
* API域名
|
||||
*/
|
||||
// @Autowired
|
||||
// private ApiProperties apiProperties;
|
||||
// /**
|
||||
// * 配置
|
||||
// */
|
||||
// @Autowired
|
||||
// private SettingService settingService;
|
||||
// /**
|
||||
// * 联合登陆
|
||||
// */
|
||||
// @Autowired
|
||||
// private ConnectService connectService;
|
||||
// /**
|
||||
// * 联合登陆
|
||||
// */
|
||||
// @Autowired
|
||||
// private OrderService orderService;
|
||||
|
||||
|
||||
@Override
|
||||
public R<Object> h5pay(HttpServletRequest request, HttpServletResponse response1, PayParam payParam) {
|
||||
|
||||
try {
|
||||
// CashierParam cashierParam = cashierSupport.cashierParam(payParam);
|
||||
//
|
||||
// //支付参数准备
|
||||
// SceneInfo sceneInfo = new SceneInfo();
|
||||
// sceneInfo.setPayer_client_ip(IpKit.getRealIp(request));
|
||||
// H5Info h5Info = new H5Info();
|
||||
// h5Info.setType("WAP");
|
||||
// sceneInfo.setH5_info(h5Info);
|
||||
//
|
||||
// //支付金额
|
||||
// Integer fen = CurrencyUtil.fen(cashierParam.getPrice());
|
||||
// //第三方付款订单
|
||||
// String outOrderNo = SnowFlake.getIdStr();
|
||||
// //过期时间
|
||||
// String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
|
||||
//
|
||||
// //回传数据
|
||||
// String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8);
|
||||
//
|
||||
//
|
||||
// WechatPaymentSetting setting = wechatPaymentSetting();
|
||||
// String appid = setting.getServiceAppId();
|
||||
// if (appid == null) {
|
||||
// throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
|
||||
// }
|
||||
// UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
|
||||
// .setAppid(appid)
|
||||
// .setMchid(setting.getMchId())
|
||||
// .setDescription(cashierParam.getDetail())
|
||||
// .setOut_trade_no(outOrderNo)
|
||||
// .setTime_expire(timeExpire)
|
||||
// .setAttach(attach)
|
||||
// .setNotify_url(notifyUrl(apiProperties.getBuyer(), PaymentMethodEnum.WECHAT))
|
||||
// .setAmount(new Amount().setTotal(fen)).setScene_info(sceneInfo);
|
||||
//
|
||||
// log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
|
||||
// PaymentHttpResponse response = WechatApi.v3(
|
||||
// RequestMethodEnums.POST,
|
||||
// WechatDomain.CHINA.toString(),
|
||||
// WechatApiEnum.H5_PAY.toString(),
|
||||
// setting.getMchId(),
|
||||
// setting.getSerialNumber(),
|
||||
// null,
|
||||
// setting.getApiclient_key(),
|
||||
// JSONUtil.toJsonStr(unifiedOrderModel)
|
||||
// );
|
||||
|
||||
return null;
|
||||
// return R.ok(JSONUtil.toJsonStr(response.getBody()));
|
||||
} catch (Exception e) {
|
||||
log.error("微信H5支付错误", e);
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<Object> jsApiPay(HttpServletRequest request, PayParam payParam) {
|
||||
|
||||
try {
|
||||
// Connect connect = connectService.queryConnect(
|
||||
// ConnectQueryDTO.builder().userId(UserContext.getCurrentUser().getId()).unionType(ConnectEnum.WECHAT.name()).build()
|
||||
// );
|
||||
// if (connect == null) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// Payer payer = new Payer();
|
||||
// payer.setOpenid(connect.getUnionId());
|
||||
//
|
||||
// CashierParam cashierParam = cashierSupport.cashierParam(payParam);
|
||||
//
|
||||
// //支付金额
|
||||
// Integer fen = CurrencyUtil.fen(cashierParam.getPrice());
|
||||
// //第三方付款订单
|
||||
// String outOrderNo = SnowFlake.getIdStr();
|
||||
// //过期时间
|
||||
// String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
|
||||
//
|
||||
// String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8);
|
||||
//
|
||||
// WechatPaymentSetting setting = wechatPaymentSetting();
|
||||
// String appid = setting.getServiceAppId();
|
||||
// if (appid == null) {
|
||||
// throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
|
||||
// }
|
||||
// UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
|
||||
// .setAppid(appid)
|
||||
// .setMchid(setting.getMchId())
|
||||
// .setDescription(cashierParam.getDetail())
|
||||
// .setOut_trade_no(outOrderNo)
|
||||
// .setTime_expire(timeExpire)
|
||||
// .setAttach(attach)
|
||||
// .setNotify_url(notifyUrl(apiProperties.getBuyer(), PaymentMethodEnum.WECHAT))
|
||||
// .setAmount(new Amount().setTotal(fen))
|
||||
// .setPayer(payer);
|
||||
//
|
||||
// log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
|
||||
// PaymentHttpResponse response = WechatApi.v3(
|
||||
// RequestMethodEnums.POST,
|
||||
// WechatDomain.CHINA.toString(),
|
||||
// WechatApiEnum.JS_API_PAY.toString(),
|
||||
// setting.getMchId(),
|
||||
// setting.getSerialNumber(),
|
||||
// null,
|
||||
// setting.getApiclient_key(),
|
||||
// JSONUtil.toJsonStr(unifiedOrderModel)
|
||||
// );
|
||||
// //根据证书序列号查询对应的证书来验证签名结果
|
||||
// boolean verifySignature = WxPayKit.verifySignature(response, getPlatformCert());
|
||||
// log.info("verifySignature: {}", verifySignature);
|
||||
// log.info("统一下单响应 {}", response);
|
||||
//
|
||||
// if (verifySignature) {
|
||||
// String body = response.getBody();
|
||||
// JSONObject jsonObject = JSONUtil.parseObj(body);
|
||||
// String prepayId = jsonObject.getStr("prepay_id");
|
||||
// Map<String, String> map = WxPayKit.jsApiCreateSign(appid, prepayId, setting.getApiclient_key());
|
||||
// log.info("唤起支付参数:{}", map);
|
||||
//
|
||||
// return ResultUtil.data(map);
|
||||
// }
|
||||
log.error("微信支付参数验证错误,请及时处理");
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
} catch (Exception e) {
|
||||
log.error("支付异常", e);
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<Object> appPay(HttpServletRequest request, PayParam payParam) {
|
||||
|
||||
try {
|
||||
|
||||
CashierParam cashierParam = cashierSupport.cashierParam(payParam);
|
||||
|
||||
//支付金额
|
||||
// Integer fen = CurrencyUtil.fen(cashierParam.getPrice());
|
||||
// //第三方付款订单
|
||||
// String outOrderNo = SnowFlake.getIdStr();
|
||||
// //过期时间
|
||||
// String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
|
||||
//
|
||||
// String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8);
|
||||
//
|
||||
// WechatPaymentSetting setting = wechatPaymentSetting();
|
||||
// String appid = setting.getAppId();
|
||||
// if (appid == null) {
|
||||
// throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
|
||||
// }
|
||||
// UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
|
||||
// .setAppid(appid)
|
||||
// .setMchid(setting.getMchId())
|
||||
// .setDescription(cashierParam.getDetail())
|
||||
// .setOut_trade_no(outOrderNo)
|
||||
// .setTime_expire(timeExpire)
|
||||
// .setAttach(attach)
|
||||
// .setNotify_url(notifyUrl(apiProperties.getBuyer(), PaymentMethodEnum.WECHAT))
|
||||
// .setAmount(new Amount().setTotal(fen));
|
||||
//
|
||||
//
|
||||
// log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
|
||||
// PaymentHttpResponse response = WechatApi.v3(
|
||||
// RequestMethodEnums.POST,
|
||||
// WechatDomain.CHINA.toString(),
|
||||
// WechatApiEnum.APP_PAY.toString(),
|
||||
// setting.getMchId(),
|
||||
// setting.getSerialNumber(),
|
||||
// null,
|
||||
// setting.getApiclient_key(),
|
||||
// JSONUtil.toJsonStr(unifiedOrderModel)
|
||||
// );
|
||||
// //根据证书序列号查询对应的证书来验证签名结果
|
||||
// boolean verifySignature = WxPayKit.verifySignature(response, getPlatformCert());
|
||||
// log.info("verifySignature: {}", verifySignature);
|
||||
// log.info("统一下单响应 {}", response);
|
||||
//
|
||||
// if (verifySignature) {
|
||||
// JSONObject jsonObject = JSONUtil.parseObj(response.getBody());
|
||||
// String prepayId = jsonObject.getStr("prepay_id");
|
||||
// Map<String, String> map = WxPayKit.appPrepayIdCreateSign(appid,
|
||||
// setting.getMchId(),
|
||||
// prepayId,
|
||||
// setting.getApiclient_key(), SignType.HMACSHA256);
|
||||
// log.info("唤起支付参数:{}", map);
|
||||
//
|
||||
// return R.ok(map);
|
||||
// }
|
||||
log.error("微信支付参数验证错误,请及时处理");
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
} catch (Exception e) {
|
||||
log.error("支付异常", e);
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<Object> nativePay(HttpServletRequest request, PayParam payParam) {
|
||||
|
||||
try {
|
||||
|
||||
CashierParam cashierParam = cashierSupport.cashierParam(payParam);
|
||||
|
||||
//支付金额
|
||||
BigDecimal fen =cashierParam.getPrice();
|
||||
//第三方付款订单
|
||||
String outOrderNo = SnowFlake.getIdStr();
|
||||
//过期时间
|
||||
// String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
|
||||
|
||||
String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8);
|
||||
|
||||
|
||||
String appid = "setting.getServiceAppId()";
|
||||
if (appid == null) {
|
||||
throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
|
||||
}
|
||||
UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
|
||||
.setAppid(appid)
|
||||
.setMchid("setting.getMchId()")
|
||||
.setDescription(cashierParam.getDetail())
|
||||
.setOut_trade_no(outOrderNo)
|
||||
// .setTime_expire(timeExpire)
|
||||
//回传参数
|
||||
.setAttach(attach)
|
||||
.setNotify_url(notifyUrl("apiProperties.getBuyer()", PaymentMethodEnum.WECHAT))
|
||||
.setAmount(new Amount().setTotal(fen));
|
||||
|
||||
log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
|
||||
// PaymentHttpResponse response = WechatApi.v3(
|
||||
// RequestMethodEnums.POST,
|
||||
// WechatDomain.CHINA.toString(),
|
||||
// WechatApiEnum.NATIVE_PAY.toString(),
|
||||
//// setting.getMchId(),
|
||||
//// setting.getSerialNumber(),
|
||||
// null,
|
||||
//// setting.getApiclient_key(),
|
||||
// JSONUtil.toJsonStr(unifiedOrderModel)
|
||||
// );
|
||||
PaymentHttpResponse response=null;
|
||||
log.info("统一下单响应 {}", response);
|
||||
//根据证书序列号查询对应的证书来验证签名结果
|
||||
// boolean verifySignature = WxPayKit.verifySignature(response, getPlatformCert());
|
||||
// log.info("verifySignature: {}", verifySignature);
|
||||
//
|
||||
// if (verifySignature) {
|
||||
// return R.ok(new JSONObject(response.getBody()).getStr("code_url"));
|
||||
// } else {
|
||||
// log.error("微信支付参数验证错误,请及时处理");
|
||||
// throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
// }
|
||||
return R.ok("统一下单成功",outOrderNo);
|
||||
} catch (ServiceException e) {
|
||||
log.error("支付异常", e);
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
} catch (Exception e) {
|
||||
log.error("支付异常", e);
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<Object> mpPay(HttpServletRequest request, PayParam payParam) {
|
||||
|
||||
try {
|
||||
// Connect connect = connectService.queryConnect(
|
||||
// ConnectQueryDTO.builder().userId(UserContext.getCurrentUser().getId()).unionType(ConnectEnum.WECHAT_MP_OPEN_ID.name()).build()
|
||||
// );
|
||||
// if (connect == null) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// Payer payer = new Payer();
|
||||
// payer.setOpenid(connect.getUnionId());
|
||||
//
|
||||
// CashierParam cashierParam = cashierSupport.cashierParam(payParam);
|
||||
//
|
||||
// //支付金额
|
||||
// Integer fen = CurrencyUtil.fen(cashierParam.getPrice());
|
||||
// //第三方付款订单
|
||||
// String outOrderNo = SnowFlake.getIdStr();
|
||||
// //过期时间
|
||||
// String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
|
||||
//
|
||||
// //微信小程序,appid 需要单独获取,这里读取了联合登陆配置的appid ,实际场景小程序自动登录,所以这个appid是最为保险的做法
|
||||
// //如果有2开需求,这里需要调整,修改这个appid的获取途径即可
|
||||
// String appid = wechatPaymentSetting().getMpAppId();
|
||||
// if (StringUtils.isEmpty(appid)) {
|
||||
// throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
|
||||
// }
|
||||
// String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8);
|
||||
//
|
||||
// WechatPaymentSetting setting = wechatPaymentSetting();
|
||||
// UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
|
||||
// .setAppid(appid)
|
||||
// .setMchid(setting.getMchId())
|
||||
// .setDescription(cashierParam.getDetail())
|
||||
// .setOut_trade_no(outOrderNo)
|
||||
// .setTime_expire(timeExpire)
|
||||
// .setAttach(attach)
|
||||
// .setNotify_url(notifyUrl(apiProperties.getBuyer(), PaymentMethodEnum.WECHAT))
|
||||
// .setAmount(new Amount().setTotal(fen))
|
||||
// .setPayer(payer);
|
||||
//
|
||||
// log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
|
||||
// PaymentHttpResponse response = WechatApi.v3(
|
||||
// RequestMethodEnums.POST,
|
||||
// WechatDomain.CHINA.toString(),
|
||||
// WechatApiEnum.JS_API_PAY.toString(),
|
||||
// setting.getMchId(),
|
||||
// setting.getSerialNumber(),
|
||||
// null,
|
||||
// setting.getApiclient_key(),
|
||||
// JSONUtil.toJsonStr(unifiedOrderModel)
|
||||
// );
|
||||
// //根据证书序列号查询对应的证书来验证签名结果
|
||||
// boolean verifySignature = WxPayKit.verifySignature(response, getPlatformCert());
|
||||
// log.info("verifySignature: {}", verifySignature);
|
||||
// log.info("统一下单响应 {}", response);
|
||||
//
|
||||
// if (verifySignature) {
|
||||
// String body = response.getBody();
|
||||
// JSONObject jsonObject = JSONUtil.parseObj(body);
|
||||
// String prepayId = jsonObject.getStr("prepay_id");
|
||||
// Map<String, String> map = WxPayKit.jsApiCreateSign(appid, prepayId, setting.getApiclient_key());
|
||||
// log.info("唤起支付参数:{}", map);
|
||||
//
|
||||
// return R.ok(map);
|
||||
// }
|
||||
log.error("微信支付参数验证错误,请及时处理");
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
} catch (Exception e) {
|
||||
log.error("支付异常", e);
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void callBack(HttpServletRequest request) {
|
||||
try {
|
||||
verifyNotify(request);
|
||||
} catch (Exception e) {
|
||||
log.error("支付异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(HttpServletRequest request) {
|
||||
try {
|
||||
verifyNotify(request);
|
||||
} catch (Exception e) {
|
||||
log.error("支付异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证结果,执行支付回调
|
||||
*
|
||||
* @param request
|
||||
* @throws Exception
|
||||
*/
|
||||
private void verifyNotify(HttpServletRequest request) throws Exception {
|
||||
|
||||
String timestamp = request.getHeader("Wechatpay-Timestamp");
|
||||
String nonce = request.getHeader("Wechatpay-Nonce");
|
||||
String serialNo = request.getHeader("Wechatpay-Serial");
|
||||
String signature = request.getHeader("Wechatpay-Signature");
|
||||
|
||||
log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
|
||||
String result = HttpKit.readData(request);
|
||||
log.info("微信支付通知密文 {}", result);
|
||||
|
||||
//校验服务器端响应¬7
|
||||
// String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
|
||||
// setting.getApiKey3(), Objects.requireNonNull(getPlatformCert()));
|
||||
//
|
||||
// log.info("微信支付通知明文 {}", plainText);
|
||||
//
|
||||
// JSONObject jsonObject = JSONUtil.parseObj(plainText);
|
||||
//
|
||||
// String payParamStr = jsonObject.getStr("attach");
|
||||
// String payParamJson = URLDecoder.decode(payParamStr, StandardCharsets.UTF_8);
|
||||
// PayParam payParam = JSONUtil.toBean(payParamJson, PayParam.class);
|
||||
|
||||
|
||||
// String tradeNo = jsonObject.getStr("transaction_id");
|
||||
// Double totalAmount = CurrencyUtil.reversalFen(jsonObject.getJSONObject("amount").getDouble("total"));
|
||||
//
|
||||
// PaymentSuccessParams paymentSuccessParams = new PaymentSuccessParams(
|
||||
// PaymentMethodEnum.WECHAT.name(),
|
||||
// tradeNo,
|
||||
// totalAmount,
|
||||
// payParam
|
||||
// );
|
||||
//
|
||||
// paymentService.success(paymentSuccessParams);
|
||||
// log.info("微信支付回调:支付成功{}", plainText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refund(RefundLog refundLog) {
|
||||
|
||||
try {
|
||||
|
||||
// Amount amount = new Amount().setRefund(CurrencyUtil.fen(refundLog.getTotalAmount()))
|
||||
// .setTotal(CurrencyUtil.fen(orderService.getPaymentTotal(refundLog.getOrderSn())));
|
||||
//
|
||||
// //退款参数准备
|
||||
// RefundModel refundModel = new RefundModel()
|
||||
// .setTransaction_id(refundLog.getPaymentReceivableNo())
|
||||
// .setOut_refund_no(refundLog.getOutOrderNo())
|
||||
// .setReason(refundLog.getRefundReason())
|
||||
// .setAmount(amount)
|
||||
// .setNotify_url(refundNotifyUrl(apiProperties.getBuyer(), PaymentMethodEnum.WECHAT));
|
||||
//
|
||||
// WechatPaymentSetting setting = wechatPaymentSetting();
|
||||
//
|
||||
// log.info("微信退款参数 {}", JSONUtil.toJsonStr(refundModel));
|
||||
// PaymentHttpResponse response = WechatApi.v3(
|
||||
// RequestMethodEnums.POST,
|
||||
// WechatDomain.CHINA.toString(),
|
||||
// WechatApiEnum.DOMESTIC_REFUNDS.toString(),
|
||||
// setting.getMchId(),
|
||||
// setting.getSerialNumber(),
|
||||
// null,
|
||||
// setting.getApiclient_key(),
|
||||
// JSONUtil.toJsonStr(refundModel)
|
||||
// );
|
||||
// log.info("微信退款响应 {}", response);
|
||||
// //退款申请成功
|
||||
// if (response.getStatus() == 200) {
|
||||
// refundLogService.save(refundLog);
|
||||
// } else {
|
||||
// //退款申请失败
|
||||
// refundLog.setErrorMessage(response.getBody());
|
||||
// refundLogService.save(refundLog);
|
||||
// }
|
||||
} catch (Exception e) {
|
||||
log.error("微信退款申请失败", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel(RefundLog refundLog) {
|
||||
this.refund(refundLog);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void refundNotify(HttpServletRequest request) {
|
||||
String timestamp = request.getHeader("Wechatpay-Timestamp");
|
||||
String nonce = request.getHeader("Wechatpay-Nonce");
|
||||
String serialNo = request.getHeader("Wechatpay-Serial");
|
||||
String signature = request.getHeader("Wechatpay-Signature");
|
||||
|
||||
log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
|
||||
String result = HttpKit.readData(request);
|
||||
log.info("微信退款通知密文 {}", result);
|
||||
JSONObject ciphertext = JSONUtil.parseObj(result);
|
||||
|
||||
try { //校验服务器端响应¬
|
||||
// String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
|
||||
// wechatPaymentSetting().getApiKey3(), Objects.requireNonNull(getPlatformCert()));
|
||||
// log.info("微信退款通知明文 {}", plainText);
|
||||
|
||||
// if (("REFUND.SUCCESS").equals(ciphertext.getStr("event_type"))) {
|
||||
// log.info("退款成功 {}", plainText);
|
||||
// //校验服务器端响应
|
||||
// JSONObject jsonObject = JSONUtil.parseObj(plainText);
|
||||
// String transactionId = jsonObject.getStr("transaction_id");
|
||||
// String refundId = jsonObject.getStr("refund_id");
|
||||
//
|
||||
// RefundLog refundLog = refundLogService.getOne(new LambdaQueryWrapper<RefundLog>().eq(RefundLog::getPaymentReceivableNo, transactionId));
|
||||
// if (refundLog != null) {
|
||||
// refundLog.setIsRefund(true);
|
||||
// refundLog.setReceivableNo(refundId);
|
||||
// refundLogService.saveOrUpdate(refundLog);
|
||||
// }
|
||||
//
|
||||
// } else {
|
||||
// log.info("退款失败 {}", plainText);
|
||||
// JSONObject jsonObject = JSONUtil.parseObj(plainText);
|
||||
// String transactionId = jsonObject.getStr("transaction_id");
|
||||
// String refundId = jsonObject.getStr("refund_id");
|
||||
//
|
||||
// RefundLog refundLog = refundLogService.getOne(new LambdaQueryWrapper<RefundLog>().eq(RefundLog::getPaymentReceivableNo, transactionId));
|
||||
// if (refundLog != null) {
|
||||
// refundLog.setReceivableNo(refundId);
|
||||
// refundLog.setErrorMessage(ciphertext.getStr("summary"));
|
||||
// refundLogService.saveOrUpdate(refundLog);
|
||||
// }
|
||||
// }
|
||||
} catch (Exception e) {
|
||||
log.error("微信退款失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 获取微信支付配置
|
||||
// *
|
||||
// * @return
|
||||
// */
|
||||
// private WechatPaymentSetting wechatPaymentSetting() {
|
||||
// try {
|
||||
// Setting systemSetting = settingService.get(SettingEnum.WECHAT_PAYMENT.name());
|
||||
// WechatPaymentSetting wechatPaymentSetting = JSONUtil.toBean(systemSetting.getSettingValue(), WechatPaymentSetting.class);
|
||||
// return wechatPaymentSetting;
|
||||
// } catch (Exception e) {
|
||||
// log.error("微信支付暂不支持", e);
|
||||
// throw new ServiceException(ResultCode.PAY_NOT_SUPPORT);
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* 获取平台公钥
|
||||
*
|
||||
* @return 平台公钥
|
||||
*/
|
||||
private X509Certificate getPlatformCert() {
|
||||
//获取缓存中的平台公钥,如果有则直接返回,否则去微信请求
|
||||
// String publicCert = cache.get(CachePrefix.WECHAT_PLAT_FORM_CERT.getPrefix());
|
||||
// if (!StringUtils.isEmpty(publicCert)) {
|
||||
// return PayKit.getCertificate(publicCert);
|
||||
// }
|
||||
//获取平台证书列表
|
||||
try {
|
||||
|
||||
// WechatPaymentSetting setting = wechatPaymentSetting();
|
||||
|
||||
// PaymentHttpResponse response = WechatApi.v3(
|
||||
// RequestMethodEnums.GET,
|
||||
// WechatDomain.CHINA.toString(),
|
||||
// WechatApiEnum.GET_CERTIFICATES.toString(),
|
||||
// setting.getMchId(),
|
||||
// setting.getSerialNumber(),
|
||||
// null,
|
||||
// setting.getApiclient_key(),
|
||||
// ""
|
||||
// );
|
||||
// String body = response.getBody();
|
||||
// log.info("获取微信平台证书body: {}", body);
|
||||
//
|
||||
// if (response.getStatus() == 200) {
|
||||
// JSONObject jsonObject = JSONUtil.parseObj(body);
|
||||
// JSONArray dataArray = jsonObject.getJSONArray("data");
|
||||
// log.info("证书信息: {}", dataArray);
|
||||
//
|
||||
// //默认认为只有一个平台证书
|
||||
// JSONObject encryptObject = dataArray.getJSONObject(0);
|
||||
// JSONObject encryptCertificate = encryptObject.getJSONObject("encrypt_certificate");
|
||||
// String associatedData = encryptCertificate.getStr("associated_data");
|
||||
// String cipherText = encryptCertificate.getStr("ciphertext");
|
||||
// String nonce = encryptCertificate.getStr("nonce");
|
||||
// publicCert = getPlatformCertStr(associatedData, nonce, cipherText);
|
||||
// long second = (PayKit.getCertificate(publicCert).getNotAfter().getTime() - System.currentTimeMillis()) / 1000;
|
||||
// cache.put(CachePrefix.WECHAT_PLAT_FORM_CERT.getPrefix(), publicCert, second);
|
||||
// } else {
|
||||
// log.error("证书获取失败:{}" + body);
|
||||
// throw new ServiceException(ResultCode.WECHAT_CERT_ERROR);
|
||||
// }
|
||||
// return PayKit.getCertificate(publicCert);
|
||||
} catch (Exception e) {
|
||||
log.error("证书获取失败", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
//
|
||||
// /**
|
||||
// * 获取平台证书缓存的字符串
|
||||
// * 下列各个密钥参数
|
||||
// *
|
||||
// * @param associatedData 密钥参数
|
||||
// * @param nonce 密钥参数
|
||||
// * @param cipherText 密钥参数
|
||||
// * @return platform key
|
||||
// * @throws GeneralSecurityException 密钥获取异常
|
||||
// */
|
||||
// private String getPlatformCertStr(String associatedData, String nonce, String cipherText) throws GeneralSecurityException {
|
||||
//
|
||||
//
|
||||
// AesUtil aesUtil = new AesUtil(wechatPaymentSetting().getApiKey3().getBytes(StandardCharsets.UTF_8));
|
||||
// //平台证书密文解密
|
||||
// //encrypt_certificate 中的 associated_data nonce ciphertext
|
||||
// return aesUtil.decryptToString(
|
||||
// associatedData.getBytes(StandardCharsets.UTF_8),
|
||||
// nonce.getBytes(StandardCharsets.UTF_8),
|
||||
// cipherText
|
||||
// );
|
||||
// }
|
||||
}
|
||||
|
@ -0,0 +1,205 @@
|
||||
package com.wzj.soopin.transaction.kit.plugin.wallet;
|
||||
|
||||
|
||||
import com.wzj.soopin.member.enums.AccountBillChangeTypeEnum;
|
||||
import com.wzj.soopin.member.enums.AccountBillSourceEnum;
|
||||
import com.wzj.soopin.member.enums.AccountTypeEnum;
|
||||
import com.wzj.soopin.member.service.IMemberAccountService;
|
||||
import com.wzj.soopin.transaction.domain.po.RefundLog;
|
||||
import com.wzj.soopin.transaction.enums.CashierEnum;
|
||||
import com.wzj.soopin.transaction.enums.PaymentMethodEnum;
|
||||
import com.wzj.soopin.transaction.kit.CashierSupport;
|
||||
import com.wzj.soopin.transaction.kit.Payment;
|
||||
import com.wzj.soopin.transaction.kit.dto.PayParam;
|
||||
import com.wzj.soopin.transaction.kit.dto.PaymentSuccessParams;
|
||||
import com.wzj.soopin.transaction.kit.params.dto.CashierParam;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.constant.ResultCode;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.domain.event.Constants;
|
||||
import org.dromara.common.core.domain.model.LoginUser;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.redisson.api.RLock;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
||||
/**
|
||||
* WalletPlugin
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v1.0
|
||||
* 2021-02-20 10:14
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class WalletPlugin implements Payment {
|
||||
|
||||
/**
|
||||
* 支付日志
|
||||
*/
|
||||
// private final paymentService;
|
||||
/**
|
||||
* 退款日志
|
||||
*/
|
||||
// private final RefundLogService refundLogService;
|
||||
/**
|
||||
* 会员余额
|
||||
*/
|
||||
private final IMemberAccountService memberAccountService;
|
||||
/**
|
||||
* 收银台
|
||||
*/
|
||||
private final CashierSupport cashierSupport;
|
||||
|
||||
private final RedissonClient redisson;
|
||||
|
||||
@Override
|
||||
public R<Object> h5pay(HttpServletRequest request, HttpServletResponse response, PayParam payParam) {
|
||||
savePaymentLog(payParam);
|
||||
return R.ok(ResultCode.PAY_SUCCESS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<Object> jsApiPay(HttpServletRequest request, PayParam payParam) {
|
||||
savePaymentLog(payParam);
|
||||
return R.ok(ResultCode.PAY_SUCCESS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<Object> appPay(HttpServletRequest request, PayParam payParam) {
|
||||
savePaymentLog(payParam);
|
||||
return R.ok(ResultCode.PAY_SUCCESS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<Object> nativePay(HttpServletRequest request, PayParam payParam) {
|
||||
if (payParam.getOrderType().equals(CashierEnum.RECHARGE.name())) {
|
||||
throw new ServiceException(ResultCode.CAN_NOT_RECHARGE_WALLET);
|
||||
}
|
||||
savePaymentLog(payParam);
|
||||
return R.ok(ResultCode.PAY_SUCCESS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<Object> mpPay(HttpServletRequest request, PayParam payParam) {
|
||||
|
||||
savePaymentLog(payParam);
|
||||
return R.ok(ResultCode.PAY_SUCCESS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel(RefundLog refundLog) {
|
||||
|
||||
try {
|
||||
// memberWalletService.increase(new MemberWalletUpdateDTO(refundLog.getTotalAmount(),
|
||||
// refundLog.getMemberId(),
|
||||
// "取消[" + refundLog.getOrderSn() + "]订单,退还金额[" + refundLog.getTotalAmount() + "]",
|
||||
// DepositServiceTypeEnum.WALLET_REFUND.name()));
|
||||
// refundLog.setIsRefund(true);
|
||||
// refundLogService.save(refundLog);
|
||||
} catch (Exception e) {
|
||||
log.error("订单取消错误", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存支付日志
|
||||
*
|
||||
* @param payParam 支付参数
|
||||
*/
|
||||
private void savePaymentLog(PayParam payParam) {
|
||||
//同一个会员如果在不同的客户端使用预存款支付,会存在同时支付,无法保证预存款的正确性,所以对会员加锁
|
||||
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
if (loginUser == null) {
|
||||
throw new ServiceException("用户未登录");
|
||||
}
|
||||
RLock lock = redisson.getLock(loginUser + "");
|
||||
lock.lock();
|
||||
try {
|
||||
//获取支付收银参数
|
||||
CashierParam cashierParam = cashierSupport.cashierParam(payParam);
|
||||
this.callBack(payParam, cashierParam);
|
||||
} catch (Exception e) {
|
||||
throw e;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refund(RefundLog refundLog) {
|
||||
try {
|
||||
// memberWalletService.increase(new MemberWalletUpdateDTO(refundLog.getTotalAmount(),
|
||||
// refundLog.getMemberId(),
|
||||
// "售后[" + refundLog.getAfterSaleNo() + "]审批,退还金额[" + refundLog.getTotalAmount() + "]",
|
||||
// DepositServiceTypeEnum.WALLET_REFUND.name()));
|
||||
// refundLog.setIsRefund(true);
|
||||
// refundLogService.save(refundLog);
|
||||
} catch (Exception e) {
|
||||
log.error("退款失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付订单
|
||||
*
|
||||
* @param payParam 支付参数
|
||||
* @param cashierParam 收银台参数
|
||||
*/
|
||||
public void callBack(PayParam payParam, CashierParam cashierParam) {
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
//支付信息
|
||||
try {
|
||||
if (loginUser.getUserId() == null) {
|
||||
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
|
||||
}
|
||||
//个人账户扣减
|
||||
boolean result = memberAccountService.reduceMoney(
|
||||
cashierParam.getPrice(),
|
||||
loginUser.getUserId(),
|
||||
|
||||
AccountBillSourceEnum.PAYMENT,
|
||||
"订单[" + cashierParam.getOrderSns() + "]支付金额[" + cashierParam.getPrice() + "]"
|
||||
);
|
||||
//更新支付日志
|
||||
if (result) {
|
||||
try {
|
||||
// PaymentSuccessParams paymentSuccessParams = new PaymentSuccessParams(
|
||||
// PaymentMethodEnum.WALLET.name(),
|
||||
// "",
|
||||
// cashierParam.getPrice(),
|
||||
// payParam
|
||||
// );
|
||||
//
|
||||
// paymentService.success(paymentSuccessParams);
|
||||
log.info("支付回调通知:余额支付:{}", payParam);
|
||||
} catch (ServiceException e) {
|
||||
//业务异常,则支付手动回滚
|
||||
// memberWalletService.increase(new MemberWalletUpdateDTO(
|
||||
// cashierParam.getPrice(),
|
||||
// UserContext.getCurrentUser().getId(),
|
||||
// "订单[" + cashierParam.getOrderSns() + "]支付异常,余额返还[" + cashierParam.getPrice() + "]",
|
||||
// DepositServiceTypeEnum.WALLET_REFUND.name())
|
||||
// );
|
||||
throw e;
|
||||
}
|
||||
} else {
|
||||
throw new ServiceException(ResultCode.WALLET_INSUFFICIENT);
|
||||
}
|
||||
} catch (ServiceException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
log.info("余额支付异常", e);
|
||||
throw new ServiceException(ResultCode.WALLET_INSUFFICIENT);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,859 @@
|
||||
package com.wzj.soopin.transaction.kit.plugin.wechat;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.ContentType;
|
||||
import com.wzj.soopin.transaction.kit.core.PaymentHttpResponse;
|
||||
import com.wzj.soopin.transaction.kit.core.enums.RequestMethodEnums;
|
||||
import com.wzj.soopin.transaction.kit.core.kit.HttpKit;
|
||||
import com.wzj.soopin.transaction.kit.core.kit.PayKit;
|
||||
import com.wzj.soopin.transaction.kit.core.kit.WxPayKit;
|
||||
import com.wzj.soopin.transaction.kit.plugin.wechat.enums.WechatApiEnum;
|
||||
import com.wzj.soopin.transaction.kit.plugin.wechat.enums.WechatDomain;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.security.PrivateKey;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 微信支付相关接口
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2021/1/26 15:25
|
||||
*/
|
||||
|
||||
public class WechatApi {
|
||||
|
||||
private WechatApi() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取接口请求的 URL
|
||||
*
|
||||
* @param wechatApiEnum {@link WechatApiEnum} 支付 API 接口枚举
|
||||
* @return {@link String} 返回完整的接口请求URL
|
||||
*/
|
||||
public static String getReqUrl(WechatApiEnum wechatApiEnum) {
|
||||
return getReqUrl(wechatApiEnum, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取接口请求的 URL
|
||||
*
|
||||
* @param wechatApiEnum {@link WechatApiEnum} 支付 API 接口枚举
|
||||
* @param isSandBox 是否是沙箱环境
|
||||
* @return {@link String} 返回完整的接口请求URL
|
||||
*/
|
||||
public static String getReqUrl(WechatApiEnum wechatApiEnum, boolean isSandBox) {
|
||||
return getReqUrl(wechatApiEnum, null, isSandBox);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取接口请求的 URL
|
||||
*
|
||||
* @param wechatApiEnum {@link WechatApiEnum} 支付 API 接口枚举
|
||||
* @param wechatDomain {@link WechatDomain} 支付 API 接口域名枚举
|
||||
* @param isSandBox 是否是沙箱环境
|
||||
* @return {@link String} 返回完整的接口请求URL
|
||||
*/
|
||||
public static String getReqUrl(WechatApiEnum wechatApiEnum, WechatDomain wechatDomain, boolean isSandBox) {
|
||||
if (wechatDomain == null) {
|
||||
wechatDomain = WechatDomain.CHINA;
|
||||
}
|
||||
return wechatDomain.getType()
|
||||
.concat(isSandBox ? WechatApiEnum.SAND_BOX_NEW.getUrl() : "")
|
||||
.concat(wechatApiEnum.getUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* 发起请求
|
||||
*
|
||||
* @param apiUrl 接口 URL
|
||||
* 通过 {@link WechatApi#getReqUrl(WechatApiEnum)}
|
||||
* 或者 {@link WechatApi#getReqUrl(WechatApiEnum, WechatDomain, boolean)} 来获取
|
||||
* @param params 接口请求参数
|
||||
* @return {@link String} 请求返回的结果
|
||||
*/
|
||||
public static String execution(String apiUrl, Map<String, String> params) {
|
||||
return doPost(apiUrl, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发起请求
|
||||
*
|
||||
* @param apiUrl 接口 URL
|
||||
* 通过 {@link WechatApi#getReqUrl(WechatApiEnum)}
|
||||
* 或者 {@link WechatApi#getReqUrl(WechatApiEnum, WechatDomain, boolean)} 来获取
|
||||
* @param params 接口请求参数
|
||||
* @return {@link String} 请求返回的结果
|
||||
*/
|
||||
public static String executionByGet(String apiUrl, Map<String, Object> params) {
|
||||
return doGet(apiUrl, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发起请求
|
||||
*
|
||||
* @param apiUrl 接口 URL
|
||||
* 通过 {@link WechatApi#getReqUrl(WechatApiEnum)}
|
||||
* 或者 {@link WechatApi#getReqUrl(WechatApiEnum, WechatDomain, boolean)} 来获取
|
||||
* @param params 接口请求参数
|
||||
* @param certPath 证书文件路径
|
||||
* @param certPass 证书密码
|
||||
* @return {@link String} 请求返回的结果
|
||||
*/
|
||||
public static String execution(String apiUrl, Map<String, String> params, String certPath, String certPass) {
|
||||
return doPostSsl(apiUrl, params, certPath, certPass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发起请求
|
||||
*
|
||||
* @param apiUrl 接口 URL
|
||||
* 通过 {@link WechatApi#getReqUrl(WechatApiEnum)}
|
||||
* 或者 {@link WechatApi#getReqUrl(WechatApiEnum, WechatDomain, boolean)} 来获取
|
||||
* @param params 接口请求参数
|
||||
* @param certPath 证书文件路径
|
||||
* @return {@link String} 请求返回的结果
|
||||
*/
|
||||
public static String execution(String apiUrl, Map<String, String> params, String certPath) {
|
||||
return doPostSsl(apiUrl, params, certPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发起请求
|
||||
*
|
||||
* @param apiUrl 接口 URL
|
||||
* 通过 {@link WechatApi#getReqUrl(WechatApiEnum)}
|
||||
* 或者 {@link WechatApi#getReqUrl(WechatApiEnum, WechatDomain, boolean)} 来获取
|
||||
* @param params 接口请求参数
|
||||
* @param certFile 证书文件输入流
|
||||
* @param certPass 证书密码
|
||||
* @return {@link String} 请求返回的结果
|
||||
*/
|
||||
public static String execution(String apiUrl, Map<String, String> params, InputStream certFile, String certPass) {
|
||||
return doPostSsl(apiUrl, params, certFile, certPass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发起请求
|
||||
*
|
||||
* @param apiUrl 接口 URL
|
||||
* 通过 {@link WechatApi#getReqUrl(WechatApiEnum)}
|
||||
* 或者 {@link WechatApi#getReqUrl(WechatApiEnum, WechatDomain, boolean)} 来获取
|
||||
* @param params 接口请求参数
|
||||
* @param certFile 证书文件输入流
|
||||
* @return {@link String} 请求返回的结果
|
||||
*/
|
||||
public static String execution(String apiUrl, Map<String, String> params, InputStream certFile) {
|
||||
return doPostSsl(apiUrl, params, certFile);
|
||||
}
|
||||
|
||||
public static String execution(String apiUrl, Map<String, String> params,
|
||||
String certPath, String certPass, String filePath) {
|
||||
return doUploadSsl(apiUrl, params, certPath, certPass, filePath);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* V3 接口统一执行入口
|
||||
*
|
||||
* @param method {@link RequestMethodEnums} 请求方法
|
||||
* @param urlPrefix 可通过 {@link WechatDomain}来获取
|
||||
* @param urlSuffix 可通过 {@link WechatApiEnum} 来获取,URL挂载参数需要自行拼接
|
||||
* @param mchId 商户Id
|
||||
* @param serialNo 商户 API 证书序列号
|
||||
* @param platSerialNo 平台序列号,接口中包含敏感信息时必传
|
||||
* @param keyPath apiclient_key.pem 证书路径
|
||||
* @param body 接口请求参数
|
||||
* @param nonceStr 随机字符库
|
||||
* @param timestamp 时间戳
|
||||
* @param authType 认证类型
|
||||
* @param file 文件
|
||||
* @return {@link PaymentHttpResponse} 请求返回的结果
|
||||
* @throws Exception 接口执行异常
|
||||
*/
|
||||
public static PaymentHttpResponse v3(RequestMethodEnums method, String urlPrefix, String urlSuffix,
|
||||
String mchId, String serialNo, String platSerialNo, String keyPath,
|
||||
String body, String nonceStr, long timestamp, String authType,
|
||||
File file) throws Exception {
|
||||
//构建 Authorization
|
||||
String authorization = WxPayKit.buildAuthorization(method, urlSuffix, mchId, serialNo,
|
||||
keyPath, body, nonceStr, timestamp, authType);
|
||||
|
||||
if (StrUtil.isEmpty(platSerialNo)) {
|
||||
platSerialNo = serialNo;
|
||||
}
|
||||
if (method == RequestMethodEnums.GET) {
|
||||
return get(urlPrefix.concat(urlSuffix), authorization, platSerialNo, null);
|
||||
} else if (method == RequestMethodEnums.POST) {
|
||||
return post(urlPrefix.concat(urlSuffix), authorization, platSerialNo, body);
|
||||
} else if (method == RequestMethodEnums.DELETE) {
|
||||
return delete(urlPrefix.concat(urlSuffix), authorization, platSerialNo, body);
|
||||
} else if (method == RequestMethodEnums.UPLOAD) {
|
||||
return upload(urlPrefix.concat(urlSuffix), authorization, platSerialNo, body, file);
|
||||
} else if (method == RequestMethodEnums.PUT) {
|
||||
return put(urlPrefix.concat(urlSuffix), authorization, platSerialNo, body);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* V3 接口统一执行入口
|
||||
*
|
||||
* @param method {@link RequestMethodEnums} 请求方法
|
||||
* @param urlPrefix 可通过 {@link WechatDomain}来获取
|
||||
* @param urlSuffix 可通过 {@link WechatApiEnum} 来获取,URL挂载参数需要自行拼接
|
||||
* @param mchId 商户Id
|
||||
* @param serialNo 商户 API 证书序列号
|
||||
* @param platSerialNo 平台序列号,接口中包含敏感信息时必传
|
||||
* @param privateKey 商户私钥
|
||||
* @param body 接口请求参数
|
||||
* @param nonceStr 随机字符库
|
||||
* @param timestamp 时间戳
|
||||
* @param authType 认证类型
|
||||
* @param file 文件
|
||||
* @return {@link PaymentHttpResponse} 请求返回的结果
|
||||
* @throws Exception 接口执行异常
|
||||
*/
|
||||
public static PaymentHttpResponse v3(RequestMethodEnums method, String urlPrefix, String urlSuffix,
|
||||
String mchId, String serialNo, String platSerialNo, PrivateKey privateKey,
|
||||
String body, String nonceStr, long timestamp, String authType,
|
||||
File file) throws Exception {
|
||||
//构建 Authorization
|
||||
String authorization = WxPayKit.buildAuthorization(method, urlSuffix, mchId, serialNo,
|
||||
privateKey, body, nonceStr, timestamp, authType);
|
||||
|
||||
if (StrUtil.isEmpty(platSerialNo)) {
|
||||
platSerialNo = serialNo;
|
||||
}
|
||||
|
||||
if (method == RequestMethodEnums.GET) {
|
||||
return get(urlPrefix.concat(urlSuffix), authorization, platSerialNo, null);
|
||||
} else if (method == RequestMethodEnums.POST) {
|
||||
return post(urlPrefix.concat(urlSuffix), authorization, platSerialNo, body);
|
||||
} else if (method == RequestMethodEnums.DELETE) {
|
||||
return delete(urlPrefix.concat(urlSuffix), authorization, platSerialNo, body);
|
||||
} else if (method == RequestMethodEnums.UPLOAD) {
|
||||
return upload(urlPrefix.concat(urlSuffix), authorization, platSerialNo, body, file);
|
||||
} else if (method == RequestMethodEnums.PUT) {
|
||||
return put(urlPrefix.concat(urlSuffix), authorization, platSerialNo, body);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* V3 接口统一执行入口
|
||||
*
|
||||
* @param method {@link RequestMethodEnums} 请求方法
|
||||
* @param urlPrefix 可通过 {@link WechatDomain}来获取
|
||||
* @param urlSuffix 可通过 {@link WechatApiEnum} 来获取,URL挂载参数需要自行拼接
|
||||
* @param mchId 商户Id
|
||||
* @param serialNo 商户 API 证书序列号
|
||||
* @param platSerialNo 平台序列号
|
||||
* @param keyPath apiclient_key.pem 证书路径
|
||||
* @param body 接口请求参数
|
||||
* @return {@link PaymentHttpResponse} 请求返回的结果
|
||||
* @throws Exception 接口执行异常
|
||||
*/
|
||||
public static PaymentHttpResponse v3(RequestMethodEnums method, String urlPrefix, String urlSuffix, String mchId,
|
||||
String serialNo, String platSerialNo, String keyPath, String body) throws Exception {
|
||||
long timestamp = System.currentTimeMillis() / 1000;
|
||||
String authType = "WECHATPAY2-SHA256-RSA2048";
|
||||
String nonceStr = WxPayKit.generateStr();
|
||||
return v3(method, urlPrefix, urlSuffix, mchId, serialNo, platSerialNo, keyPath, body, nonceStr, timestamp, authType, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* V3 接口统一执行入口
|
||||
*
|
||||
* @param method {@link RequestMethodEnums} 请求方法
|
||||
* @param urlPrefix 可通过 {@link WechatDomain}来获取
|
||||
* @param urlSuffix 可通过 {@link WechatApiEnum} 来获取,URL挂载参数需要自行拼接
|
||||
* @param mchId 商户Id
|
||||
* @param serialNo 商户 API 证书序列号
|
||||
* @param platSerialNo 平台序列号
|
||||
* @param privateKey 商户私钥
|
||||
* @param body 接口请求参数
|
||||
* @return {@link PaymentHttpResponse} 请求返回的结果
|
||||
* @throws Exception 接口执行异常
|
||||
*/
|
||||
public static PaymentHttpResponse v3(RequestMethodEnums method, String urlPrefix, String urlSuffix, String mchId,
|
||||
String serialNo, String platSerialNo, PrivateKey privateKey, String body) throws Exception {
|
||||
long timestamp = System.currentTimeMillis() / 1000;
|
||||
String authType = "WECHATPAY2-SHA256-RSA2048";
|
||||
String nonceStr = WxPayKit.generateStr();
|
||||
return v3(method, urlPrefix, urlSuffix, mchId, serialNo, platSerialNo, privateKey, body, nonceStr, timestamp, authType, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* V3 接口统一执行入口
|
||||
*
|
||||
* @param method {@link RequestMethodEnums} 请求方法
|
||||
* @param urlPrefix 可通过 {@link WechatDomain}来获取
|
||||
* @param urlSuffix 可通过 {@link WechatApiEnum} 来获取,URL挂载参数需要自行拼接
|
||||
* @param mchId 商户Id
|
||||
* @param serialNo 商户 API 证书序列号
|
||||
* @param platSerialNo 平台序列号
|
||||
* @param keyPath apiclient_key.pem 证书路径
|
||||
* @param params Get 接口请求参数
|
||||
* @return {@link PaymentHttpResponse} 请求返回的结果
|
||||
* @throws Exception 接口执行异常
|
||||
*/
|
||||
public static PaymentHttpResponse v3(RequestMethodEnums method, String urlPrefix, String urlSuffix,
|
||||
String mchId, String serialNo, String platSerialNo, String keyPath,
|
||||
Map<String, String> params) throws Exception {
|
||||
long timestamp = System.currentTimeMillis() / 1000;
|
||||
String authType = "WECHATPAY2-SHA256-RSA2048";
|
||||
String nonceStr = WxPayKit.generateStr();
|
||||
if (null != params && !params.keySet().isEmpty()) {
|
||||
urlSuffix = urlSuffix.concat("?").concat(PayKit.createLinkString(params, true));
|
||||
}
|
||||
return v3(method, urlPrefix, urlSuffix, mchId, serialNo, platSerialNo, keyPath, "", nonceStr, timestamp, authType, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* V3 接口统一执行入口
|
||||
*
|
||||
* @param method {@link RequestMethodEnums} 请求方法
|
||||
* @param urlPrefix 可通过 {@link WechatDomain}来获取
|
||||
* @param urlSuffix 可通过 {@link WechatApiEnum} 来获取,URL挂载参数需要自行拼接
|
||||
* @param mchId 商户Id
|
||||
* @param serialNo 商户 API 证书序列号
|
||||
* @param platSerialNo 平台序列号
|
||||
* @param privateKey 商户私钥
|
||||
* @param params Get 接口请求参数
|
||||
* @return {@link PaymentHttpResponse} 请求返回的结果
|
||||
* @throws Exception 接口执行异常
|
||||
*/
|
||||
public static PaymentHttpResponse v3(RequestMethodEnums method, String urlPrefix, String urlSuffix,
|
||||
String mchId, String serialNo, String platSerialNo, PrivateKey privateKey,
|
||||
Map<String, String> params) throws Exception {
|
||||
long timestamp = System.currentTimeMillis() / 1000;
|
||||
String authType = "WECHATPAY2-SHA256-RSA2048";
|
||||
String nonceStr = WxPayKit.generateStr();
|
||||
if (null != params && !params.keySet().isEmpty()) {
|
||||
urlSuffix = urlSuffix.concat("?").concat(PayKit.createLinkString(params, true));
|
||||
}
|
||||
return v3(method, urlPrefix, urlSuffix, mchId, serialNo, platSerialNo, privateKey, "", nonceStr, timestamp, authType, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* V3 接口统一执行入口
|
||||
*
|
||||
* @param urlPrefix 可通过 {@link WechatDomain}来获取
|
||||
* @param urlSuffix 可通过 {@link WechatApiEnum} 来获取,URL挂载参数需要自行拼接
|
||||
* @param mchId 商户Id
|
||||
* @param serialNo 商户 API 证书序列号
|
||||
* @param platSerialNo 平台序列号
|
||||
* @param keyPath apiclient_key.pem 证书路径
|
||||
* @param body 接口请求参数
|
||||
* @param file 文件
|
||||
* @return {@link PaymentHttpResponse} 请求返回的结果
|
||||
* @throws Exception 接口执行异常
|
||||
*/
|
||||
public static PaymentHttpResponse v3(String urlPrefix, String urlSuffix, String mchId, String serialNo, String platSerialNo, String keyPath, String body, File file) throws Exception {
|
||||
long timestamp = System.currentTimeMillis() / 1000;
|
||||
String authType = "WECHATPAY2-SHA256-RSA2048";
|
||||
String nonceStr = WxPayKit.generateStr();
|
||||
return v3(RequestMethodEnums.UPLOAD, urlPrefix, urlSuffix, mchId, serialNo, platSerialNo, keyPath, body, nonceStr, timestamp, authType, file);
|
||||
}
|
||||
|
||||
/**
|
||||
* V3 接口统一执行入口
|
||||
*
|
||||
* @param urlPrefix 可通过 {@link WechatDomain}来获取
|
||||
* @param urlSuffix 可通过 {@link WechatApiEnum} 来获取,URL挂载参数需要自行拼接
|
||||
* @param mchId 商户Id
|
||||
* @param serialNo 商户 API 证书序列号
|
||||
* @param platSerialNo 平台序列号
|
||||
* @param privateKey 商户私钥
|
||||
* @param body 接口请求参数
|
||||
* @param file 文件
|
||||
* @return {@link PaymentHttpResponse} 请求返回的结果
|
||||
* @throws Exception 接口执行异常
|
||||
*/
|
||||
public static PaymentHttpResponse v3(String urlPrefix, String urlSuffix, String mchId, String serialNo,
|
||||
String platSerialNo, PrivateKey privateKey, String body, File file) throws Exception {
|
||||
long timestamp = System.currentTimeMillis() / 1000;
|
||||
String authType = "WECHATPAY2-SHA256-RSA2048";
|
||||
String nonceStr = WxPayKit.generateStr();
|
||||
return v3(RequestMethodEnums.UPLOAD, urlPrefix, urlSuffix, mchId, serialNo, platSerialNo, privateKey, body, nonceStr, timestamp, authType, file);
|
||||
}
|
||||
|
||||
/**
|
||||
* V3 接口统一执行入口
|
||||
*
|
||||
* @param method {@link RequestMethodEnums} 请求方法
|
||||
* @param urlPrefix 可通过 {@link WechatDomain}来获取
|
||||
* @param urlSuffix 可通过 {@link WechatApiEnum} 来获取,URL挂载参数需要自行拼接
|
||||
* @param mchId 商户Id
|
||||
* @param serialNo 商户 API 证书序列号
|
||||
* @param platSerialNo 平台序列号,接口中包含敏感信息时必传
|
||||
* @param keyPath apiclient_key.pem 证书路径
|
||||
* @param body 接口请求参数
|
||||
* @param nonceStr 随机字符库
|
||||
* @param timestamp 时间戳
|
||||
* @param authType 认证类型
|
||||
* @param file 文件
|
||||
* @return {@link Map} 请求返回的结果
|
||||
* @throws Exception 接口执行异常
|
||||
*/
|
||||
@Deprecated
|
||||
public static Map<String, Object> v3Execution(RequestMethodEnums method, String urlPrefix, String urlSuffix,
|
||||
String mchId, String serialNo, String platSerialNo, String keyPath,
|
||||
String body, String nonceStr, long timestamp, String authType,
|
||||
File file) throws Exception {
|
||||
PaymentHttpResponse response = v3(method, urlPrefix, urlSuffix, mchId, serialNo, platSerialNo, keyPath, body, nonceStr, timestamp, authType, file);
|
||||
return buildResMap(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* V3 接口统一执行入口
|
||||
*
|
||||
* @param method {@link RequestMethodEnums} 请求方法
|
||||
* @param urlPrefix 可通过 {@link WechatDomain}来获取
|
||||
* @param urlSuffix 可通过 {@link WechatApiEnum} 来获取,URL挂载参数需要自行拼接
|
||||
* @param mchId 商户Id
|
||||
* @param serialNo 商户 API 证书序列号
|
||||
* @param keyPath apiclient_key.pem 证书路径
|
||||
* @param body 接口请求参数
|
||||
* @return {@link Map} 请求返回的结果
|
||||
*/
|
||||
@Deprecated
|
||||
public static Map<String, Object> v3Execution(RequestMethodEnums method, String urlPrefix, String urlSuffix, String mchId,
|
||||
String serialNo, String keyPath, String body) throws Exception {
|
||||
PaymentHttpResponse response = v3(method, urlPrefix, urlSuffix, mchId, serialNo, null, keyPath, body);
|
||||
return buildResMap(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* V3 接口统一执行入口
|
||||
*
|
||||
* @param method {@link RequestMethodEnums} 请求方法
|
||||
* @param urlPrefix 可通过 {@link WechatDomain}来获取
|
||||
* @param urlSuffix 可通过 {@link WechatApiEnum} 来获取,URL挂载参数需要自行拼接
|
||||
* @param mchId 商户Id
|
||||
* @param serialNo 商户 API 证书序列号
|
||||
* @param platSerialNo 平台序列号
|
||||
* @param keyPath apiclient_key.pem 证书路径
|
||||
* @param body 接口请求参数
|
||||
* @return {@link Map} 请求返回的结果
|
||||
* @throws Exception 接口执行异常
|
||||
*/
|
||||
@Deprecated
|
||||
public static Map<String, Object> v3Execution(RequestMethodEnums method, String urlPrefix, String urlSuffix, String mchId,
|
||||
String serialNo, String platSerialNo, String keyPath, String body) throws Exception {
|
||||
PaymentHttpResponse response = v3(method, urlPrefix, urlSuffix, mchId, serialNo, platSerialNo, keyPath, body);
|
||||
return buildResMap(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* V3 接口统一执行入口
|
||||
*
|
||||
* @param method {@link RequestMethodEnums} 请求方法
|
||||
* @param urlPrefix 可通过 {@link WechatDomain}来获取
|
||||
* @param urlSuffix 可通过 {@link WechatApiEnum} 来获取,URL挂载参数需要自行拼接
|
||||
* @param mchId 商户Id
|
||||
* @param serialNo 商户 API 证书序列号
|
||||
* @param platSerialNo 平台序列号
|
||||
* @param keyPath apiclient_key.pem 证书路径
|
||||
* @param params Get 接口请求参数
|
||||
* @return {@link Map} 请求返回的结果
|
||||
* @throws Exception 接口执行异常
|
||||
*/
|
||||
@Deprecated
|
||||
public static Map<String, Object> v3Execution(RequestMethodEnums method, String urlPrefix, String urlSuffix,
|
||||
String mchId, String serialNo, String platSerialNo, String keyPath,
|
||||
Map<String, String> params) throws Exception {
|
||||
PaymentHttpResponse response = v3(method, urlPrefix, urlSuffix, mchId, serialNo, platSerialNo, keyPath, params);
|
||||
return buildResMap(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* V3 接口统一执行入口
|
||||
*
|
||||
* @param method {@link RequestMethodEnums} 请求方法
|
||||
* @param urlPrefix 可通过 {@link WechatDomain}来获取
|
||||
* @param urlSuffix 可通过 {@link WechatApiEnum} 来获取,URL挂载参数需要自行拼接
|
||||
* @param mchId 商户Id
|
||||
* @param serialNo 商户 API 证书序列号
|
||||
* @param keyPath apiclient_key.pem 证书路径
|
||||
* @param params Get 接口请求参数
|
||||
* @return {@link Map} 请求返回的结果
|
||||
* @throws Exception 接口执行异常
|
||||
*/
|
||||
@Deprecated
|
||||
public static Map<String, Object> v3Execution(RequestMethodEnums method, String urlPrefix, String urlSuffix,
|
||||
String mchId, String serialNo, String keyPath,
|
||||
Map<String, String> params) throws Exception {
|
||||
PaymentHttpResponse response = v3(method, urlPrefix, urlSuffix, mchId, serialNo, null, keyPath, params);
|
||||
return buildResMap(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* V3 接口统一执行入口
|
||||
*
|
||||
* @param urlPrefix 可通过 {@link WechatDomain}来获取
|
||||
* @param urlSuffix 可通过 {@link WechatApiEnum} 来获取,URL挂载参数需要自行拼接
|
||||
* @param mchId 商户Id
|
||||
* @param serialNo 商户 API 证书序列号
|
||||
* @param platSerialNo 平台序列号
|
||||
* @param keyPath apiclient_key.pem 证书路径
|
||||
* @param body 接口请求参数
|
||||
* @param file 文件
|
||||
* @return {@link Map} 请求返回的结果
|
||||
* @throws Exception 接口执行异常
|
||||
*/
|
||||
@Deprecated
|
||||
public static Map<String, Object> v3Upload(String urlPrefix, String urlSuffix, String mchId, String serialNo, String platSerialNo, String keyPath, String body, File file) throws Exception {
|
||||
PaymentHttpResponse response = v3(urlPrefix, urlSuffix, mchId, serialNo, platSerialNo, keyPath, body, file);
|
||||
return buildResMap(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* V3 接口统一执行入口
|
||||
*
|
||||
* @param urlPrefix 可通过 {@link WechatDomain}来获取
|
||||
* @param urlSuffix 可通过 {@link WechatApiEnum} 来获取,URL挂载参数需要自行拼接
|
||||
* @param mchId 商户Id
|
||||
* @param serialNo 商户 API 证书序列号
|
||||
* @param keyPath apiclient_key.pem 证书路径
|
||||
* @param body 接口请求参数
|
||||
* @param file 文件
|
||||
* @return {@link Map} 请求返回的结果
|
||||
* @throws Exception 接口执行异常
|
||||
*/
|
||||
@Deprecated
|
||||
public static Map<String, Object> v3Upload(String urlPrefix, String urlSuffix, String mchId, String serialNo, String keyPath, String body, File file) throws Exception {
|
||||
return v3Upload(urlPrefix, urlSuffix, mchId, serialNo, null, keyPath, body, file);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发放企业红包
|
||||
*
|
||||
* @param params 请求参数
|
||||
* @param certPath 证书文件路径
|
||||
* @param certPass 证书密码
|
||||
* @return {@link String} 请求返回的结果
|
||||
*/
|
||||
public static String sendWorkWxRedPack(Map<String, String> params, String certPath, String certPass) {
|
||||
return execution(getReqUrl(WechatApiEnum.SEND_WORK_WX_RED_PACK), params, certPath, certPass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发放企业红包
|
||||
*
|
||||
* @param params 请求参数
|
||||
* @param certFile 证书文件的 InputStream
|
||||
* @param certPass 证书密码
|
||||
* @return {@link String} 请求返回的结果
|
||||
*/
|
||||
public static String sendWorkWxRedPack(Map<String, String> params, InputStream certFile, String certPass) {
|
||||
return execution(getReqUrl(WechatApiEnum.SEND_WORK_WX_RED_PACK), params, certFile, certPass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询向员工付款记录
|
||||
*
|
||||
* @param params 请求参数
|
||||
* @param certPath 证书文件路径
|
||||
* @param certPass 证书密码
|
||||
* @return {@link String} 请求返回的结果
|
||||
*/
|
||||
public static String queryWorkWxRedPack(Map<String, String> params, String certPath, String certPass) {
|
||||
return execution(getReqUrl(WechatApiEnum.QUERY_WORK_WX_RED_PACK), params, certPath, certPass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询向员工付款记录
|
||||
*
|
||||
* @param params 请求参数
|
||||
* @param certFile 证书文件的 InputStream
|
||||
* @param certPass 证书密码
|
||||
* @return {@link String} 请求返回的结果
|
||||
*/
|
||||
public static String queryWorkWxRedPack(Map<String, String> params, InputStream certFile, String certPass) {
|
||||
return execution(getReqUrl(WechatApiEnum.QUERY_WORK_WX_RED_PACK), params, certFile, certPass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 向员工付款
|
||||
*
|
||||
* @param params 请求参数
|
||||
* @param certPath 证书文件路径
|
||||
* @param certPass 证书密码
|
||||
* @return {@link String} 请求返回的结果
|
||||
*/
|
||||
public static String trans2pocket(Map<String, String> params, String certPath, String certPass) {
|
||||
return execution(getReqUrl(WechatApiEnum.PAY_WWS_TRANS_2_POCKET), params, certPath, certPass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 向员工付款
|
||||
*
|
||||
* @param params 请求参数
|
||||
* @param certFile 证书文件的 InputStream
|
||||
* @param certPass 证书密码
|
||||
* @return {@link String} 请求返回的结果
|
||||
*/
|
||||
public static String trans2pocket(Map<String, String> params, InputStream certFile, String certPass) {
|
||||
return execution(getReqUrl(WechatApiEnum.PAY_WWS_TRANS_2_POCKET), params, certFile, certPass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询向员工付款记录
|
||||
*
|
||||
* @param params 请求参数
|
||||
* @param certPath 证书文件路径
|
||||
* @param certPass 证书密码
|
||||
* @return {@link String} 请求返回的结果
|
||||
*/
|
||||
public static String queryTrans2pocket(Map<String, String> params, String certPath, String certPass) {
|
||||
return execution(getReqUrl(WechatApiEnum.QUERY_WWS_TRANS_2_POCKET), params, certPath, certPass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询向员工付款记录
|
||||
*
|
||||
* @param params 请求参数
|
||||
* @param certFile 证书文件的 InputStream
|
||||
* @param certPass 证书密码
|
||||
* @return {@link String} 请求返回的结果
|
||||
*/
|
||||
public static String queryTrans2pocket(Map<String, String> params, InputStream certFile, String certPass) {
|
||||
return execution(getReqUrl(WechatApiEnum.QUERY_WWS_TRANS_2_POCKET), params, certFile, certPass);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param url 请求url
|
||||
* @param params 请求参数
|
||||
* @return {@link String} 请求返回的结果
|
||||
*/
|
||||
public static String doGet(String url, Map<String, Object> params) {
|
||||
return HttpKit.getDelegate().get(url, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* get 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param params 请求参数
|
||||
* @param headers 请求头
|
||||
* @return {@link PaymentHttpResponse} 请求返回的结果
|
||||
*/
|
||||
public static PaymentHttpResponse get(String url, Map<String, Object> params, Map<String, String> headers) {
|
||||
return HttpKit.getDelegate().get(url, params, headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* get 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param authorization 授权信息
|
||||
* @param serialNumber 公钥证书序列号
|
||||
* @param params 请求参数
|
||||
* @return {@link PaymentHttpResponse} 请求返回的结果
|
||||
*/
|
||||
public static PaymentHttpResponse get(String url, String authorization, String serialNumber, Map<String, Object> params) {
|
||||
return get(url, params, getHeaders(authorization, serialNumber));
|
||||
}
|
||||
|
||||
/**
|
||||
* post 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param data 请求参数
|
||||
* @param headers 请求头
|
||||
* @return {@link PaymentHttpResponse} 请求返回的结果
|
||||
*/
|
||||
public static PaymentHttpResponse post(String url, String data, Map<String, String> headers) {
|
||||
return HttpKit.getDelegate().post(url, data, headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* post 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param authorization 授权信息
|
||||
* @param serialNumber 公钥证书序列号
|
||||
* @param data 请求参数
|
||||
* @return {@link PaymentHttpResponse} 请求返回的结果
|
||||
*/
|
||||
public static PaymentHttpResponse post(String url, String authorization, String serialNumber, String data) {
|
||||
return post(url, data, getHeaders(authorization, serialNumber));
|
||||
}
|
||||
|
||||
/**
|
||||
* delete 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param data 请求参数
|
||||
* @param headers 请求头
|
||||
* @return {@link PaymentHttpResponse} 请求返回的结果
|
||||
*/
|
||||
public static PaymentHttpResponse delete(String url, String data, Map<String, String> headers) {
|
||||
return HttpKit.getDelegate().delete(url, data, headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* delete 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param authorization 授权信息
|
||||
* @param serialNumber 公钥证书序列号
|
||||
* @param data 请求参数
|
||||
* @return {@link PaymentHttpResponse} 请求返回的结果
|
||||
*/
|
||||
public static PaymentHttpResponse delete(String url, String authorization, String serialNumber, String data) {
|
||||
return delete(url, data, getHeaders(authorization, serialNumber));
|
||||
}
|
||||
|
||||
/**
|
||||
* upload 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param params 请求参数
|
||||
* @param headers 请求头
|
||||
* @return {@link PaymentHttpResponse} 请求返回的结果
|
||||
*/
|
||||
public static PaymentHttpResponse upload(String url, Map<String, Object> params, Map<String, String> headers) {
|
||||
return HttpKit.getDelegate().post(url, params, headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* upload 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param authorization 授权信息
|
||||
* @param serialNumber 公钥证书序列号
|
||||
* @param data 请求参数
|
||||
* @param file 上传文件
|
||||
* @return {@link PaymentHttpResponse} 请求返回的结果
|
||||
*/
|
||||
public static PaymentHttpResponse upload(String url, String authorization, String serialNumber, String data, File file) {
|
||||
Map<String, Object> paramMap = new HashMap<>(2);
|
||||
paramMap.put("file", file);
|
||||
paramMap.put("meta", data);
|
||||
return upload(url, paramMap, getUploadHeaders(authorization, serialNumber));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* put 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param data 请求参数
|
||||
* @param headers 请求头
|
||||
* @return {@link PaymentHttpResponse} 请求返回的结果
|
||||
*/
|
||||
public static PaymentHttpResponse put(String url, String data, Map<String, String> headers) {
|
||||
return HttpKit.getDelegate().put(url, data, headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* put 请求
|
||||
*
|
||||
* @param url 请求url
|
||||
* @param authorization 授权信息
|
||||
* @param serialNumber 公钥证书序列号
|
||||
* @param data 请求参数
|
||||
* @return {@link PaymentHttpResponse} 请求返回的结果
|
||||
*/
|
||||
public static PaymentHttpResponse put(String url, String authorization, String serialNumber, String data) {
|
||||
return put(url, data, getHeaders(authorization, serialNumber));
|
||||
}
|
||||
|
||||
public static String doPost(String url, Map<String, String> params) {
|
||||
return HttpKit.getDelegate().post(url, WxPayKit.toXml(params));
|
||||
}
|
||||
|
||||
public static String doPostSsl(String url, Map<String, String> params, String certPath, String certPass) {
|
||||
return HttpKit.getDelegate().post(url, WxPayKit.toXml(params), certPath, certPass);
|
||||
}
|
||||
|
||||
public static String doPostSsl(String url, Map<String, String> params, InputStream certFile, String certPass) {
|
||||
return HttpKit.getDelegate().post(url, WxPayKit.toXml(params), certFile, certPass);
|
||||
}
|
||||
|
||||
public static String doPostSsl(String url, Map<String, String> params, String certPath) {
|
||||
if (params.isEmpty() || !params.containsKey("mch_id")) {
|
||||
throw new RuntimeException("请求参数中必须包含 mch_id,如接口参考中不包 mch_id, 请使用其他同名构造方法。");
|
||||
}
|
||||
String certPass = params.get("mch_id");
|
||||
return doPostSsl(url, params, certPath, certPass);
|
||||
}
|
||||
|
||||
public static String doPostSsl(String url, Map<String, String> params, InputStream certFile) {
|
||||
if (params.isEmpty() || !params.containsKey("mch_id")) {
|
||||
throw new RuntimeException("请求参数中必须包含 mch_id,如接口参考中不包 mch_id, 请使用其他同名构造方法。");
|
||||
}
|
||||
String certPass = params.get("mch_id");
|
||||
return doPostSsl(url, params, certFile, certPass);
|
||||
}
|
||||
|
||||
public static String doUploadSsl(String url, Map<String, String> params, String certPath, String certPass, String filePath) {
|
||||
return HttpKit.getDelegate().upload(url, WxPayKit.toXml(params), certPath, certPass, filePath);
|
||||
}
|
||||
|
||||
public static String doUploadSsl(String url, Map<String, String> params, String certPath, String filePath) {
|
||||
if (params.isEmpty() || !params.containsKey("mch_id")) {
|
||||
throw new RuntimeException("请求参数中必须包含 mch_id,如接口参考中不包 mch_id, 请使用其他同名构造方法。");
|
||||
}
|
||||
String certPass = params.get("mch_id");
|
||||
return doUploadSsl(url, params, certPath, certPass, filePath);
|
||||
}
|
||||
|
||||
|
||||
public static Map<String, String> getBaseHeaders(String authorization) {
|
||||
Map<String, String> headers = new HashMap<>(5);
|
||||
headers.put("Accept", ContentType.JSON.toString());
|
||||
headers.put("Authorization", authorization);
|
||||
return headers;
|
||||
}
|
||||
|
||||
public static Map<String, String> getHeaders(String authorization, String serialNumber) {
|
||||
Map<String, String> headers = getBaseHeaders(authorization);
|
||||
headers.put("Content-Type", ContentType.JSON.toString());
|
||||
if (StrUtil.isNotEmpty(serialNumber)) {
|
||||
headers.put("Wechatpay-Serial", serialNumber);
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
public static Map<String, String> getUploadHeaders(String authorization, String serialNumber) {
|
||||
Map<String, String> headers = getBaseHeaders(authorization);
|
||||
headers.put("Content-Type", "multipart/form-data;boundary=\"boundary\"");
|
||||
if (StrUtil.isNotEmpty(serialNumber)) {
|
||||
headers.put("Wechatpay-Serial", serialNumber);
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建返回参数
|
||||
*
|
||||
* @param response {@link PaymentHttpResponse}
|
||||
* @return {@link Map}
|
||||
*/
|
||||
public static Map<String, Object> buildResMap(PaymentHttpResponse response) {
|
||||
if (response == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> map = new HashMap<>(6);
|
||||
String timestamp = response.getHeader("Wechatpay-Timestamp");
|
||||
String nonceStr = response.getHeader("Wechatpay-Nonce");
|
||||
String serialNo = response.getHeader("Wechatpay-Serial");
|
||||
String signature = response.getHeader("Wechatpay-Signature");
|
||||
String body = response.getBody();
|
||||
int status = response.getStatus();
|
||||
map.put("timestamp", timestamp);
|
||||
map.put("nonceStr", nonceStr);
|
||||
map.put("serialNumber", serialNo);
|
||||
map.put("signature", signature);
|
||||
map.put("body", body);
|
||||
map.put("status", status);
|
||||
return map;
|
||||
}
|
||||
}
|
@ -0,0 +1,694 @@
|
||||
package com.wzj.soopin.transaction.kit.plugin.wechat;
|
||||
|
||||
import cn.hutool.cache.Cache;
|
||||
import cn.hutool.core.net.URLDecoder;
|
||||
import cn.hutool.core.net.URLEncoder;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.wzj.soopin.transaction.domain.po.RefundLog;
|
||||
import com.wzj.soopin.transaction.enums.PaymentMethodEnum;
|
||||
import com.wzj.soopin.transaction.kit.CashierSupport;
|
||||
import com.wzj.soopin.transaction.kit.Payment;
|
||||
import com.wzj.soopin.transaction.kit.core.PaymentHttpResponse;
|
||||
import com.wzj.soopin.transaction.kit.core.enums.RequestMethodEnums;
|
||||
import com.wzj.soopin.transaction.kit.core.enums.SignType;
|
||||
import com.wzj.soopin.transaction.kit.core.kit.HttpKit;
|
||||
import com.wzj.soopin.transaction.kit.core.kit.IpKit;
|
||||
import com.wzj.soopin.transaction.kit.core.kit.WxPayKit;
|
||||
import com.wzj.soopin.transaction.kit.core.utils.DateTimeZoneUtil;
|
||||
import com.wzj.soopin.transaction.kit.dto.PayParam;
|
||||
import com.wzj.soopin.transaction.kit.dto.PaymentSuccessParams;
|
||||
import com.wzj.soopin.transaction.kit.params.dto.CashierParam;
|
||||
import com.wzj.soopin.transaction.kit.plugin.wechat.enums.WechatApiEnum;
|
||||
import com.wzj.soopin.transaction.kit.plugin.wechat.enums.WechatDomain;
|
||||
import com.wzj.soopin.transaction.kit.plugin.wechat.model.*;
|
||||
import com.wzj.soopin.transaction.service.PaymentService;
|
||||
import com.wzj.soopin.transaction.service.RefundLogService;
|
||||
import com.wzj.soopin.transaction.util.CurrencyUtil;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.constant.ResultCode;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 微信支付
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020/12/21 17:44
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class WechatPlugin implements Payment {
|
||||
|
||||
/**
|
||||
* 收银台
|
||||
*/
|
||||
@Autowired
|
||||
|
||||
private CashierSupport cashierSupport;
|
||||
// /**
|
||||
// * 支付日志
|
||||
// */
|
||||
// @Autowired
|
||||
// private PaymentService paymentService;
|
||||
// /**
|
||||
// * 缓存
|
||||
// */
|
||||
// @Autowired
|
||||
// private Cache<String> cache;
|
||||
// /**
|
||||
// * 退款日志
|
||||
// */
|
||||
// @Autowired
|
||||
// private RefundLogService refundLogService;
|
||||
/**
|
||||
* API域名
|
||||
*/
|
||||
// @Autowired
|
||||
// private ApiProperties apiProperties;
|
||||
// /**
|
||||
// * 配置
|
||||
// */
|
||||
// @Autowired
|
||||
// private SettingService settingService;
|
||||
// /**
|
||||
// * 联合登陆
|
||||
// */
|
||||
// @Autowired
|
||||
// private ConnectService connectService;
|
||||
// /**
|
||||
// * 联合登陆
|
||||
// */
|
||||
// @Autowired
|
||||
// private OrderService orderService;
|
||||
|
||||
|
||||
@Override
|
||||
public R<Object> h5pay(HttpServletRequest request, HttpServletResponse response1, PayParam payParam) {
|
||||
|
||||
try {
|
||||
// CashierParam cashierParam = cashierSupport.cashierParam(payParam);
|
||||
//
|
||||
// //支付参数准备
|
||||
// SceneInfo sceneInfo = new SceneInfo();
|
||||
// sceneInfo.setPayer_client_ip(IpKit.getRealIp(request));
|
||||
// H5Info h5Info = new H5Info();
|
||||
// h5Info.setType("WAP");
|
||||
// sceneInfo.setH5_info(h5Info);
|
||||
//
|
||||
// //支付金额
|
||||
// Integer fen = CurrencyUtil.fen(cashierParam.getPrice());
|
||||
// //第三方付款订单
|
||||
// String outOrderNo = SnowFlake.getIdStr();
|
||||
// //过期时间
|
||||
// String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
|
||||
//
|
||||
// //回传数据
|
||||
// String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8);
|
||||
//
|
||||
//
|
||||
// WechatPaymentSetting setting = wechatPaymentSetting();
|
||||
// String appid = setting.getServiceAppId();
|
||||
// if (appid == null) {
|
||||
// throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
|
||||
// }
|
||||
// UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
|
||||
// .setAppid(appid)
|
||||
// .setMchid(setting.getMchId())
|
||||
// .setDescription(cashierParam.getDetail())
|
||||
// .setOut_trade_no(outOrderNo)
|
||||
// .setTime_expire(timeExpire)
|
||||
// .setAttach(attach)
|
||||
// .setNotify_url(notifyUrl(apiProperties.getBuyer(), PaymentMethodEnum.WECHAT))
|
||||
// .setAmount(new Amount().setTotal(fen)).setScene_info(sceneInfo);
|
||||
//
|
||||
// log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
|
||||
// PaymentHttpResponse response = WechatApi.v3(
|
||||
// RequestMethodEnums.POST,
|
||||
// WechatDomain.CHINA.toString(),
|
||||
// WechatApiEnum.H5_PAY.toString(),
|
||||
// setting.getMchId(),
|
||||
// setting.getSerialNumber(),
|
||||
// null,
|
||||
// setting.getApiclient_key(),
|
||||
// JSONUtil.toJsonStr(unifiedOrderModel)
|
||||
// );
|
||||
|
||||
return null;
|
||||
// return R.ok(JSONUtil.toJsonStr(response.getBody()));
|
||||
} catch (Exception e) {
|
||||
log.error("微信H5支付错误", e);
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<Object> jsApiPay(HttpServletRequest request, PayParam payParam) {
|
||||
|
||||
try {
|
||||
// Connect connect = connectService.queryConnect(
|
||||
// ConnectQueryDTO.builder().userId(UserContext.getCurrentUser().getId()).unionType(ConnectEnum.WECHAT.name()).build()
|
||||
// );
|
||||
// if (connect == null) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// Payer payer = new Payer();
|
||||
// payer.setOpenid(connect.getUnionId());
|
||||
//
|
||||
// CashierParam cashierParam = cashierSupport.cashierParam(payParam);
|
||||
//
|
||||
// //支付金额
|
||||
// Integer fen = CurrencyUtil.fen(cashierParam.getPrice());
|
||||
// //第三方付款订单
|
||||
// String outOrderNo = SnowFlake.getIdStr();
|
||||
// //过期时间
|
||||
// String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
|
||||
//
|
||||
// String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8);
|
||||
//
|
||||
// WechatPaymentSetting setting = wechatPaymentSetting();
|
||||
// String appid = setting.getServiceAppId();
|
||||
// if (appid == null) {
|
||||
// throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
|
||||
// }
|
||||
// UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
|
||||
// .setAppid(appid)
|
||||
// .setMchid(setting.getMchId())
|
||||
// .setDescription(cashierParam.getDetail())
|
||||
// .setOut_trade_no(outOrderNo)
|
||||
// .setTime_expire(timeExpire)
|
||||
// .setAttach(attach)
|
||||
// .setNotify_url(notifyUrl(apiProperties.getBuyer(), PaymentMethodEnum.WECHAT))
|
||||
// .setAmount(new Amount().setTotal(fen))
|
||||
// .setPayer(payer);
|
||||
//
|
||||
// log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
|
||||
// PaymentHttpResponse response = WechatApi.v3(
|
||||
// RequestMethodEnums.POST,
|
||||
// WechatDomain.CHINA.toString(),
|
||||
// WechatApiEnum.JS_API_PAY.toString(),
|
||||
// setting.getMchId(),
|
||||
// setting.getSerialNumber(),
|
||||
// null,
|
||||
// setting.getApiclient_key(),
|
||||
// JSONUtil.toJsonStr(unifiedOrderModel)
|
||||
// );
|
||||
// //根据证书序列号查询对应的证书来验证签名结果
|
||||
// boolean verifySignature = WxPayKit.verifySignature(response, getPlatformCert());
|
||||
// log.info("verifySignature: {}", verifySignature);
|
||||
// log.info("统一下单响应 {}", response);
|
||||
//
|
||||
// if (verifySignature) {
|
||||
// String body = response.getBody();
|
||||
// JSONObject jsonObject = JSONUtil.parseObj(body);
|
||||
// String prepayId = jsonObject.getStr("prepay_id");
|
||||
// Map<String, String> map = WxPayKit.jsApiCreateSign(appid, prepayId, setting.getApiclient_key());
|
||||
// log.info("唤起支付参数:{}", map);
|
||||
//
|
||||
// return ResultUtil.data(map);
|
||||
// }
|
||||
log.error("微信支付参数验证错误,请及时处理");
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
} catch (Exception e) {
|
||||
log.error("支付异常", e);
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<Object> appPay(HttpServletRequest request, PayParam payParam) {
|
||||
|
||||
try {
|
||||
|
||||
CashierParam cashierParam = cashierSupport.cashierParam(payParam);
|
||||
|
||||
//支付金额
|
||||
// Integer fen = CurrencyUtil.fen(cashierParam.getPrice());
|
||||
// //第三方付款订单
|
||||
// String outOrderNo = SnowFlake.getIdStr();
|
||||
// //过期时间
|
||||
// String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
|
||||
//
|
||||
// String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8);
|
||||
//
|
||||
// WechatPaymentSetting setting = wechatPaymentSetting();
|
||||
// String appid = setting.getAppId();
|
||||
// if (appid == null) {
|
||||
// throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
|
||||
// }
|
||||
// UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
|
||||
// .setAppid(appid)
|
||||
// .setMchid(setting.getMchId())
|
||||
// .setDescription(cashierParam.getDetail())
|
||||
// .setOut_trade_no(outOrderNo)
|
||||
// .setTime_expire(timeExpire)
|
||||
// .setAttach(attach)
|
||||
// .setNotify_url(notifyUrl(apiProperties.getBuyer(), PaymentMethodEnum.WECHAT))
|
||||
// .setAmount(new Amount().setTotal(fen));
|
||||
//
|
||||
//
|
||||
// log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
|
||||
// PaymentHttpResponse response = WechatApi.v3(
|
||||
// RequestMethodEnums.POST,
|
||||
// WechatDomain.CHINA.toString(),
|
||||
// WechatApiEnum.APP_PAY.toString(),
|
||||
// setting.getMchId(),
|
||||
// setting.getSerialNumber(),
|
||||
// null,
|
||||
// setting.getApiclient_key(),
|
||||
// JSONUtil.toJsonStr(unifiedOrderModel)
|
||||
// );
|
||||
// //根据证书序列号查询对应的证书来验证签名结果
|
||||
// boolean verifySignature = WxPayKit.verifySignature(response, getPlatformCert());
|
||||
// log.info("verifySignature: {}", verifySignature);
|
||||
// log.info("统一下单响应 {}", response);
|
||||
//
|
||||
// if (verifySignature) {
|
||||
// JSONObject jsonObject = JSONUtil.parseObj(response.getBody());
|
||||
// String prepayId = jsonObject.getStr("prepay_id");
|
||||
// Map<String, String> map = WxPayKit.appPrepayIdCreateSign(appid,
|
||||
// setting.getMchId(),
|
||||
// prepayId,
|
||||
// setting.getApiclient_key(), SignType.HMACSHA256);
|
||||
// log.info("唤起支付参数:{}", map);
|
||||
//
|
||||
// return R.ok(map);
|
||||
// }
|
||||
log.error("微信支付参数验证错误,请及时处理");
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
} catch (Exception e) {
|
||||
log.error("支付异常", e);
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<Object> nativePay(HttpServletRequest request, PayParam payParam) {
|
||||
|
||||
try {
|
||||
|
||||
// CashierParam cashierParam = cashierSupport.cashierParam(payParam);
|
||||
//
|
||||
// //支付金额
|
||||
// Integer fen = CurrencyUtil.fen(cashierParam.getPrice());
|
||||
// //第三方付款订单
|
||||
// String outOrderNo = SnowFlake.getIdStr();
|
||||
// //过期时间
|
||||
// String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
|
||||
//
|
||||
// String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8);
|
||||
//
|
||||
// WechatPaymentSetting setting = wechatPaymentSetting();
|
||||
//
|
||||
// String appid = setting.getServiceAppId();
|
||||
// if (appid == null) {
|
||||
// throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
|
||||
// }
|
||||
// UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
|
||||
// .setAppid(appid)
|
||||
// .setMchid(setting.getMchId())
|
||||
// .setDescription(cashierParam.getDetail())
|
||||
// .setOut_trade_no(outOrderNo)
|
||||
// .setTime_expire(timeExpire)
|
||||
// //回传参数
|
||||
// .setAttach(attach)
|
||||
// .setNotify_url(notifyUrl(apiProperties.getBuyer(), PaymentMethodEnum.WECHAT))
|
||||
// .setAmount(new Amount().setTotal(fen));
|
||||
//
|
||||
// log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
|
||||
// PaymentHttpResponse response = WechatApi.v3(
|
||||
// RequestMethodEnums.POST,
|
||||
// WechatDomain.CHINA.toString(),
|
||||
// WechatApiEnum.NATIVE_PAY.toString(),
|
||||
// setting.getMchId(),
|
||||
// setting.getSerialNumber(),
|
||||
// null,
|
||||
// setting.getApiclient_key(),
|
||||
// JSONUtil.toJsonStr(unifiedOrderModel)
|
||||
// );
|
||||
// log.info("统一下单响应 {}", response);
|
||||
// //根据证书序列号查询对应的证书来验证签名结果
|
||||
// boolean verifySignature = WxPayKit.verifySignature(response, getPlatformCert());
|
||||
// log.info("verifySignature: {}", verifySignature);
|
||||
//
|
||||
// if (verifySignature) {
|
||||
// return R.ok(new JSONObject(response.getBody()).getStr("code_url"));
|
||||
// } else {
|
||||
// log.error("微信支付参数验证错误,请及时处理");
|
||||
// throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
// }
|
||||
return null;
|
||||
} catch (ServiceException e) {
|
||||
log.error("支付异常", e);
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
} catch (Exception e) {
|
||||
log.error("支付异常", e);
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<Object> mpPay(HttpServletRequest request, PayParam payParam) {
|
||||
|
||||
try {
|
||||
// Connect connect = connectService.queryConnect(
|
||||
// ConnectQueryDTO.builder().userId(UserContext.getCurrentUser().getId()).unionType(ConnectEnum.WECHAT_MP_OPEN_ID.name()).build()
|
||||
// );
|
||||
// if (connect == null) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// Payer payer = new Payer();
|
||||
// payer.setOpenid(connect.getUnionId());
|
||||
//
|
||||
// CashierParam cashierParam = cashierSupport.cashierParam(payParam);
|
||||
//
|
||||
// //支付金额
|
||||
// Integer fen = CurrencyUtil.fen(cashierParam.getPrice());
|
||||
// //第三方付款订单
|
||||
// String outOrderNo = SnowFlake.getIdStr();
|
||||
// //过期时间
|
||||
// String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
|
||||
//
|
||||
// //微信小程序,appid 需要单独获取,这里读取了联合登陆配置的appid ,实际场景小程序自动登录,所以这个appid是最为保险的做法
|
||||
// //如果有2开需求,这里需要调整,修改这个appid的获取途径即可
|
||||
// String appid = wechatPaymentSetting().getMpAppId();
|
||||
// if (StringUtils.isEmpty(appid)) {
|
||||
// throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
|
||||
// }
|
||||
// String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8);
|
||||
//
|
||||
// WechatPaymentSetting setting = wechatPaymentSetting();
|
||||
// UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
|
||||
// .setAppid(appid)
|
||||
// .setMchid(setting.getMchId())
|
||||
// .setDescription(cashierParam.getDetail())
|
||||
// .setOut_trade_no(outOrderNo)
|
||||
// .setTime_expire(timeExpire)
|
||||
// .setAttach(attach)
|
||||
// .setNotify_url(notifyUrl(apiProperties.getBuyer(), PaymentMethodEnum.WECHAT))
|
||||
// .setAmount(new Amount().setTotal(fen))
|
||||
// .setPayer(payer);
|
||||
//
|
||||
// log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
|
||||
// PaymentHttpResponse response = WechatApi.v3(
|
||||
// RequestMethodEnums.POST,
|
||||
// WechatDomain.CHINA.toString(),
|
||||
// WechatApiEnum.JS_API_PAY.toString(),
|
||||
// setting.getMchId(),
|
||||
// setting.getSerialNumber(),
|
||||
// null,
|
||||
// setting.getApiclient_key(),
|
||||
// JSONUtil.toJsonStr(unifiedOrderModel)
|
||||
// );
|
||||
// //根据证书序列号查询对应的证书来验证签名结果
|
||||
// boolean verifySignature = WxPayKit.verifySignature(response, getPlatformCert());
|
||||
// log.info("verifySignature: {}", verifySignature);
|
||||
// log.info("统一下单响应 {}", response);
|
||||
//
|
||||
// if (verifySignature) {
|
||||
// String body = response.getBody();
|
||||
// JSONObject jsonObject = JSONUtil.parseObj(body);
|
||||
// String prepayId = jsonObject.getStr("prepay_id");
|
||||
// Map<String, String> map = WxPayKit.jsApiCreateSign(appid, prepayId, setting.getApiclient_key());
|
||||
// log.info("唤起支付参数:{}", map);
|
||||
//
|
||||
// return R.ok(map);
|
||||
// }
|
||||
log.error("微信支付参数验证错误,请及时处理");
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
} catch (Exception e) {
|
||||
log.error("支付异常", e);
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void callBack(HttpServletRequest request) {
|
||||
try {
|
||||
verifyNotify(request);
|
||||
} catch (Exception e) {
|
||||
log.error("支付异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(HttpServletRequest request) {
|
||||
try {
|
||||
verifyNotify(request);
|
||||
} catch (Exception e) {
|
||||
log.error("支付异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证结果,执行支付回调
|
||||
*
|
||||
* @param request
|
||||
* @throws Exception
|
||||
*/
|
||||
private void verifyNotify(HttpServletRequest request) throws Exception {
|
||||
|
||||
String timestamp = request.getHeader("Wechatpay-Timestamp");
|
||||
String nonce = request.getHeader("Wechatpay-Nonce");
|
||||
String serialNo = request.getHeader("Wechatpay-Serial");
|
||||
String signature = request.getHeader("Wechatpay-Signature");
|
||||
|
||||
log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
|
||||
String result = HttpKit.readData(request);
|
||||
log.info("微信支付通知密文 {}", result);
|
||||
|
||||
//校验服务器端响应¬7
|
||||
// String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
|
||||
// setting.getApiKey3(), Objects.requireNonNull(getPlatformCert()));
|
||||
//
|
||||
// log.info("微信支付通知明文 {}", plainText);
|
||||
//
|
||||
// JSONObject jsonObject = JSONUtil.parseObj(plainText);
|
||||
//
|
||||
// String payParamStr = jsonObject.getStr("attach");
|
||||
// String payParamJson = URLDecoder.decode(payParamStr, StandardCharsets.UTF_8);
|
||||
// PayParam payParam = JSONUtil.toBean(payParamJson, PayParam.class);
|
||||
|
||||
|
||||
// String tradeNo = jsonObject.getStr("transaction_id");
|
||||
// Double totalAmount = CurrencyUtil.reversalFen(jsonObject.getJSONObject("amount").getDouble("total"));
|
||||
//
|
||||
// PaymentSuccessParams paymentSuccessParams = new PaymentSuccessParams(
|
||||
// PaymentMethodEnum.WECHAT.name(),
|
||||
// tradeNo,
|
||||
// totalAmount,
|
||||
// payParam
|
||||
// );
|
||||
//
|
||||
// paymentService.success(paymentSuccessParams);
|
||||
// log.info("微信支付回调:支付成功{}", plainText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refund(RefundLog refundLog) {
|
||||
|
||||
try {
|
||||
|
||||
// Amount amount = new Amount().setRefund(CurrencyUtil.fen(refundLog.getTotalAmount()))
|
||||
// .setTotal(CurrencyUtil.fen(orderService.getPaymentTotal(refundLog.getOrderSn())));
|
||||
//
|
||||
// //退款参数准备
|
||||
// RefundModel refundModel = new RefundModel()
|
||||
// .setTransaction_id(refundLog.getPaymentReceivableNo())
|
||||
// .setOut_refund_no(refundLog.getOutOrderNo())
|
||||
// .setReason(refundLog.getRefundReason())
|
||||
// .setAmount(amount)
|
||||
// .setNotify_url(refundNotifyUrl(apiProperties.getBuyer(), PaymentMethodEnum.WECHAT));
|
||||
//
|
||||
// WechatPaymentSetting setting = wechatPaymentSetting();
|
||||
//
|
||||
// log.info("微信退款参数 {}", JSONUtil.toJsonStr(refundModel));
|
||||
// PaymentHttpResponse response = WechatApi.v3(
|
||||
// RequestMethodEnums.POST,
|
||||
// WechatDomain.CHINA.toString(),
|
||||
// WechatApiEnum.DOMESTIC_REFUNDS.toString(),
|
||||
// setting.getMchId(),
|
||||
// setting.getSerialNumber(),
|
||||
// null,
|
||||
// setting.getApiclient_key(),
|
||||
// JSONUtil.toJsonStr(refundModel)
|
||||
// );
|
||||
// log.info("微信退款响应 {}", response);
|
||||
// //退款申请成功
|
||||
// if (response.getStatus() == 200) {
|
||||
// refundLogService.save(refundLog);
|
||||
// } else {
|
||||
// //退款申请失败
|
||||
// refundLog.setErrorMessage(response.getBody());
|
||||
// refundLogService.save(refundLog);
|
||||
// }
|
||||
} catch (Exception e) {
|
||||
log.error("微信退款申请失败", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel(RefundLog refundLog) {
|
||||
this.refund(refundLog);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void refundNotify(HttpServletRequest request) {
|
||||
String timestamp = request.getHeader("Wechatpay-Timestamp");
|
||||
String nonce = request.getHeader("Wechatpay-Nonce");
|
||||
String serialNo = request.getHeader("Wechatpay-Serial");
|
||||
String signature = request.getHeader("Wechatpay-Signature");
|
||||
|
||||
log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
|
||||
String result = HttpKit.readData(request);
|
||||
log.info("微信退款通知密文 {}", result);
|
||||
JSONObject ciphertext = JSONUtil.parseObj(result);
|
||||
|
||||
try { //校验服务器端响应¬
|
||||
// String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
|
||||
// wechatPaymentSetting().getApiKey3(), Objects.requireNonNull(getPlatformCert()));
|
||||
// log.info("微信退款通知明文 {}", plainText);
|
||||
|
||||
// if (("REFUND.SUCCESS").equals(ciphertext.getStr("event_type"))) {
|
||||
// log.info("退款成功 {}", plainText);
|
||||
// //校验服务器端响应
|
||||
// JSONObject jsonObject = JSONUtil.parseObj(plainText);
|
||||
// String transactionId = jsonObject.getStr("transaction_id");
|
||||
// String refundId = jsonObject.getStr("refund_id");
|
||||
//
|
||||
// RefundLog refundLog = refundLogService.getOne(new LambdaQueryWrapper<RefundLog>().eq(RefundLog::getPaymentReceivableNo, transactionId));
|
||||
// if (refundLog != null) {
|
||||
// refundLog.setIsRefund(true);
|
||||
// refundLog.setReceivableNo(refundId);
|
||||
// refundLogService.saveOrUpdate(refundLog);
|
||||
// }
|
||||
//
|
||||
// } else {
|
||||
// log.info("退款失败 {}", plainText);
|
||||
// JSONObject jsonObject = JSONUtil.parseObj(plainText);
|
||||
// String transactionId = jsonObject.getStr("transaction_id");
|
||||
// String refundId = jsonObject.getStr("refund_id");
|
||||
//
|
||||
// RefundLog refundLog = refundLogService.getOne(new LambdaQueryWrapper<RefundLog>().eq(RefundLog::getPaymentReceivableNo, transactionId));
|
||||
// if (refundLog != null) {
|
||||
// refundLog.setReceivableNo(refundId);
|
||||
// refundLog.setErrorMessage(ciphertext.getStr("summary"));
|
||||
// refundLogService.saveOrUpdate(refundLog);
|
||||
// }
|
||||
// }
|
||||
} catch (Exception e) {
|
||||
log.error("微信退款失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 获取微信支付配置
|
||||
// *
|
||||
// * @return
|
||||
// */
|
||||
// private WechatPaymentSetting wechatPaymentSetting() {
|
||||
// try {
|
||||
// Setting systemSetting = settingService.get(SettingEnum.WECHAT_PAYMENT.name());
|
||||
// WechatPaymentSetting wechatPaymentSetting = JSONUtil.toBean(systemSetting.getSettingValue(), WechatPaymentSetting.class);
|
||||
// return wechatPaymentSetting;
|
||||
// } catch (Exception e) {
|
||||
// log.error("微信支付暂不支持", e);
|
||||
// throw new ServiceException(ResultCode.PAY_NOT_SUPPORT);
|
||||
// }
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * 获取平台公钥
|
||||
// *
|
||||
// * @return 平台公钥
|
||||
// */
|
||||
// private X509Certificate getPlatformCert() {
|
||||
// //获取缓存中的平台公钥,如果有则直接返回,否则去微信请求
|
||||
// String publicCert = cache.getString(CachePrefix.WECHAT_PLAT_FORM_CERT.getPrefix());
|
||||
// if (!StringUtils.isEmpty(publicCert)) {
|
||||
// return PayKit.getCertificate(publicCert);
|
||||
// }
|
||||
// //获取平台证书列表
|
||||
// try {
|
||||
//
|
||||
// WechatPaymentSetting setting = wechatPaymentSetting();
|
||||
//
|
||||
// PaymentHttpResponse response = WechatApi.v3(
|
||||
// RequestMethodEnums.GET,
|
||||
// WechatDomain.CHINA.toString(),
|
||||
// WechatApiEnum.GET_CERTIFICATES.toString(),
|
||||
// setting.getMchId(),
|
||||
// setting.getSerialNumber(),
|
||||
// null,
|
||||
// setting.getApiclient_key(),
|
||||
// ""
|
||||
// );
|
||||
// String body = response.getBody();
|
||||
// log.info("获取微信平台证书body: {}", body);
|
||||
//
|
||||
// if (response.getStatus() == 200) {
|
||||
// JSONObject jsonObject = JSONUtil.parseObj(body);
|
||||
// JSONArray dataArray = jsonObject.getJSONArray("data");
|
||||
// log.info("证书信息: {}", dataArray);
|
||||
//
|
||||
// //默认认为只有一个平台证书
|
||||
// JSONObject encryptObject = dataArray.getJSONObject(0);
|
||||
// JSONObject encryptCertificate = encryptObject.getJSONObject("encrypt_certificate");
|
||||
// String associatedData = encryptCertificate.getStr("associated_data");
|
||||
// String cipherText = encryptCertificate.getStr("ciphertext");
|
||||
// String nonce = encryptCertificate.getStr("nonce");
|
||||
// publicCert = getPlatformCertStr(associatedData, nonce, cipherText);
|
||||
// long second = (PayKit.getCertificate(publicCert).getNotAfter().getTime() - System.currentTimeMillis()) / 1000;
|
||||
// cache.put(CachePrefix.WECHAT_PLAT_FORM_CERT.getPrefix(), publicCert, second);
|
||||
// } else {
|
||||
// log.error("证书获取失败:{}" + body);
|
||||
// throw new ServiceException(ResultCode.WECHAT_CERT_ERROR);
|
||||
// }
|
||||
// return PayKit.getCertificate(publicCert);
|
||||
// } catch (Exception e) {
|
||||
// log.error("证书获取失败", e);
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获取平台证书缓存的字符串
|
||||
// * 下列各个密钥参数
|
||||
// *
|
||||
// * @param associatedData 密钥参数
|
||||
// * @param nonce 密钥参数
|
||||
// * @param cipherText 密钥参数
|
||||
// * @return platform key
|
||||
// * @throws GeneralSecurityException 密钥获取异常
|
||||
// */
|
||||
// private String getPlatformCertStr(String associatedData, String nonce, String cipherText) throws GeneralSecurityException {
|
||||
//
|
||||
//
|
||||
// AesUtil aesUtil = new AesUtil(wechatPaymentSetting().getApiKey3().getBytes(StandardCharsets.UTF_8));
|
||||
// //平台证书密文解密
|
||||
// //encrypt_certificate 中的 associated_data nonce ciphertext
|
||||
// return aesUtil.decryptToString(
|
||||
// associatedData.getBytes(StandardCharsets.UTF_8),
|
||||
// nonce.getBytes(StandardCharsets.UTF_8),
|
||||
// cipherText
|
||||
// );
|
||||
// }
|
||||
}
|
||||
|
@ -0,0 +1,577 @@
|
||||
package com.wzj.soopin.transaction.kit.plugin.wechat.enums;
|
||||
|
||||
/**
|
||||
* 微信api列表
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020/12/17 17:43
|
||||
*/
|
||||
public enum WechatApiEnum {
|
||||
|
||||
/**
|
||||
* 沙箱环境
|
||||
*/
|
||||
SAND_BOX_NEW("/sandboxnew"),
|
||||
/**
|
||||
* 获取沙箱环境验签秘钥
|
||||
*/
|
||||
GET_SIGN_KEY("/sandboxnew/pay/getsignkey"),
|
||||
/**
|
||||
* 获取平台证书列表
|
||||
*/
|
||||
GET_CERTIFICATES("/v3/certificates"),
|
||||
|
||||
/**
|
||||
* 营销专用-图片上传
|
||||
*/
|
||||
MARKETING_UPLOAD_MEDIA("/v3/marketing/favor/media/image-upload"),
|
||||
/**
|
||||
* 通用接口-图片上传
|
||||
*/
|
||||
MERCHANT_UPLOAD_MEDIA("/v3/merchant/media/upload"),
|
||||
/**
|
||||
* 通用接口-视频上传
|
||||
*/
|
||||
MERCHANT_VIDEO_UPLOAD("/v3/merchant/media/video_upload"),
|
||||
|
||||
|
||||
/**
|
||||
* 创建/查询支付分订单
|
||||
*/
|
||||
PAY_SCORE_SERVICE_ORDER("/v3/payscore/serviceorder"),
|
||||
/**
|
||||
* 取消支付分订单
|
||||
*/
|
||||
PAY_SCORE_SERVICE_ORDER_CANCEL("/v3/payscore/serviceorder/%s/cancel"),
|
||||
/**
|
||||
* 修改支付分订单金额
|
||||
*/
|
||||
PAY_SCORE_SERVICE_ORDER_MODIFY("/v3/payscore/serviceorder/%s/modify"),
|
||||
/**
|
||||
* 完结支付分订单
|
||||
*/
|
||||
PAY_SCORE_SERVICE_ORDER_COMPLETE("/v3/payscore/serviceorder/%s/complete"),
|
||||
/**
|
||||
* 支付分订单收款
|
||||
*/
|
||||
PAY_SCORE_SERVICE_ORDER_PAY("/v3/payscore/serviceorder/%s/pay"),
|
||||
/**
|
||||
* 同步服务订单信息
|
||||
*/
|
||||
PAY_SCORE_SERVICE_ORDER_SYNC("/v3/payscore/serviceorder/%s/sync"),
|
||||
/**
|
||||
* 查询用户支付分开启状态
|
||||
*/
|
||||
PAY_SCORE_USER_SERVICE_STATE("/v3/payscore/user-service-state"),
|
||||
/**
|
||||
* 商户解除用户授权关系
|
||||
*/
|
||||
PAY_SCORE_PERMISSIONS_TERMINATE("/payscore/users/%s/permissions/%s/terminate"),
|
||||
|
||||
/**
|
||||
* 特约商户进件-提交申请单
|
||||
*/
|
||||
APPLY_4_SUB("/v3/applyment4sub/applyment/"),
|
||||
/**
|
||||
* 特约商户进件-通过业务申请编号查询申请状态
|
||||
*/
|
||||
GET_APPLY_STATE("/v3/applyment4sub/applyment/business_code/%s"),
|
||||
/**
|
||||
* 特约商户进件-通过申请单号查询申请状态
|
||||
*/
|
||||
GET_APPLY_STATE_BY_ID("/v3/applyment4sub/applyment/applyment_id/%s"),
|
||||
/**
|
||||
* 特约商户进件-修改结算帐号
|
||||
*/
|
||||
MODIFY_SETTLEMENT("/v3/apply4sub/sub_merchants/%s/modify-settlement"),
|
||||
/**
|
||||
* 特约商户进件-查询结算账户
|
||||
*/
|
||||
GET_SETTLEMENT("/v3/apply4sub/sub_merchants/%s/settlement"),
|
||||
|
||||
/**
|
||||
* 商户开户意愿确认-提交申请单 OR 查询申请单审核结果
|
||||
*/
|
||||
MER_OPEN_APPLY_SUBMIT_OR_RESULT("/v3/apply4subject/applyment"),
|
||||
/**
|
||||
* 商户开户意愿确认-撤销申请单
|
||||
*/
|
||||
MER_OPEN_APPLY_CANCEL("/v3/apply4subject/applyment/%s/cancel"),
|
||||
/**
|
||||
* 商户开户意愿确认-获取商户开户意愿确认状态
|
||||
*/
|
||||
GET_MER_OPEN_APPLY_STATE("/v3/apply4subject/applyment/merchants/%s/state"),
|
||||
|
||||
/**
|
||||
* 商业支付投诉-查询投诉信息
|
||||
*/
|
||||
MERCHANT_SERVICE_COMPLAINTS("/v3/merchant-service/complaints"),
|
||||
/**
|
||||
* 商业支付投诉-创建/查询/更新/删除投诉通知回调
|
||||
*/
|
||||
MERCHANT_SERVICE_COMPLAINTS_NOTIFICATIONS("/v3/merchant-service/complaint-notifications"),
|
||||
|
||||
|
||||
/**
|
||||
* 代金券-创建代金券批次
|
||||
*/
|
||||
CREATE_COUPON_STOCKS("/v3/marketing/favor/coupon-stocks"),
|
||||
/**
|
||||
* 代金券-激活代金券批次
|
||||
*/
|
||||
START_COUPON_STOCKS("/v3/marketing/favor/stocks/%s/start"),
|
||||
/**
|
||||
* 代金券-发放代金券
|
||||
*/
|
||||
COUPON_SEND("/v3/marketing/favor/users/%s/coupons"),
|
||||
/**
|
||||
* 代金券-暂停代金券批次
|
||||
*/
|
||||
PAUSE_COUPON_STOCKS("/v3/marketing/favor/stocks/%s/pause"),
|
||||
/**
|
||||
* 代金券-重启代金券批次
|
||||
*/
|
||||
RESTART_COUPON_STOCKS("/v3/marketing/favor/stocks/%s/restart"),
|
||||
/**
|
||||
* 代金券-条件查询批次列表
|
||||
*/
|
||||
QUERY_COUPON_STOCKS("/v3/marketing/favor/stocks"),
|
||||
/**
|
||||
* 代金券-查询批次详情
|
||||
*/
|
||||
QUERY_COUPON_STOCKS_INFO("/v3/marketing/favor/stocks/%s"),
|
||||
/**
|
||||
* 代金券-查询代金券详情
|
||||
*/
|
||||
QUERY_COUPON_INFO("/v3/marketing/favor/users/%s/coupons/%s"),
|
||||
/**
|
||||
* 代金券-查询代金券可用商户
|
||||
*/
|
||||
QUERY_COUPON_MERCHANTS("/v3/marketing/favor/stocks/%s/merchants"),
|
||||
/**
|
||||
* 代金券-查询代金券可用单品
|
||||
*/
|
||||
QUERY_COUPON_ITEMS("/v3/marketing/favor/stocks/%s/items"),
|
||||
/**
|
||||
* 代金券-根据商户号查用户的券
|
||||
*/
|
||||
QUERY_USER_COUPON("/v3/marketing/favor/users/%s/coupons"),
|
||||
/**
|
||||
* 代金券-下载批次核销明细
|
||||
*/
|
||||
COUPON_STOCKS_USER_FLOW_DOWNLOAD("/v3/marketing/favor/stocks/%s/use-flow"),
|
||||
/**
|
||||
* 代金券-下载批次退款明细
|
||||
*/
|
||||
COUPON_STOCKS_REFUND_FLOW_DOWNLOAD("/v3/marketing/favor/stocks/%s/refund-flow"),
|
||||
/**
|
||||
* 代金券-设置消息通知地址
|
||||
*/
|
||||
SETTING_COUPON_CALLBACKS("/v3/marketing/favor/callbacks"),
|
||||
/**
|
||||
* 商家券-创建商家券
|
||||
*/
|
||||
CREATE_BUSINESS_COUPON("/v3/marketing/busifavor/stocks"),
|
||||
/**
|
||||
* 发放消费卡
|
||||
*/
|
||||
SEND_BUSINESS_COUPON("/v3/marketing/busifavor/coupons/%s/send"),
|
||||
/**
|
||||
* H5 发券
|
||||
*/
|
||||
H5_SEND_COUPON("/busifavor/getcouponinfo"),
|
||||
/**
|
||||
* 商家券-查询商家券批次详情
|
||||
*/
|
||||
QUERY_BUSINESS_COUPON_STOCKS_INFO("/v3/marketing/busifavor/stocks/%s"),
|
||||
/**
|
||||
* 商家券-查询商家券批次详情
|
||||
*/
|
||||
USE_BUSINESS_COUPON("/v3/marketing/busifavor/coupons/use"),
|
||||
/**
|
||||
* 商家券-根据过滤条件查询用户券
|
||||
*/
|
||||
QUERY_BUSINESS_USER_COUPON("/v3/marketing/busifavor/users/%s/coupons"),
|
||||
/**
|
||||
* 商家券-查询用户单张券详情
|
||||
*/
|
||||
QUERY_BUSINESS_USER_COUPON_INFO("/v3/marketing/busifavor/users/%s/coupons/%s/appids/%s"),
|
||||
/**
|
||||
* 商家券-上传预存code
|
||||
*/
|
||||
BUSINESS_COUPON_UPLOAD_CODE("/v3/marketing/busifavor/stocks/%/couponcodes"),
|
||||
/**
|
||||
* 商家券-设置/查询商家券事件通知地址
|
||||
*/
|
||||
BUSINESS_COUPON_CALLBACKS("/v3/marketing/busifavor/callbacks"),
|
||||
/**
|
||||
* 关联订单信息
|
||||
*/
|
||||
BUSINESS_COUPON_ASSOCIATE("/v3/marketing/busifavor/coupons/associate"),
|
||||
/**
|
||||
* 取消关联订单信息
|
||||
*/
|
||||
BUSINESS_COUPON_DISASSOCIATE("/v3/marketing/busifavor/coupons/disassociate"),
|
||||
|
||||
/**
|
||||
* 支付有礼-创建全场满额送活动
|
||||
*/
|
||||
PAY_GIFT_ACTIVITY("/v3/marketing/paygiftactivity/unique-threshold-activity"),
|
||||
/**
|
||||
* 支付有礼-获取支付有礼活动列表
|
||||
*/
|
||||
PAY_GIFT_ACTIVITY_GET("/v3/marketing/paygiftactivity/activities"),
|
||||
/**
|
||||
* 支付有礼-查询活动详情接口
|
||||
*/
|
||||
PAY_GIFT_ACTIVITY_INFO("/v3/marketing/paygiftactivity/activities/%s"),
|
||||
/**
|
||||
* 支付有礼-查询活动发券商户号
|
||||
*/
|
||||
PAY_GIFT_ACTIVITY_QUERY_MER("/v3/marketing/paygiftactivity/activities/%s/merchants"),
|
||||
/**
|
||||
* 支付有礼-查询活动指定商品列表
|
||||
*/
|
||||
PAY_GIFT_ACTIVITY_QUERY_GOODS("/v3/marketing/paygiftactivity/activities/%s/goods"),
|
||||
/**
|
||||
* 支付有礼-终止活动
|
||||
*/
|
||||
PAY_GIFT_ACTIVITY_TERMINATE("/v3/marketing/paygiftactivity/activities/%s/terminate"),
|
||||
/**
|
||||
* 支付有礼-新增活动发券商户号
|
||||
*/
|
||||
PAY_GIFT_ACTIVITY_ADD_MERCHANTS("/v3/marketing/paygiftactivity/activities/%s/merchants/add"),
|
||||
/**
|
||||
* 支付有礼-删除活动发券商户号
|
||||
*/
|
||||
PAY_GIFT_ACTIVITY_DELETE_MERCHANTS("/v3/marketing/paygiftactivity/activities/%s/merchants/delete"),
|
||||
|
||||
|
||||
/**
|
||||
* 点金计划-点金计划管理
|
||||
*/
|
||||
CHANGE_GOLD_PLAN_STATUS("/v3/goldplan/merchants/changegoldplanstatus"),
|
||||
/**
|
||||
* 点金计划-商家小票管理
|
||||
*/
|
||||
CHANGE_CUSTOM_PAGE_STATUS("/v3/goldplan/merchants/changecustompagestatus"),
|
||||
/**
|
||||
* 点金计划-同业过滤标签管理
|
||||
*/
|
||||
SET_ADVERTISING_INDUSTRY_FILTER("/v3/goldplan/merchants/set-advertising-industry-filter"),
|
||||
|
||||
|
||||
/**
|
||||
* 电商收付通-二级商户进件
|
||||
*/
|
||||
E_COMMERCE_APPLY("/v3/ecommerce/applyments/"),
|
||||
/**
|
||||
* 电商收付通-查询进件申请状态
|
||||
*/
|
||||
E_COMMERCE_APPLY_STATE("/v3/ecommerce/applyments/%s"),
|
||||
/**
|
||||
* 电商收付通-通过业务申请编号查询申请状态
|
||||
*/
|
||||
E_COMMERCE_APPLY_STATE_BY_NO("/v3/ecommerce/applyments/out-request-no/%s"),
|
||||
|
||||
/**
|
||||
* 合单下单-APP支付
|
||||
*/
|
||||
COMBINE_TRANSACTIONS_APP("/v3/combine-transactions/app"),
|
||||
/**
|
||||
* 合单下单-JS支付
|
||||
*/
|
||||
COMBINE_TRANSACTIONS_JS("/v3/combine-transactions/jsapi"),
|
||||
/**
|
||||
* 合单下单-H5支付
|
||||
*/
|
||||
COMBINE_TRANSACTIONS_H5("/v3/combine-transactions/h5"),
|
||||
/**
|
||||
* 合单下单-Native支付
|
||||
*/
|
||||
COMBINE_TRANSACTIONS_NATIVE("/v3/combine-transactions/native"),
|
||||
/**
|
||||
* 合单下单-合单查询订单
|
||||
*/
|
||||
COMBINE_TRANSACTIONS_QUERY("/v3/combine-transactions/out-trade-no/%s"),
|
||||
/**
|
||||
* 合单下单-合单关闭订单
|
||||
*/
|
||||
COMBINE_TRANSACTIONS_CLOSE("/v3/combine-transactions/out-trade-no/%s/close"),
|
||||
|
||||
/**
|
||||
* 电商收付通-补差接口-请求补差
|
||||
*/
|
||||
CREATE_SUBSIDIES("v3/ecommerce/subsidies/create"),
|
||||
/**
|
||||
* 电商收付通-补差接口-请求补差回退
|
||||
*/
|
||||
RETURN_SUBSIDIES("/v3/ecommerce/subsidies/return"),
|
||||
/**
|
||||
* 电商收付通-补差接口-取消补差
|
||||
*/
|
||||
CANCEL_SUBSIDIES("/v3/ecommerce/subsidies/cancel"),
|
||||
/**
|
||||
* 电商收付通-分账接口-请求分账/查询分账结果
|
||||
*/
|
||||
PROFIT_SHARING_ORDERS("/v3/ecommerce/profitsharing/orders"),
|
||||
/**
|
||||
* 电商收付通-分账接口-查询分账回退结果
|
||||
*/
|
||||
PROFIT_SHARING_RETURN_ORDERS("/v3/ecommerce/profitsharing/returnorders"),
|
||||
/**
|
||||
* 电商收付通-分账接口-完结分账
|
||||
*/
|
||||
PROFIT_SHARING_FINISH_ORDER("/v3/ecommerce/profitsharing/finish-order"),
|
||||
/**
|
||||
* 添加分账接收方
|
||||
*/
|
||||
PROFIT_SHARING_RECEIVERS_ADD("/v3/ecommerce/profitsharing/receivers/add"),
|
||||
/**
|
||||
* 删除分账接收方
|
||||
*/
|
||||
PROFIT_SHARING_RECEIVERS_DELETE("/v3/ecommerce/profitsharing/receivers/delete"),
|
||||
/**
|
||||
* 退款接口-退款申请
|
||||
*/
|
||||
DOMESTIC_REFUNDS("/v3/refund/domestic/refunds"),
|
||||
/**
|
||||
* 电商收付通-退款接口-退款申请
|
||||
*/
|
||||
E_COMMERCE_REFUNDS("/v3/ecommerce/refunds/apply"),
|
||||
/**
|
||||
* 电商收付通-退款接口-通过微信支付退款单号查询退款
|
||||
*/
|
||||
QUERY_REFUND("/v3/ecommerce/refunds/id/%s"),
|
||||
/**
|
||||
* 电商收付通-退款接口-通过商户退款单号查询退款
|
||||
*/
|
||||
QUERY_REFUNDS_BY_REFUND_NO("/v3/ecommerce/refunds/out-refund-no/%s"),
|
||||
/**
|
||||
* 电商收付通-余额查询接口
|
||||
*/
|
||||
QUERY_BALANCE("/v3/ecommerce/fund/balance/%s"),
|
||||
/**
|
||||
* 查询二级商户账户日终余额
|
||||
*/
|
||||
QUERY_END_DAY_BALANCE("/v3/ecommerce/fund/enddaybalance/%s"),
|
||||
/**
|
||||
* 查询电商平台账户实时余额
|
||||
*/
|
||||
QUERY_MERCHANT_BALANCE("/v3/merchant/fund/balance/%s"),
|
||||
/**
|
||||
* 查询电商平台账户日终余额
|
||||
*/
|
||||
QUERY_MERCHANT_END_DAY_BALANCE("/v3/merchant/fund/dayendbalance/%s"),
|
||||
/**
|
||||
* 电商收付通-提现接口-账户余额提现
|
||||
*/
|
||||
WITHDRAW("/v3/ecommerce/fund/withdraw"),
|
||||
/**
|
||||
* 电商收付通-提现接口-提现状态查询
|
||||
*/
|
||||
QUERY_WITHDRAW("/v3/ecommerce/fund/withdraw/%s"),
|
||||
/**
|
||||
* 电商收付通-提现接口-商户提现单号查询
|
||||
*/
|
||||
QUERY_WITHDRAW_BY_OUT_REQUEST_NO("/v3/ecommerce/fund/withdraw/out-request-no/%s"),
|
||||
/**
|
||||
* 电商收付通-提现接口-电商平台提现
|
||||
*/
|
||||
MERCHANT_WITHDRAW("/v3/merchant/fund/withdraw"),
|
||||
/**
|
||||
* 电商收付通-提现接口-微信支付提现单号查询
|
||||
*/
|
||||
QUERY_MERCHANT_WITHDRAW("/v3/ecommerce/fund/withdraw/%s"),
|
||||
/**`
|
||||
* 电商收付通-提现接口-商户提现单号查询
|
||||
*/
|
||||
QUERY_MERCHANT_WITHDRAW_BY_OUT_REQUEST_NO("/v3/merchant/fund/withdraw/out-request-no/%s"),
|
||||
/**
|
||||
* 电商收付通-提现接口-按日下载提现异常文件
|
||||
*/
|
||||
WITHDRAW_BILL("/v3/merchant/fund/withdraw/bill-type/%s"),
|
||||
/**
|
||||
* 申请交易账单
|
||||
*/
|
||||
TRADE_BILL("/v3/bill/tradebill"),
|
||||
/**
|
||||
* 申请资金账单
|
||||
*/
|
||||
FUND_FLOW_BILL("/v3/bill/fundflowbill"),
|
||||
/**
|
||||
* 下载账单
|
||||
*/
|
||||
BILL_DOWNLOAD("/v3/billdownload/file"),
|
||||
|
||||
/**
|
||||
* 银行特约商户违规信息查询
|
||||
*/
|
||||
GET_VIOLATION("/risk/getviolation"),
|
||||
/**
|
||||
* 事前-风险商户核查接口
|
||||
*/
|
||||
QUERY_MCH_RISK("/mchrisk/querymchrisk"),
|
||||
/**
|
||||
* 事后-风险商户处理结果同步接口
|
||||
*/
|
||||
SYNC_MCH_RISK_RESULT("/mchrisk/syncmchriskresult"),
|
||||
/**
|
||||
* 间联模式查询商户审核状态开放接口
|
||||
*/
|
||||
BANK_QUERY_MCH_AUDIT_INFO("/mchrisk/bankquerymchauditinfo"),
|
||||
/**
|
||||
* 渠道商查询商户审核信息
|
||||
*/
|
||||
CHANNEL_QUERY_MCH_AUDIT_INFO("/mchrisk/channelquerymchauditinfo"),
|
||||
/**
|
||||
* 设置风险通知回调链接
|
||||
*/
|
||||
SET_MCH_RISK_CALLBACK("/mchrisk/setmchriskcallback"),
|
||||
|
||||
/**
|
||||
* 向员工付款
|
||||
*/
|
||||
PAY_WWS_TRANS_2_POCKET("/mmpaymkttransfers/promotion/paywwsptrans2pocket"),
|
||||
/**
|
||||
* 查询向员工付款记录
|
||||
*/
|
||||
QUERY_WWS_TRANS_2_POCKET("/mmpaymkttransfers/promotion/querywwsptrans2pocket"),
|
||||
/**
|
||||
* 发放企业红包
|
||||
*/
|
||||
SEND_WORK_WX_RED_PACK("/mmpaymkttransfers/sendworkwxredpack"),
|
||||
/**
|
||||
* 查询企业红包记录
|
||||
*/
|
||||
QUERY_WORK_WX_RED_PACK("/mmpaymkttransfers/queryworkwxredpack"),
|
||||
|
||||
/**
|
||||
* 查询/更新先享卡订单
|
||||
*/
|
||||
DISCOUNT_CARD_ORDER("/v3/discount-card/orders/%s"),
|
||||
/**
|
||||
* 查询先享卡订单
|
||||
*/
|
||||
DISCOUNT_CARD_ORDER_TRADE_NO("/v3/discount-card/orders/out-trade-no/%s"),
|
||||
|
||||
|
||||
/**
|
||||
* 服务人员注册
|
||||
*/
|
||||
SMART_GUIDE_GUIDES("/v3/smartguide/guides"),
|
||||
/**
|
||||
* 服务人员分配
|
||||
*/
|
||||
SMART_GUIDE_GUIDES_ASSIGN("/v3/smartguide/guides/%s/assign"),
|
||||
/**
|
||||
* 服务人员信息更新
|
||||
*/
|
||||
SMART_GUIDE_GUIDES_UPDATE("/v3/smartguide/guides/%s"),
|
||||
|
||||
/**
|
||||
* 报关接口-订单附加信息提交接口
|
||||
*/
|
||||
CUSTOM_DECLARE_ORDER("/cgi-bin/mch/customs/customdeclareorder"),
|
||||
/**
|
||||
* 报关接口-订单附加信息查询接口
|
||||
*/
|
||||
CUSTOM_DECLARE_QUERY("/cgi-bin/mch/customs/customdeclarequery"),
|
||||
/**
|
||||
* 报关接口-订单附加信息重推接口
|
||||
*/
|
||||
CUSTOM_DECLARE_RE_DECLARE("/cgi-bin/mch/newcustoms/customdeclareredeclare"),
|
||||
|
||||
/**
|
||||
* APP 下单 API
|
||||
*/
|
||||
APP_PAY("/v3/pay/transactions/app"),
|
||||
PARTNER_APP_PAY("/v3/pay/partner/transactions/app"),
|
||||
/**
|
||||
* JS API 下单 API
|
||||
*/
|
||||
JS_API_PAY("/v3/pay/transactions/jsapi"),
|
||||
PARTNER_JS_API_PAY("/v3/pay/partner/transactions/jsapi"),
|
||||
/**
|
||||
* Native 下单 API
|
||||
*/
|
||||
NATIVE_PAY("/v3/pay/transactions/native"),
|
||||
PARTNER_NATIVE_PAY("/v3/pay/partner/transactions/native"),
|
||||
/**
|
||||
* H5 下单 API
|
||||
*/
|
||||
H5_PAY("/v3/pay/transactions/h5"),
|
||||
PARTNER_H5_PAY("/v3/pay/partner/transactions/h5"),
|
||||
/**
|
||||
* 微信支付订单号查询
|
||||
*/
|
||||
ORDER_QUERY_BY_ID("/v3/pay/transactions/id/%s"),
|
||||
PARTNER_ORDER_QUERY_BY_ID("/v3/pay/partner/transactions/id/%s"),
|
||||
/**
|
||||
* 商户订单号查询
|
||||
*/
|
||||
ORDER_QUERY_BY_NO("/v3/pay/transactions/out-trade-no/%s"),
|
||||
PARTNER_ORDER_QUERY_BY_NO("/v3/pay/partner/transactions/out-trade-no/%s"),
|
||||
/**
|
||||
* 关闭订单
|
||||
*/
|
||||
CLOSE_ORDER_BY_NO("/v3/pay/transactions/out-trade-no/%s/close"),
|
||||
PARTNER_CLOSE_ORDER_BY_NO("/v3/pay/partner/transactions/out-trade-no/%s/close"),
|
||||
|
||||
|
||||
/**
|
||||
* 委托营销-建立合作关系
|
||||
*/
|
||||
PARTNERSHIPS_BUILD("/v3/marketing/partnerships/build"),
|
||||
/**
|
||||
* 委托营销-终止合作关系
|
||||
*/
|
||||
PARTNERSHIPS_TERMINATE("/v3/marketing/partnerships/terminate"),
|
||||
|
||||
|
||||
/**
|
||||
* 智慧商圈-商圈积分同步
|
||||
*/
|
||||
BUSINESS_CIRCLE_POINTS_NOTIFY("/v3/businesscircle/points/notify"),
|
||||
|
||||
|
||||
/**
|
||||
* 连锁品牌-分账-查询分账
|
||||
*/
|
||||
BRAND_PROFIT_SHARING_ORDERS("/v3/brand/profitsharing/orders"),
|
||||
/**
|
||||
* 连锁品牌-分账回退-查询分账回退
|
||||
*/
|
||||
BRAND_PROFIT_SHARING_RETURN_ORDERS("/v3/brand/profitsharing/returnorders"),
|
||||
/**
|
||||
* 连锁品牌-完结分账
|
||||
*/
|
||||
BRAND_PROFIT_SHARING_FINISH_ORDER("/v3/brand/profitsharing/finish-order"),
|
||||
/**
|
||||
* 连锁品牌-添加分账接收方
|
||||
*/
|
||||
BRAND_PROFIT_SHARING_RECEIVERS_ADD("/v3/brand/profitsharing/receivers/add"),
|
||||
/**
|
||||
* 连锁品牌-删除分账接收方
|
||||
*/
|
||||
BRAND_PROFIT_SHARING_RECEIVERS_delete("/v3/brand/profitsharing/receivers/delete"),
|
||||
;
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
private final String url;
|
||||
|
||||
WechatApiEnum(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return url;
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package com.wzj.soopin.transaction.kit.plugin.wechat.enums;
|
||||
|
||||
/**
|
||||
* 微信支付域名
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020/12/17 17:44
|
||||
*/
|
||||
public enum WechatDomain {
|
||||
/**
|
||||
* 中国国内
|
||||
*/
|
||||
CHINA("https://api.mch.weixin.qq.com"),
|
||||
/**
|
||||
* 中国国内(备用域名)
|
||||
*/
|
||||
CHINA2("https://api2.mch.weixin.qq.com"),
|
||||
/**
|
||||
* 东南亚
|
||||
*/
|
||||
HK("https://apihk.mch.weixin.qq.com"),
|
||||
/**
|
||||
* 其它
|
||||
*/
|
||||
US("https://apius.mch.weixin.qq.com"),
|
||||
/**
|
||||
* 获取公钥
|
||||
*/
|
||||
FRAUD("https://fraud.mch.weixin.qq.com"),
|
||||
/**
|
||||
* 活动
|
||||
*/
|
||||
ACTION("https://action.weixin.qq.com"),
|
||||
/**
|
||||
* 刷脸支付
|
||||
* PAY_APP
|
||||
*/
|
||||
PAY_APP("https://payapp.weixin.qq.com");
|
||||
|
||||
|
||||
/**
|
||||
* 域名
|
||||
*/
|
||||
private final String domain;
|
||||
|
||||
WechatDomain(String domain) {
|
||||
this.domain = domain;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return domain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return domain;
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package com.wzj.soopin.transaction.kit.plugin.wechat.model;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 统一下单-订单金额
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020/12/17 17:44
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class Amount {
|
||||
|
||||
/**
|
||||
* 总金额
|
||||
*/
|
||||
private BigDecimal total;
|
||||
|
||||
/**
|
||||
* 货币类型
|
||||
*/
|
||||
private String currency = "CNY";
|
||||
|
||||
/**
|
||||
* 退款金额
|
||||
*/
|
||||
private BigDecimal refund;
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package com.wzj.soopin.transaction.kit.plugin.wechat.model;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* 统一下单-优惠功能
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020/12/17 17:46
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class Detail {
|
||||
/**
|
||||
* 订单原价
|
||||
*/
|
||||
private int cost_price;
|
||||
/**
|
||||
* 商品小票ID
|
||||
*/
|
||||
private String invoice_id;
|
||||
/**
|
||||
* 单品列表
|
||||
*/
|
||||
private List<GoodsDetail> goods_detail;
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package com.wzj.soopin.transaction.kit.plugin.wechat.model;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 统一下单-单品列表
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020/12/17 17:47
|
||||
*/
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class GoodsDetail {
|
||||
/**
|
||||
* 商户侧商品编码
|
||||
*/
|
||||
private String merchant_goods_id;
|
||||
/**
|
||||
* 微信侧商品编码
|
||||
*/
|
||||
private String wechatpay_goods_id;
|
||||
/**
|
||||
* 商品名称
|
||||
*/
|
||||
private String goods_name;
|
||||
/**
|
||||
* 商品数量
|
||||
*/
|
||||
private int quantity;
|
||||
/**
|
||||
* 商品单价
|
||||
*/
|
||||
private int unit_price;
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package com.wzj.soopin.transaction.kit.plugin.wechat.model;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 统一下单-H5 场景信息
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020/12/17 17:56
|
||||
*/
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class H5Info {
|
||||
/**
|
||||
* 场景类型
|
||||
*/
|
||||
private String type;
|
||||
/**
|
||||
* 应用名称
|
||||
*/
|
||||
private String app_name;
|
||||
/**
|
||||
* 网站URL
|
||||
*/
|
||||
private String app_url;
|
||||
/**
|
||||
* iOS 平台 BundleID
|
||||
*/
|
||||
private String bundle_id;
|
||||
/**
|
||||
* Android 平台 PackageName
|
||||
*/
|
||||
private String package_name;
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.wzj.soopin.transaction.kit.plugin.wechat.model;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 统一下单-支付者
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020/12/17 17:56
|
||||
*/
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class Payer {
|
||||
/**
|
||||
* 用户标识
|
||||
*/
|
||||
private String openid;
|
||||
/**
|
||||
* 用户服务标识
|
||||
*/
|
||||
private String sp_openid;
|
||||
/**
|
||||
* 用户子标识
|
||||
*/
|
||||
private String sub_openid;
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package com.wzj.soopin.transaction.kit.plugin.wechat.model;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
|
||||
/**
|
||||
* 国内退款-退款申请
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020/12/17 17:58
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class RefundModel {
|
||||
|
||||
|
||||
/**
|
||||
* 原支付交易对应的微信订单号
|
||||
*/
|
||||
private String transaction_id;
|
||||
|
||||
/**
|
||||
* 商户订单号
|
||||
*/
|
||||
private String out_trade_no;
|
||||
|
||||
/**
|
||||
* 商户退款单号
|
||||
*/
|
||||
private String out_refund_no;
|
||||
|
||||
/**
|
||||
* 退款理由
|
||||
*/
|
||||
private String reason;
|
||||
|
||||
/**
|
||||
* 退款金额
|
||||
*/
|
||||
private Amount amount;
|
||||
|
||||
/**
|
||||
* 通知地址
|
||||
*/
|
||||
private String notify_url;
|
||||
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,32 @@
|
||||
package com.wzj.soopin.transaction.kit.plugin.wechat.model;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 统一下单-场景信息
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020/12/17 17:57
|
||||
*/
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class SceneInfo {
|
||||
/**
|
||||
* 用户终端IP
|
||||
*/
|
||||
private String payer_client_ip;
|
||||
/**
|
||||
* 商户端设备号
|
||||
*/
|
||||
private String device_id;
|
||||
/**
|
||||
* 商户门店信息
|
||||
*/
|
||||
private StoreInfo store_info;
|
||||
/**
|
||||
* H5 场景信息
|
||||
*/
|
||||
private H5Info h5_info;
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.wzj.soopin.transaction.kit.plugin.wechat.model;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 统一下单-结算信息
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020/12/17 17:58
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class SettleInfo {
|
||||
/**
|
||||
* 是否指定分账
|
||||
*/
|
||||
private boolean profit_sharing;
|
||||
/**
|
||||
* 补差金额
|
||||
*/
|
||||
private int subsidy_amount;
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package com.wzj.soopin.transaction.kit.plugin.wechat.model;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 统一下单-商户门店信息
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020/12/17 17:58
|
||||
*/
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class StoreInfo {
|
||||
/**
|
||||
* 门店编号
|
||||
*/
|
||||
private String id;
|
||||
/**
|
||||
* 门店名称
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 地区编码
|
||||
*/
|
||||
private String area_code;
|
||||
/**
|
||||
* 详细地址
|
||||
*/
|
||||
private String address;
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
package com.wzj.soopin.transaction.kit.plugin.wechat.model;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
|
||||
/**
|
||||
* 统一下单-商户门店信息
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020/12/17 17:58
|
||||
*/
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class UnifiedOrderModel {
|
||||
/**
|
||||
* 公众号ID
|
||||
*/
|
||||
private String appid;
|
||||
/**
|
||||
* 服务商公众号ID
|
||||
*/
|
||||
private String sp_appid;
|
||||
/**
|
||||
* 直连商户号
|
||||
*/
|
||||
private String mchid;
|
||||
/**
|
||||
* 服务商户号
|
||||
*/
|
||||
private String sp_mchid;
|
||||
/**
|
||||
* 子商户公众号ID
|
||||
*/
|
||||
private String sub_appid;
|
||||
/**
|
||||
* 子商户号
|
||||
*/
|
||||
private String sub_mchid;
|
||||
/**
|
||||
* 商品描述
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* 商户订单号
|
||||
*/
|
||||
private String out_trade_no;
|
||||
/**
|
||||
* 交易结束时间
|
||||
*/
|
||||
private String time_expire;
|
||||
/**
|
||||
* 附加数据
|
||||
*/
|
||||
private String attach;
|
||||
/**
|
||||
* 通知地址
|
||||
*/
|
||||
private String notify_url;
|
||||
/**
|
||||
* 订单优惠标记
|
||||
*/
|
||||
private String goods_tag;
|
||||
/**
|
||||
* 结算信息
|
||||
*/
|
||||
private SettleInfo settle_info;
|
||||
/**
|
||||
* 订单金额
|
||||
*/
|
||||
private Amount amount;
|
||||
/**
|
||||
* 支付者
|
||||
*/
|
||||
private Payer payer;
|
||||
/**
|
||||
* 优惠功能
|
||||
*/
|
||||
private Detail detail;
|
||||
/**
|
||||
* 场景信息
|
||||
*/
|
||||
private SceneInfo scene_info;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,14 @@
|
||||
package com.wzj.soopin.transaction.mapper;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.wzj.soopin.transaction.domain.po.RefundLog;
|
||||
|
||||
/**
|
||||
* 退款日志数据层
|
||||
* @author Chopper
|
||||
* @since 2020-12-19 09:25
|
||||
*/
|
||||
public interface RefundLogMapper extends BaseMapper<RefundLog> {
|
||||
|
||||
}
|
@ -7,4 +7,6 @@ public interface IChargeService extends IService<Charge> {
|
||||
|
||||
boolean audit(Long id, String status);
|
||||
|
||||
boolean charge(Charge charge);
|
||||
|
||||
}
|
||||
|
@ -7,5 +7,9 @@ import com.wzj.soopin.transaction.domain.po.Withdraw;
|
||||
public interface IWithdrawService extends IService<Withdraw> {
|
||||
boolean audit(WithdrawBO bo);
|
||||
|
||||
boolean withdraw(Long id);
|
||||
boolean withdrawCallback(WithdrawBO withdraw);
|
||||
|
||||
boolean withdrawWallet(Withdraw withdraw);
|
||||
|
||||
boolean withdrawRevenue (Withdraw withdraw);
|
||||
}
|
||||
|
@ -0,0 +1,29 @@
|
||||
package com.wzj.soopin.transaction.service;
|
||||
|
||||
|
||||
import com.wzj.soopin.transaction.kit.dto.PaymentSuccessParams;
|
||||
|
||||
/**
|
||||
* 支付日志 业务层
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020-12-19 09:25
|
||||
*/
|
||||
public interface PaymentService {
|
||||
|
||||
/**
|
||||
* 支付成功通知
|
||||
*
|
||||
* @param paymentSuccessParams 支付成功回调参数
|
||||
*/
|
||||
void success(PaymentSuccessParams paymentSuccessParams);
|
||||
|
||||
|
||||
/**
|
||||
* 平台支付成功
|
||||
*
|
||||
* @param paymentSuccessParams 支付成功回调参数
|
||||
*/
|
||||
void adminPaySuccess(PaymentSuccessParams paymentSuccessParams);
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.wzj.soopin.transaction.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.wzj.soopin.transaction.domain.po.RefundLog;
|
||||
|
||||
/**
|
||||
* 退款日志 业务层
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020-12-19 09:25
|
||||
*/
|
||||
public interface RefundLogService extends IService<RefundLog> {
|
||||
/**
|
||||
* 根据售后sn查询退款日志
|
||||
* @param sn
|
||||
* @return
|
||||
*/
|
||||
RefundLog queryByAfterSaleSn(String sn);
|
||||
}
|
@ -1,13 +1,23 @@
|
||||
package com.wzj.soopin.transaction.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.wzj.soopin.member.domain.po.AccountBill;
|
||||
import com.wzj.soopin.member.domain.po.MemberAccount;
|
||||
import com.wzj.soopin.member.enums.AccountBillChangeTypeEnum;
|
||||
import com.wzj.soopin.member.enums.AccountBillSourceEnum;
|
||||
import com.wzj.soopin.member.service.IAccountBillService;
|
||||
import com.wzj.soopin.member.service.IMemberAccountService;
|
||||
import com.wzj.soopin.transaction.domain.po.Charge;
|
||||
import com.wzj.soopin.transaction.enums.ChargeStatus;
|
||||
import com.wzj.soopin.transaction.mapper.ChargeMapper;
|
||||
import com.wzj.soopin.transaction.service.IChargeService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 会员封禁
|
||||
*
|
||||
@ -18,13 +28,52 @@ import org.springframework.stereotype.Service;
|
||||
@Slf4j
|
||||
public class ChargeServiceImpl extends ServiceImpl<ChargeMapper, Charge> implements IChargeService {
|
||||
|
||||
private final IMemberAccountService memberAccountService;
|
||||
|
||||
private final IAccountBillService accountBillService;
|
||||
|
||||
@Override
|
||||
public boolean audit(Long id, String status) {
|
||||
Charge charge = getById(id);
|
||||
//调用三方充值接口
|
||||
boolean chargeSuccess = true;
|
||||
//充值成功后更新会员账户余额
|
||||
if (chargeSuccess) {
|
||||
//更新会员账户余额
|
||||
MemberAccount memberAccount = memberAccountService.getById(charge.getMemberId());
|
||||
memberAccount.setWallet(memberAccount.getWallet().add(charge.getMoney()));
|
||||
memberAccountService.updateById(memberAccount);
|
||||
//生成充值记录
|
||||
AccountBill accountBill=AccountBill.builder()
|
||||
.accountId(charge.getMemberId())
|
||||
.changeAmount(charge.getMoney())
|
||||
.changeType(AccountBillChangeTypeEnum.IN.getCode())
|
||||
.changeDesc("充值")
|
||||
.source(AccountBillSourceEnum.RECHARGE.getCode())
|
||||
.build();
|
||||
accountBillService.save(accountBill);
|
||||
return true;
|
||||
}
|
||||
//生成充值记录
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean charge(Charge charge) {
|
||||
//判断充值金额不能为零
|
||||
if (charge.getMoney().compareTo(BigDecimal.ZERO) == 0) {
|
||||
throw new ServiceException("充值金额不能为零");
|
||||
}
|
||||
//状态为待审核
|
||||
charge.setStatus(ChargeStatus.WAITING.getCode());
|
||||
charge.setType(1);
|
||||
//手续费
|
||||
charge.setFee(new BigDecimal("0.01").multiply(charge.getMoney()));
|
||||
//实际金额
|
||||
charge.setActualMoney(charge.getMoney().subtract(charge.getFee()));
|
||||
//保存充值记录
|
||||
save(charge);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,54 @@
|
||||
package com.wzj.soopin.transaction.service.impl;
|
||||
|
||||
|
||||
import com.wzj.soopin.transaction.kit.CashierSupport;
|
||||
import com.wzj.soopin.transaction.kit.dto.PaymentSuccessParams;
|
||||
import com.wzj.soopin.transaction.kit.params.CashierExecute;
|
||||
import com.wzj.soopin.transaction.service.PaymentService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 支付日志 业务实现
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020-12-19 09:25
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class PaymentServiceImpl implements PaymentService {
|
||||
|
||||
@Autowired
|
||||
private List<CashierExecute> cashierExecutes;
|
||||
@Autowired
|
||||
private CashierSupport cashierSupport;
|
||||
|
||||
@Override
|
||||
public void success(PaymentSuccessParams paymentSuccessParams) {
|
||||
|
||||
boolean paymentResult = cashierSupport.paymentResult(paymentSuccessParams.getPayParam());
|
||||
if (paymentResult) {
|
||||
log.warn("订单支付状态后,调用支付成功接口,流水号:{}", paymentSuccessParams.getReceivableNo());
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("支付成功,第三方流水:{}", paymentSuccessParams.getReceivableNo());
|
||||
//支付结果处理
|
||||
for (CashierExecute cashierExecute : cashierExecutes) {
|
||||
cashierExecute.paymentSuccess(paymentSuccessParams);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void adminPaySuccess(PaymentSuccessParams paymentSuccessParams) {
|
||||
|
||||
log.debug("支付状态修改成功->银行转账");
|
||||
//支付结果处理
|
||||
for (CashierExecute cashierExecute : cashierExecutes) {
|
||||
cashierExecute.paymentSuccess(paymentSuccessParams);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.wzj.soopin.transaction.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.wzj.soopin.transaction.domain.po.RefundLog;
|
||||
import com.wzj.soopin.transaction.mapper.RefundLogMapper;
|
||||
import com.wzj.soopin.transaction.service.RefundLogService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 退款日志 业务实现
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2020-12-19 09:25
|
||||
*/
|
||||
@Service
|
||||
public class RefundLogServiceImpl extends ServiceImpl<RefundLogMapper, RefundLog> implements RefundLogService {
|
||||
|
||||
@Override
|
||||
public RefundLog queryByAfterSaleSn(String sn) {
|
||||
return this.getOne(new LambdaUpdateWrapper<RefundLog>().eq(RefundLog::getAfterSaleNo, sn));
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ import com.wzj.soopin.member.enums.AccountBillChangeTypeEnum;
|
||||
import com.wzj.soopin.member.enums.AccountBillSourceEnum;
|
||||
import com.wzj.soopin.transaction.enums.WithdrawAuditStatus;
|
||||
import com.wzj.soopin.transaction.enums.WithdrawStatus;
|
||||
import com.wzj.soopin.transaction.enums.WithdrawType;
|
||||
import com.wzj.soopin.transaction.mapper.WithdrawMapper;
|
||||
import com.wzj.soopin.member.service.*;
|
||||
import com.wzj.soopin.transaction.domain.vo.YishengAccountVO;
|
||||
@ -17,6 +18,8 @@ import com.wzj.soopin.transaction.service.IYishengService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.system.domain.SysTenantAccount;
|
||||
import org.dromara.system.service.ISysTenantAccountService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@ -35,12 +38,15 @@ public class WithdrawServiceImpl extends ServiceImpl<WithdrawMapper, Withdraw> i
|
||||
|
||||
private final IMemberAccountService memberAccountService;
|
||||
|
||||
private final ISysTenantAccountService sysTenantAccountService;
|
||||
|
||||
|
||||
/**
|
||||
* 易生账户充值服务
|
||||
*/
|
||||
private final IYishengService yishengService;
|
||||
|
||||
private final IAccountBillService memberAccountChangeRecordService;
|
||||
private final IAccountBillService accountBillService;
|
||||
|
||||
@Override
|
||||
public boolean audit(WithdrawBO bo) {
|
||||
@ -51,60 +57,31 @@ public class WithdrawServiceImpl extends ServiceImpl<WithdrawMapper, Withdraw> i
|
||||
if (!Objects.equals(WithdrawAuditStatus.PENDING.getCode(), withdraw.getAuditStatus())) {
|
||||
throw new RuntimeException("提现申请已处理");
|
||||
}
|
||||
//发起提现
|
||||
boolean chargeSuccess = yishengService.withdraw(withdraw.getMemberId(), withdraw.getMoney());
|
||||
if (!chargeSuccess) {
|
||||
throw new RuntimeException("提现失败");
|
||||
}
|
||||
withdraw = Withdraw.builder().id(bo.getId())
|
||||
.auditReason(bo.getAuditReason())
|
||||
.auditTime(LocalDateTime.now())
|
||||
.auditStatus(bo.getAuditStatus())
|
||||
.auditBy(LoginHelper.getUserId())
|
||||
.build();
|
||||
return this.updateById(withdraw);
|
||||
this.updateById(withdraw);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean save(Withdraw entity) {
|
||||
entity.setStatus(WithdrawStatus.WAITING.getCode());
|
||||
return super.save(entity);
|
||||
}
|
||||
|
||||
public boolean withdraw(Long id) {
|
||||
Withdraw withdraw = getById(id);
|
||||
//获取用户余额信息
|
||||
MemberAccount memberAccount = memberAccountService.getById(withdraw.getMemberId());
|
||||
if (memberAccount == null) {
|
||||
throw new RuntimeException("用户不存在");
|
||||
}
|
||||
//检查当前用于的账户余额是否充足
|
||||
public boolean withdrawCallback(WithdrawBO withdraw) {
|
||||
MemberAccount memberAccount = memberAccountService.getMemberAccount(withdraw.getMemberId());
|
||||
BigDecimal balance = memberAccount.getWallet();
|
||||
|
||||
if (balance.compareTo(withdraw.getMoney()) < 0) {
|
||||
throw new RuntimeException("用户余额不足");
|
||||
}
|
||||
//调用三方支付平台获取用户余额
|
||||
YishengAccountVO yishengAccountVO = yishengService.getYishengAccount(withdraw.getMemberId());
|
||||
|
||||
if (yishengAccountVO == null) {
|
||||
throw new RuntimeException("用户余额获取失败");
|
||||
}
|
||||
|
||||
BigDecimal yishengBalance = yishengAccountVO.getBalance();
|
||||
if (yishengBalance.compareTo(withdraw.getMoney()) < 0) {
|
||||
throw new RuntimeException("用户余额不足");
|
||||
}
|
||||
|
||||
if (!yishengBalance.equals(balance)) {
|
||||
throw new RuntimeException("用户余额不一致");
|
||||
}
|
||||
|
||||
//发起提现
|
||||
boolean chargeSuccess = yishengService.withdraw(withdraw.getMemberId(), withdraw.getMoney());
|
||||
|
||||
if (chargeSuccess) {
|
||||
//提现成功后,更新会员账户余额
|
||||
////提现成功后,更新会员账户余额
|
||||
//从易生取,别用自己计算的
|
||||
//// TODO: 2025/6/21 测试的时候用计算的 测试完用易生的
|
||||
BigDecimal finalBalance = balance.subtract(withdraw.getMoney());
|
||||
yishengAccountVO = yishengService.getYishengAccount(withdraw.getMemberId());
|
||||
memberAccountService.updateById(memberAccount.toBuilder().wallet(balance.subtract(finalBalance)).build());
|
||||
YishengAccountVO yishengAccountVO = yishengService.getYishengAccount(withdraw.getMemberId());
|
||||
memberAccountService.updateById(memberAccount.toBuilder().wallet(finalBalance).build());
|
||||
//生成账户变动记录bh
|
||||
AccountBill memberAccountChangeRecord = AccountBill.builder()
|
||||
.accountId(withdraw.getMemberId())
|
||||
@ -114,11 +91,88 @@ public class WithdrawServiceImpl extends ServiceImpl<WithdrawMapper, Withdraw> i
|
||||
.changeType(AccountBillChangeTypeEnum.OUT.getCode())
|
||||
.changeDesc("提现")
|
||||
.source(AccountBillSourceEnum.WITHDRAW.getCode()).build();
|
||||
memberAccountChangeRecordService.save(memberAccountChangeRecord);
|
||||
accountBillService.save(memberAccountChangeRecord);
|
||||
|
||||
} else {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean save(Withdraw entity) {
|
||||
entity.setStatus(WithdrawStatus.WAITING.getCode());
|
||||
return super.save(entity);
|
||||
}
|
||||
|
||||
|
||||
public boolean withdrawRevenue(Withdraw withdraw) {
|
||||
//调用三方支付平台获取用户余额
|
||||
SysTenantAccount tenantAccount = sysTenantAccountService.getByTenantId(withdraw.getMemberId());
|
||||
if (tenantAccount == null) {
|
||||
throw new RuntimeException("用户不存在");
|
||||
}
|
||||
BigDecimal balance = tenantAccount.getRevenue();
|
||||
YishengAccountVO yishengAccountVO = yishengService.getYishengAccount(withdraw.getMemberId());
|
||||
|
||||
if (yishengAccountVO == null) {
|
||||
throw new RuntimeException("账户余额获取失败");
|
||||
}
|
||||
//生成费用
|
||||
BigDecimal fee = withdraw.getMoney().multiply(new BigDecimal("0.01"));
|
||||
withdraw.setFee(fee);
|
||||
withdraw.setActualMoney(withdraw.getMoney().subtract(fee));
|
||||
|
||||
|
||||
save(withdraw);
|
||||
BigDecimal newBalance = balance.subtract(withdraw.getMoney());
|
||||
|
||||
tenantAccount.setRevenue(newBalance);
|
||||
//锁定用户余额
|
||||
sysTenantAccountService.updateById(tenantAccount);
|
||||
|
||||
//生成账单
|
||||
AccountBill memberAccountChangeRecord = AccountBill.builder()
|
||||
.accountId(withdraw.getMemberId())
|
||||
.moneyBalance(balance.subtract(withdraw.getMoney()))
|
||||
.beforeBalance(balance)
|
||||
.afterBalance(newBalance)
|
||||
.changeType(AccountBillChangeTypeEnum.OUT.getCode())
|
||||
.changeDesc("提现锁定")
|
||||
.source(AccountBillSourceEnum.WITHDRAW.getCode())
|
||||
.build();
|
||||
accountBillService.save(memberAccountChangeRecord);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean withdrawWallet(Withdraw withdraw) {
|
||||
MemberAccount memberAccount = memberAccountService.getMemberAccount(withdraw.getMemberId());
|
||||
if (memberAccount == null) {
|
||||
throw new RuntimeException("用户不存在");
|
||||
}
|
||||
//检查当前用于的账户余额是否充足
|
||||
BigDecimal balance = memberAccount.getWallet();
|
||||
if (balance.compareTo(withdraw.getMoney()) < 0) {
|
||||
throw new RuntimeException("用户余额不足");
|
||||
}
|
||||
//生成费用
|
||||
BigDecimal fee = withdraw.getMoney().multiply(new BigDecimal("0.01"));
|
||||
withdraw.setFee(fee);
|
||||
withdraw.setActualMoney(withdraw.getMoney().subtract(fee));
|
||||
save(withdraw);
|
||||
|
||||
BigDecimal newBalance = balance.subtract(withdraw.getMoney());
|
||||
//锁定用户余额
|
||||
memberAccountService.updateById(memberAccount.toBuilder().wallet(newBalance).build());
|
||||
|
||||
//生成账单
|
||||
AccountBill memberAccountChangeRecord = AccountBill.builder()
|
||||
.accountId(withdraw.getMemberId())
|
||||
.moneyBalance(newBalance)
|
||||
.beforeBalance(balance)
|
||||
.afterBalance(newBalance)
|
||||
.changeType(AccountBillChangeTypeEnum.OUT.getCode())
|
||||
.changeDesc("提现锁定")
|
||||
.source(AccountBillSourceEnum.WITHDRAW.getCode())
|
||||
.build();
|
||||
accountBillService.save(memberAccountChangeRecord);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,137 @@
|
||||
package com.wzj.soopin.transaction.util;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 金额计算工具
|
||||
*
|
||||
* @author Bulbasaur
|
||||
* @since 2021/7/9 1:40 上午
|
||||
*/
|
||||
public final class CurrencyUtil {
|
||||
/**
|
||||
* 默认除法运算精度
|
||||
*/
|
||||
private static final int DEF_DIV_SCALE = 2;
|
||||
|
||||
/**
|
||||
* 这个类不能实例化
|
||||
*/
|
||||
private CurrencyUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的加法运算。
|
||||
*
|
||||
* @return 累加之和
|
||||
*/
|
||||
public static Double add(double... params) {
|
||||
BigDecimal result = new BigDecimal("0");
|
||||
for (double param : params) {
|
||||
BigDecimal bigParam = BigDecimal.valueOf(param);
|
||||
result = result.add(bigParam).setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
return result.doubleValue();
|
||||
}
|
||||
/**
|
||||
* 提供精确的减法运算。
|
||||
*
|
||||
* @return 第一个参数为被减数,其余数字为减数
|
||||
*/
|
||||
public static Double sub(double... params) {
|
||||
BigDecimal result = BigDecimal.valueOf(params[0]);
|
||||
params = Arrays.stream(params).skip(1).toArray();
|
||||
for (double param : params) {
|
||||
BigDecimal bigParam = BigDecimal.valueOf(param);
|
||||
result = result.subtract(bigParam).setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
return result.doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的乘法运算。
|
||||
*
|
||||
* @param v1 被乘数
|
||||
* @param v2 乘数
|
||||
* @return 两个参数的积
|
||||
*/
|
||||
public static Double mul(double v1, double v2) {
|
||||
BigDecimal b1 = BigDecimal.valueOf(v1);
|
||||
BigDecimal b2 = BigDecimal.valueOf(v2);
|
||||
return b1.multiply(b2).setScale(2, RoundingMode.HALF_UP).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的乘法运算。
|
||||
*
|
||||
* @param v1 被乘数
|
||||
* @param v2 乘数
|
||||
* @param scale 表示表示需要精确到小数点以后几位。
|
||||
* @return 两个参数的积
|
||||
*/
|
||||
public static Double mul(double v1, double v2, int scale) {
|
||||
if (scale < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"The scale must be a positive integer or zero");
|
||||
}
|
||||
BigDecimal b1 = BigDecimal.valueOf(v1);
|
||||
BigDecimal b2 = BigDecimal.valueOf(v2);
|
||||
return b1.multiply(b2).setScale(scale, RoundingMode.HALF_UP).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供(相对)精确的除法运算,当发生除不尽的情况时, 精确到小数点以后10位,以后的数字四舍五入。
|
||||
*
|
||||
* @param v1 被除数
|
||||
* @param v2 除数
|
||||
* @return 两个参数的商
|
||||
*/
|
||||
public static double div(double v1, double v2) {
|
||||
return div(v1, v2, DEF_DIV_SCALE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供(相对)精确的除法运算。 当发生除不尽的情况时,由scale参数指定精度,以后的数字四舍五入。
|
||||
*
|
||||
* @param v1 被除数
|
||||
* @param v2 除数
|
||||
* @param scale 表示表示需要精确到小数点以后几位。
|
||||
* @return 两个参数的商
|
||||
*/
|
||||
public static double div(double v1, double v2, int scale) {
|
||||
if (scale < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"The scale must be a positive integer or zero");
|
||||
}
|
||||
//如果被除数等于0,则返回0
|
||||
if (v2 == 0) {
|
||||
return 0;
|
||||
}
|
||||
BigDecimal b1 = BigDecimal.valueOf(v1);
|
||||
BigDecimal b2 = BigDecimal.valueOf(v2);
|
||||
return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 金额转分
|
||||
*
|
||||
* @param money 金额
|
||||
* @return 转换单位为分
|
||||
*/
|
||||
public static Integer fen(Double money) {
|
||||
double price = mul(money, 100);
|
||||
return (int) price;
|
||||
}
|
||||
|
||||
/**
|
||||
* 金额转分
|
||||
*
|
||||
* @param money 金额
|
||||
* @return double类型分
|
||||
*/
|
||||
public static double reversalFen(Double money) {
|
||||
return div(money, 100);
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package com.wzj.soopin.transaction.util;
|
||||
|
||||
import cn.hutool.core.lang.Snowflake;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.wzj.soopin.order.utils.DateUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 雪花分布式id获取
|
||||
*
|
||||
* @author Chopper
|
||||
*/
|
||||
@Slf4j
|
||||
public class SnowFlake {
|
||||
|
||||
/**
|
||||
* 机器id
|
||||
*/
|
||||
private static long workerId = 0L;
|
||||
/**
|
||||
* 机房id
|
||||
*/
|
||||
public static long datacenterId = 0L;
|
||||
|
||||
private static Snowflake snowflake;
|
||||
|
||||
/**
|
||||
* 初始化配置
|
||||
*
|
||||
* @param workerId
|
||||
* @param datacenterId
|
||||
*/
|
||||
public static void initialize(long workerId, long datacenterId) {
|
||||
snowflake = IdUtil.getSnowflake(workerId, datacenterId);
|
||||
}
|
||||
|
||||
public static long getId() {
|
||||
return snowflake.nextId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成字符,带有前缀
|
||||
*
|
||||
* @param prefix
|
||||
* @return
|
||||
*/
|
||||
public static String createStr(String prefix) {
|
||||
return prefix + DateUtil.toString(new Date(), "yyyyMMdd") + SnowFlake.getId();
|
||||
}
|
||||
|
||||
public static String getIdStr() {
|
||||
if (snowflake == null) {
|
||||
initialize(workerId, datacenterId);
|
||||
}
|
||||
return snowflake.nextId() + "";
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user