Browse Source

添加抖音、快手支付

feature-1.1
wuxicheng 3 years ago
parent
commit
542f4207d2
  1. 5
      bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/constant/RedisKeyConstant.java
  2. 69
      bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/DypayConfig.java
  3. 62
      bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/KspayConfig.java
  4. 18
      bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/PayInfo.java
  5. 6
      bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/EnumPayType.java
  6. 2
      bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/impl/VipOrderServiceImpl.java
  7. 27
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/config/RestTemplateConfiguration.java
  8. 27
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/constant/DYPayConstants.java
  9. 50
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/constant/KSPayConstants.java
  10. 42
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/controller/DYPayController.java
  11. 50
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/controller/KSPayController.java
  12. 2
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/factory/PayFactory.java
  13. 79
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/AbstractPayStrategy.java
  14. 20
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/AliPayStrategy.java
  15. 215
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/DYPayStrategy.java
  16. 214
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/KSPayStrategy.java
  17. 19
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/WxPayStrategy.java
  18. 37
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/dto/EditPayInfoSingleDto.java
  19. 5
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/dto/PayNotifyCheckDto.java
  20. 8
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/dto/UnifiedOrderDto.java
  21. 13
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/mapper/DypayConfigMapper.java
  22. 13
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/mapper/KspayConfigMapper.java
  23. 5
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/mapper/PayInfoMapper.java
  24. 12
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/DypayConfigService.java
  25. 12
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/KspayConfigService.java
  26. 15
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/DypayConfigServiceImpl.java
  27. 15
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/KspayConfigServiceImpl.java
  28. 51
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/PayInfoServiceImpl.java
  29. 138
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/utils/DYPayUtil.java
  30. 133
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/utils/KSPayUtil.java
  31. 107
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/utils/PayRestTemplateUtil.java
  32. 11
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/vo/PayInOrderVo.java
  33. 21
      bnyer-services/bnyer-pay/src/main/resources/com/bnyer/pay/mapper/DypayConfigMapper.xml
  34. 20
      bnyer-services/bnyer-pay/src/main/resources/com/bnyer/pay/mapper/KspayConfigMapper.xml
  35. 18
      bnyer-services/bnyer-pay/src/main/resources/com/bnyer/pay/mapper/PayInfoMapper.xml

5
bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/constant/RedisKeyConstant.java

@ -141,4 +141,9 @@ public class RedisKeyConstant {
* 热搜词存入时间
*/
public static final String HOT_KEY_WORD_TIME_KEY = "bnyer.hotkeywordtime";
/**
* 支付回调rediskey
*/
public static final String PAY_NOTIFY_LOCK_KEY = "bnyer.pay.notify.lock:";
}

69
bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/DypayConfig.java

@ -0,0 +1,69 @@
package com.bnyer.common.core.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.Date;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
/**
* @author :WXC
* @description :
*/
/**
* 抖音支付配置表
*/
@ApiModel(value="com-bnyer-common-core-domain-PayDypayConfig")
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "pay_dypay_config")
public class DypayConfig extends BaseDomain {
/**
* appid
*/
@TableField(value = "appid")
@ApiModelProperty(value="appid")
private String appid;
/**
* 秘钥
*/
@TableField(value = "salt")
@ApiModelProperty(value="秘钥")
private String salt;
/**
* 令牌
*/
@TableField(value = "token")
@ApiModelProperty(value="令牌")
private String token;
/**
* 回调地址url
*/
@TableField(value = "backurl")
@ApiModelProperty(value="回调地址url")
private String backurl;
/**
* 帐号状态0正常 1停用
*/
@TableField(value = "status")
@ApiModelProperty(value="帐号状态(0正常 1停用)")
private String status;
@TableField(value = "remark")
@ApiModelProperty(value="")
private String remark;
}

62
bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/KspayConfig.java

@ -0,0 +1,62 @@
package com.bnyer.common.core.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.Date;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
/**
* @author :WXC
* @description :
*/
/**
* 快手支付配置表
*/
@ApiModel(value="com-bnyer-common-core-domain-PayKspayConfig")
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "pay_kspay_config")
public class KspayConfig extends BaseDomain {
/**
* appid
*/
@TableField(value = "appid")
@ApiModelProperty(value="appid")
private String appid;
/**
* 秘钥
*/
@TableField(value = "secret")
@ApiModelProperty(value="秘钥")
private String secret;
/**
* 回调地址url
*/
@TableField(value = "backurl")
@ApiModelProperty(value="回调地址url")
private String backurl;
/**
* 帐号状态0正常 1停用
*/
@TableField(value = "status")
@ApiModelProperty(value="帐号状态(0正常 1停用)")
private String status;
@TableField(value = "remark")
@ApiModelProperty(value="")
private String remark;
}

18
bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/PayInfo.java

@ -73,12 +73,19 @@ public class PayInfo extends BaseDomain {
/**
* 支付类型wxpay/alipay
* 支付类型wxpay/alipay/kspay/dypay
*/
@TableField(value = "pay_type")
@ApiModelProperty(value="支付类型:wxpay/alipay")
@ApiModelProperty(value="支付类型:wxpay/alipay/kspay/dypay")
private String payType;
/**
* 支付渠道
*/
@TableField(value = "pay_channel")
@ApiModelProperty(value="支付渠道")
private String payChannel;
/**
* 交易类型JSAPI等
*/
@ -93,6 +100,13 @@ public class PayInfo extends BaseDomain {
@ApiModelProperty(value="支付单号(第三方返回)")
private String payNo;
/**
* 用户侧订单号(第三方返回)
*/
@TableField(value = "trade_no")
@ApiModelProperty(value="用户侧订单号(第三方返回)")
private String tradeNo;
/**
* appid
*/

6
bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/EnumPayType.java

@ -13,8 +13,10 @@ import java.util.Objects;
@AllArgsConstructor
public enum EnumPayType {
WX_PAY("wxpay","微信"),
ALI_PAY("alipay","支付宝"),
WX_PAY("wxpay","微信支付"),
ALI_PAY("alipay","支付宝支付"),
DY_PAY("dypay","抖音支付"),
KS_PAY("kspay","快手支付"),
;
private final String type;

2
bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/impl/VipOrderServiceImpl.java

@ -124,7 +124,7 @@ public class VipOrderServiceImpl extends ServiceImpl<VipOrderMapper, VipOrder> i
case AI_VIP:
break;
default:
throw new ServiceException("categoryCode未匹配上对应分类");
throw new ServiceException("vipTypeCode 未匹配上对应分类");
}
return vipOrder;
}

27
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/config/RestTemplateConfiguration.java

@ -0,0 +1,27 @@
package com.bnyer.pay.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
/**
* @author chengkun
* @date 2022/4/28 17:27
*/
@Configuration
public class RestTemplateConfiguration {
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
return new RestTemplate(factory);
}
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory(){
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(15000);
factory.setReadTimeout(10000);
return factory;
}
}

27
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/constant/DYPayConstants.java

@ -0,0 +1,27 @@
package com.bnyer.pay.constant;
/**
* @author :WXC
* @Date :2023/04/23
* @description :抖音支付常量池
*/
public class DYPayConstants {
/**
* 登陆
*/
public static final String CODE_2_SESSION = "https://developer.toutiao.com/api/apps/v2/jscode2session";
/**
* 生成预支付单
*/
public static final String CREATE_ORDER = "https://developer.toutiao.com/api/apps/ecpay/v1/create_order";
/**
* 分账
*/
public static final String SETTLE = "https://developer.toutiao.com/api/apps/ecpay/v1/settle";
/**
* 退款
*/
public static final String CREATE_REFUND = "https://developer.toutiao.com/api/apps/ecpay/v1/create_refund";
}

50
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/constant/KSPayConstants.java

@ -0,0 +1,50 @@
package com.bnyer.pay.constant;
/**
* @author :WXC
* @Date :2023/04/23
* @description : 快手支付常量池
*/
public class KSPayConstants {
/**
* https://mp.kuaishou.com/docs/develop/server/code2Session.html
* code获取openId sessionKey
*/
public static final String CODE_2_SESSION = "https://open.kuaishou.com/oauth2/mp/code2session";
/**
* https://mp.kuaishou.com/docs/develop/server/getAccessToken.html
* 接口调用凭证
*/
public static final String GET_ACCESS_TOKEN = "https://open.kuaishou.com/oauth2/access_token";
/**
* https://mp.kuaishou.com/docs/develop/server/epay/interfaceDefinition.html
* 预下单接口
*/
public static final String CREATE_ORDER = "https://open.kuaishou.com/openapi/mp/developer/epay/create_order";
/**
* 退款
* https://mp.kuaishou.com/docs/develop/server/epay/interfaceDefinition.html#_1-3%E6%94%AF%E4%BB%98%E5%9B%9E%E8%B0%83
*/
public static final String APPLY_REFUND = "https://open.kuaishou.com/openapi/mp/developer/epay/apply_refund";
/**
* 结算
* https://mp.kuaishou.com/docs/develop/server/epay/interfaceDefinition.html#_1-3%E6%94%AF%E4%BB%98%E5%9B%9E%E8%B0%83
*/
public static final String APPLY_SETTLE = "https://open.kuaishou.com/openapi/mp/developer/epay/settle";
/**
* 超时时间
*/
public static final int expireTime = 1800;
/**
* https://mp.kuaishou.com/docs/operate/platformAgreement/epayServiceCharge.html
* 商品类目编号
*/
public static final int GOODS_TYPE_VIP = 1273;
}

42
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/controller/DYPayController.java

@ -0,0 +1,42 @@
package com.bnyer.pay.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bnyer.common.core.enums.EnumPayType;
import com.bnyer.pay.design.factory.PayFactory;
import com.bnyer.pay.design.strategy.IPayStrategy;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
* @author :WXC
* @Date :2023/04/24
* @description :
*/
@Api(value = "抖音支付相关接口",tags = "抖音支付相关接口")
@RestController
@Slf4j
public class DYPayController {
/**
* 抖音支付结果通知
*/
@ApiOperation(value = "抖音支付结果通知")
@ResponseBody
@PostMapping("/dypayBack")
public JSONObject dyPayNotify(@RequestBody JSONObject object, HttpServletRequest request) {
log.info("抖音支付异步通知开始==============》{}", object);
IPayStrategy payStrategy = PayFactory.getInstance().getConcreteStrategy(EnumPayType.DY_PAY.getType());
String payNotify = payStrategy.parsePayNotify(JSON.toJSONString(object));
return JSON.parseObject(payNotify);
}
}

50
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/controller/KSPayController.java

@ -0,0 +1,50 @@
package com.bnyer.pay.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.bnyer.common.core.enums.EnumPayType;
import com.bnyer.pay.design.factory.PayFactory;
import com.bnyer.pay.design.strategy.IPayStrategy;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
* @author :WXC
* @Date :2023/04/24
* @description :
*/
@Api(value = "快手支付相关接口",tags = "快手支付相关接口")
@RestController
@Slf4j
public class KSPayController {
/**
* 快手支付结果通知
*/
@ApiOperation(value = "快手支付结果通知")
@ResponseBody
@PostMapping("/kspayBack")
public JSONObject ksPayNotify(@RequestBody JSONObject object, HttpServletRequest request) {
log.info("快手微信支付异步通知开始==============》{}", object);
log.info("快手微信支付kwaisign==============》{}", request.getHeader("kwaisign"));
String kwaisign = request.getHeader("kwaisign");
JSONObject jsonObject = new JSONObject();
jsonObject.put("data",object);
jsonObject.put("sign",kwaisign);
String string = JSONObject.toJSONString(jsonObject);
IPayStrategy payStrategy = PayFactory.getInstance().getConcreteStrategy(EnumPayType.KS_PAY.getType());
String payNotify = payStrategy.parsePayNotify(string);
return JSON.parseObject(payNotify);
}
}

2
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/factory/PayFactory.java

@ -17,6 +17,8 @@ public class PayFactory {
private static final Map<String, IPayStrategy> strategyMap = new ImmutableMap.Builder<String, IPayStrategy>()
.put(EnumPayType.ALI_PAY.getType(),new AliPayStrategy())
.put(EnumPayType.WX_PAY.getType(),new WxPayStrategy())
.put(EnumPayType.DY_PAY.getType(),new WxPayStrategy())
.put(EnumPayType.KS_PAY.getType(),new WxPayStrategy())
.build();
public static class SingletonHolder{

79
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/AbstractPayStrategy.java

@ -1,17 +1,22 @@
package com.bnyer.pay.design.strategy;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.msg.MsgConstants;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.bnyer.common.core.constant.RedisKeyConstant;
import com.bnyer.common.core.domain.PayInfo;
import com.bnyer.common.core.enums.EnumPayType;
import com.bnyer.common.redis.service.RedissonService;
import com.bnyer.pay.dto.PayNotifyCheckDto;
import com.bnyer.pay.mapper.PayInfoMapper;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import javax.annotation.Resource;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* @author :WXC
@ -24,48 +29,84 @@ public abstract class AbstractPayStrategy implements IPayStrategy {
@Resource
private PayInfoMapper payInfoMapper;
@Resource
private RedissonService redissonService;
/**
* 校验是否已支付避免重复调用
* @param checkDto
* @return
*/
public String payNotifyCheck(PayNotifyCheckDto checkDto){
log.error("回调结果校验开始,支付单号:{}",checkDto.getPayId());
String resultMsg = "";
PayInfo payInfo = payInfoMapper.selectOne(new LambdaQueryWrapper<PayInfo>().eq(PayInfo::getPayId, checkDto.getPayId()).eq(PayInfo::getIsShow, "1"));
if (Objects.isNull(payInfo)){
return buildNotifyCheckResultMsg(checkDto.getPayType(),false,"Order_Not_Exist");
}
log.info("查询到支付订单信息为:{}", JSON.toJSONString(payInfo));
//订单中的金额
String payInfoPayAmount = payInfo.getPayAmount().toString();
//回调中的金额
String notifyPayAmount = checkDto.getPayAmount();
//对账状态
Integer singleStatus = payInfo.getSingleStatus();
log.info("回调中的金额:{},订单中的金额:{}",notifyPayAmount,payInfoPayAmount);
if (payInfoPayAmount.equals(notifyPayAmount)){
if (Objects.nonNull(singleStatus)){
resultMsg = buildNotifyCheckResultMsg(checkDto.getPayType(),true,"OK");
RLock rLock = redissonService.getRLock(RedisKeyConstant.PAY_NOTIFY_LOCK_KEY + checkDto.getPayId());
try{
if(rLock.tryLock(2L, 10L, TimeUnit.SECONDS)){
PayInfo payInfo = payInfoMapper.selectOne(new LambdaQueryWrapper<PayInfo>().eq(PayInfo::getPayId, checkDto.getPayId()).eq(PayInfo::getIsShow, "1"));
if (Objects.isNull(payInfo)){
return buildNotifyCheckResultMsg(checkDto.getPayType(),false,"business fail");
}
log.info("查询到支付订单信息为:{}", JSON.toJSONString(payInfo));
//订单中的金额
String payInfoPayAmount = payInfo.getPayAmount().toString();
//回调中的金额
String notifyPayAmount = checkDto.getPayAmount();
//对账状态
Integer singleStatus = payInfo.getSingleStatus();
log.info("回调中的金额:{},订单中的金额:{}",notifyPayAmount,payInfoPayAmount);
if (payInfoPayAmount.equals(notifyPayAmount)){
if (Objects.nonNull(singleStatus)){
resultMsg = buildNotifyCheckResultMsg(checkDto.getPayType(),true,checkDto.getMsg());
}
}else {
resultMsg = buildNotifyCheckResultMsg(checkDto.getPayType(),false,"business fail");
}
}else {
log.error("获取锁失败,支付单号:{}",checkDto.getPayId());
resultMsg = buildNotifyCheckResultMsg(checkDto.getPayType(),false,"business fail");
}
}else {
resultMsg = buildNotifyCheckResultMsg(checkDto.getPayType(),false,"Amount_Diff");
}catch (Exception e){
e.printStackTrace();
log.error("回调结果校验失败,支付单号:{}",checkDto.getPayId());
resultMsg = buildNotifyCheckResultMsg(checkDto.getPayType(),false,"business fail");
}finally {
rLock.unlock();
}
return resultMsg;
}
/**
* 构建校验后回调返回结果信息
* 构建回调返回结果信息
* @param payType 支付方式
* @param isSuccess 是否构建成功消息
* @param msg 消息内容
* @return
*/
private String buildNotifyCheckResultMsg(EnumPayType payType,boolean isSuccess,String msg){
public String buildNotifyCheckResultMsg(EnumPayType payType,boolean isSuccess,String msg){
String result = "";
if (EnumPayType.WX_PAY == payType){
result = isSuccess?WxPayNotifyResponse.success(msg):WxPayNotifyResponse.fail(msg);
}else if (EnumPayType.ALI_PAY == payType){
result = isSuccess?MsgConstants.SUCCESS:MsgConstants.FAIL;
} else if (EnumPayType.KS_PAY == payType) {
JSONObject returnObj = new JSONObject();
if (isSuccess){
returnObj.put("result", 1);
}else {
returnObj.put("result", 0);
}
returnObj.put("message_id", msg);
result = JSON.toJSONString(returnObj);
} else if (EnumPayType.DY_PAY == payType) {
JSONObject returnObj = new JSONObject();
if (isSuccess){
returnObj.put("err_no", 0);
}else {
returnObj.put("err_no", -1);
}
returnObj.put("err_tips", msg);
result = JSON.toJSONString(returnObj);
}
return result;
}

20
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/AliPayStrategy.java

@ -30,6 +30,7 @@ import com.bnyer.pay.mapper.AlipayConfigMapper;
import com.bnyer.pay.service.PayInfoService;
import com.bnyer.pay.vo.PayInOrderVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@ -52,6 +53,9 @@ public class AliPayStrategy extends AbstractPayStrategy {
@Resource
private AlipayConfigMapper alipayConfigMapper;
@Autowired
private PayInfoService payInfoService;
@Transactional(propagation = Propagation.REQUIRED,rollbackFor=Exception.class)
@Override
public PayInOrderVo unifiedOrder(UnifiedOrderDto dto) {
@ -74,7 +78,7 @@ public class AliPayStrategy extends AbstractPayStrategy {
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setSubject(dto.getGoodsSubject());
model.setBody(dto.getGoodsDesc());
model.setOutTradeNo(dto.getOrderId());
model.setOutTradeNo(dto.getPayId());
//直接固定好过期时间比较好
model.setTimeExpire(DateUtil.formatDateTime(DateUtils.getDateByType(EnumTimeUnit.MINUTE,dto.getCurrDate(),30)));
// model.setTimeoutExpress(AliPayConstant.timeoutExpress);
@ -88,8 +92,8 @@ public class AliPayStrategy extends AbstractPayStrategy {
if(response.isSuccess()){
PayInOrderVo vo = new PayInOrderVo();
vo.setOutStr(response.getBody());
vo.setOrderId(dto.getOrderId());
vo.setAppid(appid);
vo.setPayId(dto.getPayId());
return vo;
}else{
throw new ServiceException(response.getMsg());
@ -101,12 +105,10 @@ public class AliPayStrategy extends AbstractPayStrategy {
}
}
@Transactional(propagation = Propagation.REQUIRED,rollbackFor=Exception.class)
@Override
public String parsePayNotify(String params) {
Map<String, String> inMap = JSON.parseObject(params, new TypeReference<HashMap<String, String>>() {});
log.info("支付宝支付回调开始: AliPayStrategy.parsePayNotify inMap:{}",JSON.toJSONString(inMap));
PayInfoService payInfoService = SpringUtils.getBean(PayInfoService.class);
try{
//--------------------------1.解析并拿到参数---------------------
String appId = inMap.get("app_id");
@ -142,8 +144,9 @@ public class AliPayStrategy extends AbstractPayStrategy {
if(tradeStatus.equals("TRADE_FINISHED") || tradeStatus.equals("TRADE_SUCCESS")){
PayNotifyCheckDto payNotifyCheckDto = new PayNotifyCheckDto();
payNotifyCheckDto.setPayType(EnumPayType.ALI_PAY);
payNotifyCheckDto.setPayId(tradeNo);
payNotifyCheckDto.setPayId(outTradeNo);
payNotifyCheckDto.setPayAmount(totalAmount);
payNotifyCheckDto.setMsg("OK");
String notifyCheckResult = super.payNotifyCheck(payNotifyCheckDto);
if (StringUtils.isNotBlank(notifyCheckResult)){
return notifyCheckResult;
@ -155,18 +158,17 @@ public class AliPayStrategy extends AbstractPayStrategy {
editPayInfoNotifyDto.setPayId(outTradeNo);
editPayInfoNotifyDto.setPayTime(gmtPayment);
editPayInfoNotifyDto.setPayAmount(totalAmount);
editPayInfoNotifyDto.setPayNo(tradeNo);
editPayInfoNotifyDto.setAppId(appId);
editPayInfoNotifyDto.setPayType(EnumPayType.ALI_PAY.getType());
editPayInfoNotifyDto.setPayNo(tradeNo);
payInfoService.editPayInfoNotify(editPayInfoNotifyDto);
}else if(tradeStatus.equals("TRADE_CLOSED")){
log.info("支付宝支付回调:TRADE_CLOSED 状态不做处理");
//交易关闭不作处理:退款或未支付超时关闭
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
// TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}catch (Exception e){
log.info("支付宝支付回调:处理过程异常,error:{}",e.getMessage());
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
// TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
return MsgConstants.SUCCESS;
}

215
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/DYPayStrategy.java

@ -0,0 +1,215 @@
package com.bnyer.pay.design.strategy;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.bnyer.common.core.context.SecurityContextHolder;
import com.bnyer.common.core.domain.DypayConfig;
import com.bnyer.common.core.domain.KspayConfig;
import com.bnyer.common.core.enums.EnumPayType;
import com.bnyer.common.core.enums.ResponseEnum;
import com.bnyer.common.core.exception.ServiceException;
import com.bnyer.common.core.utils.StringUtils;
import com.bnyer.pay.constant.DYPayConstants;
import com.bnyer.pay.constant.KSPayConstants;
import com.bnyer.pay.dto.EditPayInfoNotifyDto;
import com.bnyer.pay.dto.PayNotifyCheckDto;
import com.bnyer.pay.dto.UnifiedOrderDto;
import com.bnyer.pay.enums.EnumPayConfigStatus;
import com.bnyer.pay.mapper.DypayConfigMapper;
import com.bnyer.pay.mapper.KspayConfigMapper;
import com.bnyer.pay.service.PayInfoService;
import com.bnyer.pay.utils.DYPayUtil;
import com.bnyer.pay.utils.KSPayUtil;
import com.bnyer.pay.utils.PayRestTemplateUtil;
import com.bnyer.pay.vo.PayInOrderVo;
import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest;
import com.github.binarywang.wxpay.bean.result.BaseWxPayResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
* @author :WXC
* @Date :2023/04/23
* @description :
*/
@Slf4j
@Component
public class DYPayStrategy extends AbstractPayStrategy{
@Autowired
private DYPayUtil dyPayUtil;
@Autowired
private PayRestTemplateUtil payRestTemplateUtil;
@Autowired
private PayInfoService payInfoService;
@Autowired
private DypayConfigMapper dypayConfigMapper;
@Override
public PayInOrderVo unifiedOrder(UnifiedOrderDto dto) {
try {
List<DypayConfig> dypayConfigList = dypayConfigMapper.selectList(new LambdaQueryWrapper<DypayConfig>().eq(DypayConfig::getStatus, EnumPayConfigStatus.ENABLE.getCode()));
if (CollUtil.isEmpty(dypayConfigList)){
throw new ServiceException(ResponseEnum.PAY_CONFIG_ERROR);
}
DypayConfig dypayConfig = dypayConfigList.get(0);
String appId = dypayConfig.getAppid();
String backurl = dypayConfig.getBackurl();
String salt = dypayConfig.getSalt();
//加签验签的参数需要排序
Map<String, Object> params = new TreeMap<>();
//小程序APPID
params.put("app_id",appId);
//开发者侧的订单号。需保证同一小程序下不可重复
params.put("out_order_no", dto.getPayId());
//支付价格。单位为[分],取值范围:[1,10000000000] 100元 = 100*100 分
params.put("total_amount", BaseWxPayRequest.yuanToFen(dto.getPayAmount()));
//商品描述。
params.put("subject", dto.getGoodsSubject());
//商品详情
params.put("body", dto.getGoodsDesc());
//订单过期时间(秒) 5min-2day
params.put("valid_time", 1800);
//开发者自定义字段,回调原样回传。超过最大长度会被截断
params.put("cp_extra", "xx平台充值");
//通知地址
params.put("notify_url", backurl);
//签名,详见https://microapp.bytedance.com/docs/zh-CN/mini-app/develop/server/ecpay/TE
String sign = dyPayUtil.getSign(params,salt);
params.put("sign", sign);
//以JSON格式拼好以下参数发送请求
JSONObject payJson = new JSONObject();
payJson.put("app_id", appId);
payJson.put("out_order_no", dto.getPayId());
//此处需要传入一个数值类型,string会报错。。
payJson.put("total_amount", BaseWxPayRequest.yuanToFen(dto.getPayAmount()));
payJson.put("subject",dto.getGoodsSubject());
payJson.put("body", dto.getGoodsDesc());
payJson.put("valid_time", 1800);
payJson.put("sign", sign);
//payJson.put("cp_extra", "开发者自定义字段");
payJson.put("notify_url",backurl);
log.info("请求参数{}", payJson);
//预下单接口
String result = payRestTemplateUtil.dyPostRequest(payJson, DYPayConstants.CREATE_ORDER);
log.info("==================================");
log.info("抖音预下单result{}", result);
log.info("==================================");
if (!"".equals(result)) {
JSONObject jsonObject = JSONObject.parseObject(result);
String errNo = jsonObject.getString("err_no");
if ("0".equals(errNo)) {
JSONObject data = jsonObject.getJSONObject("data");
String orderId = data.getString("order_id");
String orderToken = data.getString("order_token");
if (null != orderToken && null != orderId) {
//保存预下单信息
PayInOrderVo payInOrderVo = new PayInOrderVo();
//把order_no和order_info_token返回前端用于调起收银台
payInOrderVo.setPayId(dto.getPayId());
payInOrderVo.setPayOrderId(orderId);
payInOrderVo.setOrderToken(orderToken);
return payInOrderVo;
} else {
return null;
}
} else {
log.error("抖音支付:统一下单接口调用失败,payId:{},error{}", dto.getPayId(), jsonObject.getString("error_msg"));
throw new ServiceException(ResponseEnum.PAY_FAILS);
}
} else {
log.error("抖音支付:统一下单接口调用失败,payId:{},error{}", dto.getPayId(), "请求结果返回为空");
throw new ServiceException(ResponseEnum.PAY_FAILS);
}
} catch (Exception e) {
e.printStackTrace();
log.error("抖音支付:支付异常,payId:{},error{}", dto.getPayId(), e.getMessage());
throw new ServiceException(ResponseEnum.PAY_FAILS);
}
}
@Override
public String parsePayNotify(String params) {
List<DypayConfig> dypayConfigList = dypayConfigMapper.selectList(new LambdaQueryWrapper<DypayConfig>()
.eq(DypayConfig::getStatus, EnumPayConfigStatus.ENABLE.getCode()));
if (CollUtil.isEmpty(dypayConfigList)){
throw new ServiceException(ResponseEnum.PAY_CONFIG_ERROR);
}
DypayConfig dypayConfig = dypayConfigList.get(0);
String appId = dypayConfig.getAppid();
String token = dypayConfig.getToken();
JSONObject object = JSONObject.parseObject(params);
//随机数
String nonce = object.getString("nonce");
//时间戳
Integer timestamp = object.getInteger("timestamp");
//签名
String msgSignature = object.getString("msg_signature");
//订单信息的json字符串
String message = object.getString("msg");
JSONObject msgJsonObj = JSON.parseObject(message);
//校验回调签名
String signMessage = dyPayUtil.getCallbackSignature(timestamp, nonce, message,token);
//签名校验
if (msgSignature.equals(signMessage)) {
//固定值SUCCESS
String status = msgJsonObj.getString("status");
//开发者侧的订单号
String outOrderNo = msgJsonObj.getString("cp_orderno");
//支付渠道: 1-微信支付,2-支付宝支付,10-抖音支付
String payChannel = msgJsonObj.getString("way");
//支付金额,单位为分
Integer totalAmount = msgJsonObj.getInteger("total_amount");
//支付渠道侧PC单号,支付页面可见(微信支付宝侧的订单号)
String paymentOrderNo = msgJsonObj.getString("payment_order_no");
//抖音侧订单号
String dyOrderId = msgJsonObj.getString("order_id");
//这里无论回调失败还是成功,都需要都各个业务层去处理相关逻辑
if("success".equals(status)){
//处理业务
PayNotifyCheckDto payNotifyCheckDto = new PayNotifyCheckDto();
payNotifyCheckDto.setPayType(EnumPayType.DY_PAY);
payNotifyCheckDto.setPayAmount(BaseWxPayResult.fenToYuan(totalAmount));
payNotifyCheckDto.setPayId(outOrderNo);
payNotifyCheckDto.setMsg("success");
String notifyCheck = super.payNotifyCheck(payNotifyCheckDto);
if (StringUtils.isNotBlank(notifyCheck)){
return notifyCheck;
}
log.info("抖音支付回调,交易正常:封装参数修改内部支付单信息");
EditPayInfoNotifyDto editPayInfoNotifyDto = new EditPayInfoNotifyDto();
editPayInfoNotifyDto.setPayAmount(BaseWxPayResult.fenToYuan(totalAmount));
editPayInfoNotifyDto.setPayTime(DateUtil.formatDateTime(new Date()));
editPayInfoNotifyDto.setAppId(appId);
editPayInfoNotifyDto.setPayId(outOrderNo);
editPayInfoNotifyDto.setPayNo(dyOrderId);
editPayInfoNotifyDto.setTradeNo(paymentOrderNo);
editPayInfoNotifyDto.setPayChannel(payChannel);
payInfoService.editPayInfoNotify(editPayInfoNotifyDto);
//正确处理后返回以下内容格式通知小程序平台不再持续回调
return super.buildNotifyCheckResultMsg(EnumPayType.DY_PAY,true,"success");
}else {
log.info("抖音支付回调不是支付成功不做处理:支付状态:{},payId:{}",status,outOrderNo);
}
} else {
log.info("抖音支付回调签名校验失败,payId:{}",msgJsonObj.getString("cp_orderno"));
}
return super.buildNotifyCheckResultMsg(EnumPayType.DY_PAY,true,"business fail");
}
}

214
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/KSPayStrategy.java

@ -0,0 +1,214 @@
package com.bnyer.pay.design.strategy;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.bnyer.common.core.context.SecurityContextHolder;
import com.bnyer.common.core.domain.KspayConfig;
import com.bnyer.common.core.enums.EnumPayType;
import com.bnyer.common.core.enums.ResponseEnum;
import com.bnyer.common.core.exception.ServiceException;
import com.bnyer.common.core.utils.StringUtils;
import com.bnyer.pay.constant.KSPayConstants;
import com.bnyer.pay.dto.EditPayInfoNotifyDto;
import com.bnyer.pay.dto.PayNotifyCheckDto;
import com.bnyer.pay.dto.UnifiedOrderDto;
import com.bnyer.pay.enums.EnumPayConfigStatus;
import com.bnyer.pay.mapper.KspayConfigMapper;
import com.bnyer.pay.service.PayInfoService;
import com.bnyer.pay.utils.KSPayUtil;
import com.bnyer.pay.utils.PayRestTemplateUtil;
import com.bnyer.pay.vo.PayInOrderVo;
import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest;
import com.github.binarywang.wxpay.bean.result.BaseWxPayResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
* @author :WXC
* @Date :2023/04/23
* @description :
*/
@Slf4j
@Component
public class KSPayStrategy extends AbstractPayStrategy{
@Autowired
private KSPayUtil ksPayUtil;
@Autowired
private PayRestTemplateUtil payRestTemplateUtil;
@Autowired
private PayInfoService payInfoService;
@Autowired
private KspayConfigMapper kspayConfigMapper;
@Override
public PayInOrderVo unifiedOrder(UnifiedOrderDto dto) {
try {
List<KspayConfig> kspayConfigList = kspayConfigMapper.selectList(new LambdaQueryWrapper<KspayConfig>().eq(KspayConfig::getStatus, EnumPayConfigStatus.ENABLE.getCode()));
if (CollUtil.isEmpty(kspayConfigList)){
throw new ServiceException(ResponseEnum.PAY_CONFIG_ERROR);
}
KspayConfig kspayConfig = kspayConfigList.get(0);
String openId = SecurityContextHolder.get("ksCode");
String appId = kspayConfig.getAppid();
String backurl = kspayConfig.getBackurl();
String secret = kspayConfig.getSecret();
//加签验签的参数需要排序
Map<String, Object> params = new TreeMap<>();
//小程序APPID
params.put("app_id", appId);
//开发者侧的订单号。需保证同一小程序下不可重复
params.put("out_order_no", dto.getPayId());
//快手用户在当前小程序的open_id,可通过login操作获取
params.put("open_id", openId);
//用户支付金额,单位为[分]。不允许传非整数的数值。
params.put("total_amount", BaseWxPayRequest.yuanToFen(dto.getPayAmount()));
//商品描述。
params.put("subject", dto.getGoodsSubject());
//商品详情
params.put("detail", dto.getGoodsDesc());
//商品类型,不同商品类目的编号见 担保支付商品类目编号
params.put("type", dto.getGoodsType());
//订单过期时间,单位秒,300s - 172800s
params.put("expire_time", KSPayConstants.expireTime);
//开发者自定义字段,回调原样回传。超过最大长度会被截断
// params.put("attach", "支付测试");
//通知地址
params.put("notify_url", backurl);
String sign = ksPayUtil.calcSign(params, secret);
JSONObject payJson = new JSONObject();
payJson.put("out_order_no", dto.getPayId());
payJson.put("open_id", openId);
payJson.put("total_amount", BaseWxPayRequest.yuanToFen(dto.getPayAmount()));
payJson.put("subject", dto.getGoodsSubject());
payJson.put("detail", dto.getGoodsDesc());
payJson.put("type", dto.getGoodsType());
payJson.put("expire_time", 1800);
payJson.put("sign", sign);
// payJson.put("attach", "测试支付");
payJson.put("notify_url", dto.getCurrDate());
log.info("请求参数{}", payJson);
//预下单接口
String ksPayAccessToken = payRestTemplateUtil.ksPostRequestUrlencoded(kspayConfig);
String result = payRestTemplateUtil.ksPostRequestJson(payJson, KSPayConstants.CREATE_ORDER, appId, ksPayAccessToken);
log.info("==================================");
log.info("快手预下单result{}", result);
log.info("==================================");
if (!"".equals(result)) {
JSONObject jsonObject = JSONObject.parseObject(result);
String resultCode = jsonObject.getString("result");
if ("1".equals(resultCode)) {
JSONObject data = jsonObject.getJSONObject("order_info");
String orderNo = data.getString("order_no");
String orderToken = data.getString("order_info_token");
if (null != orderToken && null != orderNo) {
//保存预下单信息
PayInOrderVo payInOrderVo = new PayInOrderVo();
//把order_no和order_info_token返回前端用于调起收银台
payInOrderVo.setPayId(dto.getPayId());
payInOrderVo.setPayOrderId(orderNo);
payInOrderVo.setOrderToken(orderToken);
return payInOrderVo;
} else {
return null;
}
} else {
log.error("快手支付:统一下单接口调用失败,payId:{},error{}", dto.getPayId(), jsonObject.getString("error_msg"));
throw new ServiceException(ResponseEnum.PAY_FAILS);
}
} else {
log.error("快手支付:统一下单接口调用失败,payId:{},error{}", dto.getPayId(), "请求结果返回为空");
throw new ServiceException(ResponseEnum.PAY_FAILS);
}
} catch (Exception e) {
e.printStackTrace();
log.error("快手支付:支付异常,payId:{},error{}", dto.getPayId(), e.getMessage());
throw new ServiceException(ResponseEnum.PAY_FAILS);
}
}
@Override
public String parsePayNotify(String params) {
List<KspayConfig> kspayConfigList = kspayConfigMapper.selectList(new LambdaQueryWrapper<KspayConfig>().eq(KspayConfig::getStatus, EnumPayConfigStatus.ENABLE.getCode()));
if (CollUtil.isEmpty(kspayConfigList)){
throw new ServiceException(ResponseEnum.PAY_CONFIG_ERROR);
}
KspayConfig kspayConfig = kspayConfigList.get(0);
String appId = kspayConfig.getAppid();
String secret = kspayConfig.getSecret();
JSONObject jsonObject = JSONObject.parseObject(params);
JSONObject object = jsonObject.getJSONObject("data");
String kwaisign = jsonObject.getString("sign");
String jsonString = JSONObject.toJSONString(object, SerializerFeature.WRITE_MAP_NULL_FEATURES, SerializerFeature.QuoteFieldNames);
log.info("jsonString:" + jsonString);
if (null != kwaisign) {
jsonString = jsonString + secret;
//签名校验
if (kwaisign.equals(DigestUtils.md5Hex(jsonString))) {
//当前回调消息的唯一ID,在同一个消息多次通知时,保持一致。
String messageId = object.getString("message_id");
JSONObject data = object.getJSONObject("data");
//支付渠道。取值:UNKNOWN - 未知|WECHAT-微信|ALIPAY-支付宝
String channel = data.getString("channel");
//订单支付状态。 取值: PROCESSING-处理中|SUCCESS-成功|FAILED-失败
String status = data.getString("status");
//订单金额
Integer orderAmount = data.getInteger("order_amount");
//用户侧支付页交易单号
String tradeNo = data.getString("trade_no");
//商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
String outOrderNo = data.getString("out_order_no");
//快手小程序平台订单号。
String ksOrderNo = data.getString("ks_order_no");
//回调支付成功
if ("SUCCESS".equals(status)) {
//处理业务
PayNotifyCheckDto payNotifyCheckDto = new PayNotifyCheckDto();
payNotifyCheckDto.setPayType(EnumPayType.KS_PAY);
payNotifyCheckDto.setPayAmount(BaseWxPayResult.fenToYuan(orderAmount));
payNotifyCheckDto.setPayId(outOrderNo);
payNotifyCheckDto.setMsg(messageId);
String notifyCheck = super.payNotifyCheck(payNotifyCheckDto);
if (StringUtils.isNotBlank(notifyCheck)){
return notifyCheck;
}
log.info("快手支付回调,交易正常:封装参数修改内部支付单信息");
EditPayInfoNotifyDto editPayInfoNotifyDto = new EditPayInfoNotifyDto();
editPayInfoNotifyDto.setPayAmount(BaseWxPayResult.fenToYuan(orderAmount));
editPayInfoNotifyDto.setPayTime(DateUtil.formatDateTime(new Date()));
editPayInfoNotifyDto.setAppId(appId);
editPayInfoNotifyDto.setPayId(outOrderNo);
editPayInfoNotifyDto.setPayNo(ksOrderNo);
editPayInfoNotifyDto.setTradeNo(tradeNo);
editPayInfoNotifyDto.setPayChannel(channel);
payInfoService.editPayInfoNotify(editPayInfoNotifyDto);
//正确处理后返回以下内容格式通知小程序平台不再持续回调
return super.buildNotifyCheckResultMsg(EnumPayType.KS_PAY,true,messageId);
}else {
log.info("快手支付回调不是支付成功不做处理:支付状态:{},payId:{}",status,object.getString("trade_no"));
}
} else {
log.info("快手支付回调签名校验失败,payId:{}",object.getString("trade_no"));
}
} else {
log.info("快手支付回调参数丢失,payId:{}",object.getString("trade_no"));
}
return super.buildNotifyCheckResultMsg(EnumPayType.KS_PAY,true,"business fail");
}
}

19
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/WxPayStrategy.java

@ -6,6 +6,7 @@ import com.alibaba.fastjson.TypeReference;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.msg.MsgConstants;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.bnyer.common.core.context.SecurityContextHolder;
import com.bnyer.common.core.domain.AlipayConfig;
import com.bnyer.common.core.domain.WxpayConfig;
import com.bnyer.common.core.enums.EnumPayType;
@ -53,6 +54,9 @@ public class WxPayStrategy extends AbstractPayStrategy {
@Autowired
private WxPayManager wxPayManager;
@Autowired
private PayInfoService payInfoService;
@Override
public PayInOrderVo unifiedOrder(UnifiedOrderDto dto) {
log.info("微信支付:统一下单接口调用开始,WxPayStrategy.unifiedOrder dto:{}",JSON.toJSONString(dto));
@ -78,10 +82,11 @@ public class WxPayStrategy extends AbstractPayStrategy {
private PayInOrderVo jsApiPay(UnifiedOrderDto dto) {
WxpayConfig wxPayConfig = wxPayManager.getWxPayConfigByTradeType(dto.getTradeType());
WxPayService wxPayService = wxPayManager.getWxPayService(wxPayConfig);
String openId = SecurityContextHolder.get("wxCode");
try {
WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
orderRequest.setOpenid(dto.getOpenId());
orderRequest.setOutTradeNo(dto.getOrderId());
orderRequest.setOpenid(openId);
orderRequest.setOutTradeNo(dto.getPayId());
orderRequest.setBody(dto.getGoodsDesc());
orderRequest.setSpbillCreateIp(dto.getIp());
orderRequest.setTotalFee(BaseWxPayRequest.yuanToFen(dto.getPayAmount()));
@ -93,7 +98,7 @@ public class WxPayStrategy extends AbstractPayStrategy {
}
//返回数据
PayInOrderVo payInOrderVo = new PayInOrderVo();
payInOrderVo.setOrderId(dto.getOrderId());
payInOrderVo.setPayId(dto.getPayId());
payInOrderVo.setAppid(wxPayConfig.getAppid());
payInOrderVo.setMchid(wxPayConfig.getMchid());
payInOrderVo.setPrepayid(wxPayUnifiedOrderResult.getPrepayId());
@ -103,7 +108,7 @@ public class WxPayStrategy extends AbstractPayStrategy {
payInOrderVo.setTradeType(wxPayUnifiedOrderResult.getTradeType());
return payInOrderVo;
} catch (WxPayException e) {
log.error("微信支付:统一下单接口调用失败,orderId:{},error{}", dto.getOrderId(), e.getMessage());
log.error("微信支付:统一下单接口调用失败,payId:{},error{}", dto.getPayId(), e.getMessage());
throw new ServiceException(ResponseEnum.PAY_FAILS);
}
}
@ -116,7 +121,6 @@ public class WxPayStrategy extends AbstractPayStrategy {
@Override
public String parsePayNotify(String params) {
log.info("微信支付回调开始: WxPayStrategy.parsePayNotify params:{}",params);
PayInfoService payInfoService = SpringUtils.getBean(PayInfoService.class);
Map<String, String> inMap = null;
try {
inMap = WXPayUtil.xmlToMap(params);
@ -146,6 +150,7 @@ public class WxPayStrategy extends AbstractPayStrategy {
payNotifyCheckDto.setPayType(EnumPayType.WX_PAY);
payNotifyCheckDto.setPayId(outTradeNo);
payNotifyCheckDto.setPayAmount(fenToYuan);
payNotifyCheckDto.setMsg("OK");
String notifyCheck = super.payNotifyCheck(payNotifyCheckDto);
if (StringUtils.isNotBlank(notifyCheck)){
return notifyCheck;
@ -158,10 +163,10 @@ public class WxPayStrategy extends AbstractPayStrategy {
editPayInfoNotifyDto.setPayId(outTradeNo);
editPayInfoNotifyDto.setPayNo(transactionId);
payInfoService.editPayInfoNotify(editPayInfoNotifyDto);
return WxPayNotifyResponse.success("OK");
return super.buildNotifyCheckResultMsg(EnumPayType.WX_PAY,true,"OK");
} catch (Exception e) {
log.error("微信支付回调结果异常,异常原因{}", e.getMessage());
return WxPayNotifyResponse.fail(e.getMessage());
return super.buildNotifyCheckResultMsg(EnumPayType.WX_PAY,false,e.getMessage());
}
}
}

37
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/dto/EditPayInfoSingleDto.java

@ -0,0 +1,37 @@
package com.bnyer.pay.dto;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.apache.ibatis.annotations.Param;
/**
* @author :WXC
* @Date :2023/04/24
* @description :
*/
@Getter
@Setter
@NoArgsConstructor
public class EditPayInfoSingleDto {
/**
* 支付单号:商户侧
*/
private String payId;
/**
* 支付单号第三方返回
*/
private String payNo;
/**
* 快手用户侧订单号/抖音支付宝微信侧单号第三方返回
*/
private String tradeNo;
/**
* 支付时间
*/
private String payTime;
/**
* 支付渠道
*/
private String payChannel;
}

5
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/dto/PayNotifyCheckDto.java

@ -29,4 +29,9 @@ public class PayNotifyCheckDto {
* 支付金额
*/
private String payAmount;
/**
* 消息
*/
private String msg;
}

8
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/dto/UnifiedOrderDto.java

@ -17,9 +17,9 @@ import java.util.Date;
public class UnifiedOrderDto {
/**
* 业务主订单id关联内部业务订单表
* 内部支付单号
*/
private String orderId;
private String payId;
/**
* trade_type=JSAPI此参数必传用户在商户appid下的唯一标识
*/
@ -40,6 +40,10 @@ public class UnifiedOrderDto {
* 交易类型 1--JSAPI支付小程序appId支付2--Native支付3--app支付4--JSAPI支付公众号appId支付 6-H5支付 ,必要参数
*/
private String tradeType;
/**
* 商品类型快手抖音支付需要
*/
private Integer goodsType;
/**
* 商品标题会员充值
*/

13
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/mapper/DypayConfigMapper.java

@ -0,0 +1,13 @@
package com.bnyer.pay.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bnyer.common.core.domain.DypayConfig;
import org.apache.ibatis.annotations.Mapper;
/**
* @author :WXC
* @description :
*/
@Mapper
public interface DypayConfigMapper extends BaseMapper<DypayConfig> {
}

13
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/mapper/KspayConfigMapper.java

@ -0,0 +1,13 @@
package com.bnyer.pay.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bnyer.common.core.domain.KspayConfig;
import org.apache.ibatis.annotations.Mapper;
/**
* @author :WXC
* @description :
*/
@Mapper
public interface KspayConfigMapper extends BaseMapper<KspayConfig> {
}

5
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/mapper/PayInfoMapper.java

@ -2,6 +2,7 @@ package com.bnyer.pay.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bnyer.common.core.domain.PayInfo;
import com.bnyer.pay.dto.EditPayInfoSingleDto;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@ -13,9 +14,7 @@ import org.apache.ibatis.annotations.Param;
public interface PayInfoMapper extends BaseMapper<PayInfo> {
/**
* 修改支付订单状态和对账信息
* @param payId
* @param payNo
* @return
*/
int editPayInfoForSingle(@Param("payId") String payId, @Param("payTime") String payTime, @Param("payNo") String payNo);
int editPayInfoForSingle(EditPayInfoSingleDto dto);
}

12
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/DypayConfigService.java

@ -0,0 +1,12 @@
package com.bnyer.pay.service;
import com.bnyer.common.core.domain.DypayConfig;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author :WXC
* @description :
*/
public interface DypayConfigService extends IService<DypayConfig>{
}

12
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/KspayConfigService.java

@ -0,0 +1,12 @@
package com.bnyer.pay.service;
import com.bnyer.common.core.domain.KspayConfig;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author :WXC
* @description :
*/
public interface KspayConfigService extends IService<KspayConfig>{
}

15
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/DypayConfigServiceImpl.java

@ -0,0 +1,15 @@
package com.bnyer.pay.service.impl;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bnyer.common.core.domain.DypayConfig;
import com.bnyer.pay.mapper.DypayConfigMapper;
import com.bnyer.pay.service.DypayConfigService;
/**
* @author :WXC
* @description :
*/
@Service
public class DypayConfigServiceImpl extends ServiceImpl<DypayConfigMapper, DypayConfig> implements DypayConfigService {
}

15
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/KspayConfigServiceImpl.java

@ -0,0 +1,15 @@
package com.bnyer.pay.service.impl;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bnyer.common.core.domain.KspayConfig;
import com.bnyer.pay.mapper.KspayConfigMapper;
import com.bnyer.pay.service.KspayConfigService;
/**
* @author :WXC
* @description :
*/
@Service
public class KspayConfigServiceImpl extends ServiceImpl<KspayConfigMapper, KspayConfig> implements KspayConfigService {
}

51
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/PayInfoServiceImpl.java

@ -8,18 +8,22 @@ import com.bnyer.common.core.context.SecurityContextHolder;
import com.bnyer.common.core.domain.PayInfo;
import com.bnyer.common.core.domain.R;
import com.bnyer.common.core.enums.EnumSceneCode;
import com.bnyer.common.core.enums.EnumUserClientType;
import com.bnyer.common.core.enums.ResponseEnum;
import com.bnyer.common.core.exception.ServiceException;
import com.bnyer.common.core.utils.OrderUtil;
import com.bnyer.common.core.utils.bean.EntityConvertUtil;
import com.bnyer.common.core.utils.ip.IpUtils;
import com.bnyer.common.rocketmq.config.RocketMqConstant;
import com.bnyer.img.api.dto.QueryVipOrderDto;
import com.bnyer.img.api.remote.RemoteVipOrderService;
import com.bnyer.img.api.vo.VipOrderVo;
import com.bnyer.pay.constant.KSPayConstants;
import com.bnyer.pay.design.factory.PayFactory;
import com.bnyer.pay.design.strategy.IPayStrategy;
import com.bnyer.pay.dto.AddPayInfoDto;
import com.bnyer.pay.dto.EditPayInfoNotifyDto;
import com.bnyer.pay.dto.EditPayInfoSingleDto;
import com.bnyer.pay.dto.UnifiedOrderDto;
import com.bnyer.pay.mapper.PayInfoMapper;
import com.bnyer.pay.service.PayInfoService;
@ -67,6 +71,10 @@ public class PayInfoServiceImpl extends ServiceImpl<PayInfoMapper, PayInfo> impl
public PayInOrderVo addPayInOrder(AddPayInfoDto dto, HttpServletRequest request) {
//支付金额
String payAmount = "";
//商品类型:快手支付需要
int goodsType;
//payId
String payId;
EnumSceneCode enumSceneCode = EnumSceneCode.getSceneCodeByCode(dto.getSceneCode());
switch (enumSceneCode){
//会员充值场景
@ -80,12 +88,15 @@ public class PayInfoServiceImpl extends ServiceImpl<PayInfoMapper, PayInfo> impl
}
VipOrderVo vipOrderVo = vipOrderVoListR.getData().get(0);
payAmount = vipOrderVo.getPayAmount().toString();
goodsType = KSPayConstants.GOODS_TYPE_VIP;
payId = OrderUtil.getOrderId("RV",new Date(), EnumUserClientType.getCodeByType(vipOrderVo.getUserClientType())
,String.valueOf(vipOrderVo.getUserId()));
break;
default:
throw new ServiceException("sceneCode未匹配上对应支付场景");
}
//构建统一下单请求实体
UnifiedOrderDto unifiedOrderDto = buildUnifiedOrderDto(dto, payAmount, request);
UnifiedOrderDto unifiedOrderDto = buildUnifiedOrderDto(dto, goodsType,payAmount,payId, request);
//下单,获取第三方返回信息
IPayStrategy IPayStrategy = PayFactory.getInstance().getConcreteStrategy(dto.getPayType());
PayInOrderVo payInOrderVo = IPayStrategy.unifiedOrder(unifiedOrderDto);
@ -105,12 +116,11 @@ public class PayInfoServiceImpl extends ServiceImpl<PayInfoMapper, PayInfo> impl
*/
private PayInfo buildPayInfo(PayInOrderVo payInOrderVo, UnifiedOrderDto unifiedOrderDto, AddPayInfoDto dto) {
PayInfo payInfo = new PayInfo();
payInfo.setPayId(payInOrderVo.getOrderId());
payInfo.setOrderId(dto.getOrderId());
payInfo.setSceneCode(dto.getSceneCode());
payInfo.setRemark(dto.getRemark());
payInfo.setPayType(dto.getPayType());
payInfo.setPayId(unifiedOrderDto.getOrderId());
payInfo.setPayId(unifiedOrderDto.getPayId());
payInfo.setPayAmount(new BigDecimal(unifiedOrderDto.getPayAmount()));
payInfo.setAppid(payInOrderVo.getAppid());
payInfo.setGoodsSubject(unifiedOrderDto.getGoodsSubject());
@ -124,22 +134,25 @@ public class PayInfoServiceImpl extends ServiceImpl<PayInfoMapper, PayInfo> impl
/**
* 构建统一下单请求实体
* @param dto 入参
*
* @param dto 入参
* @param goodsType
* @param payAmount 不同支付场景下的支付金额
* @param request 请求request用于获取ip地址
* @param payId 内部系统支付单号
* @param request 请求request用于获取ip地址
* @return
*/
private UnifiedOrderDto buildUnifiedOrderDto(AddPayInfoDto dto, String payAmount, HttpServletRequest request) {
private UnifiedOrderDto buildUnifiedOrderDto(AddPayInfoDto dto, int goodsType, String payAmount, String payId, HttpServletRequest request) {
//当前时间
Date currDate = new Date();
//ip地址
String ip = IpUtils.getIpAddr(request);
String wxCode = SecurityContextHolder.get("wxCode");
UnifiedOrderDto unifiedOrderDto = EntityConvertUtil.copy(dto, UnifiedOrderDto.class);
unifiedOrderDto.setPayId(payId);
unifiedOrderDto.setIp(ip);
unifiedOrderDto.setCurrDate(currDate);
unifiedOrderDto.setPayAmount(payAmount);
unifiedOrderDto.setOpenId(wxCode);
unifiedOrderDto.setGoodsType(goodsType);
return unifiedOrderDto;
}
@ -147,21 +160,20 @@ public class PayInfoServiceImpl extends ServiceImpl<PayInfoMapper, PayInfo> impl
* 修改支付单并发消息到订单系统修改订单状态
* @param editPayInfoNotifyDto
*/
@Transactional(propagation = Propagation.REQUIRED,rollbackFor=Exception.class)
@Transactional(rollbackFor=Exception.class)
@Override
public void editPayInfoNotify(EditPayInfoNotifyDto editPayInfoNotifyDto) {
log.info("开始修改内部支付订单:回调入参, editPayInfoNotifyDto:{}",JSON.toJSONString(editPayInfoNotifyDto));
String payId = editPayInfoNotifyDto.getPayId();
String payNo = editPayInfoNotifyDto.getPayNo();
String payTime = editPayInfoNotifyDto.getPayTime();
PayInfo payInfo = payInfoMapper.selectOne(new LambdaQueryWrapper<PayInfo>().eq(PayInfo::getPayId, payId));
if (Objects.isNull(payInfo)){
log.error("查询支付订单数据返回失败");
throw new ServiceException("查询支付订单数据返回失败");
}
int i = payInfoMapper.editPayInfoForSingle(payId,payTime,payNo);
//修改支付单信息
EditPayInfoSingleDto editPayInfoSingleDto = buildEditPayInfoSingleDto(editPayInfoNotifyDto);
int i = payInfoMapper.editPayInfoForSingle(editPayInfoSingleDto);
if(i==0){
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
log.error("内部支付订单修改失败,payId:{}",payId);
throw new ServiceException("内部支付订单修改失败");
}
@ -197,4 +209,17 @@ public class PayInfoServiceImpl extends ServiceImpl<PayInfoMapper, PayInfo> impl
break;
}
}
/**
* 构建修改支付单信息实体
* @param editPayInfoNotifyDto
* @return
*/
private EditPayInfoSingleDto buildEditPayInfoSingleDto(EditPayInfoNotifyDto editPayInfoNotifyDto) {
EditPayInfoSingleDto editPayInfoSingleDto = new EditPayInfoSingleDto();
editPayInfoSingleDto.setPayId(editPayInfoNotifyDto.getPayId());
editPayInfoSingleDto.setPayTime(editPayInfoNotifyDto.getPayTime());
editPayInfoSingleDto.setPayChannel(editPayInfoSingleDto.getPayChannel());
return editPayInfoSingleDto;
}
}

138
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/utils/DYPayUtil.java

@ -0,0 +1,138 @@
package com.bnyer.pay.utils;
import org.springframework.stereotype.Component;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
/**
* @author :WXC
* @Date :2023/04/23
* @description :抖音支付工具类
*/
@Component
public class DYPayUtil {
/**
* 发起请求时的签名
*/
public String getSign(Map<String, Object> paramsMap,String salt) {
List<String> paramsArr = new ArrayList<>();
for (Map.Entry<String, Object> entry : paramsMap.entrySet()) {
String key = entry.getKey();
if (key.equals("other_settle_params")) {
continue;
}
String value = entry.getValue().toString();
value = value.trim();
if (value.startsWith("\"") && value.endsWith("\"") && value.length() > 1) {
value = value.substring(1, value.length() - 1);
}
value = value.trim();
if (value.equals("") || value.equals("null")) {
continue;
}
switch (key) {
// 字段用于标识身份,不参与签名
case "app_id":
case "thirdparty_id":
case "sign":
break;
default:
paramsArr.add(value);
break;
}
}
// 支付密钥值
paramsArr.add(salt);
Collections.sort(paramsArr);
StringBuilder signStr = new StringBuilder();
String sep = "";
for (String s : paramsArr) {
signStr.append(sep).append(s);
sep = "&";
}
return md5FromStr(signStr.toString());
}
public String md5FromStr(String inStr) {
MessageDigest md5;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return "";
}
byte[] byteArray = inStr.getBytes(StandardCharsets.UTF_8);
byte[] md5Bytes = md5.digest(byteArray);
StringBuilder hexValue = new StringBuilder();
for (byte md5Byte : md5Bytes) {
int val = ((int) md5Byte) & 0xff;
if (val < 16) {
hexValue.append("0");
}
hexValue.append(Integer.toHexString(val));
}
return hexValue.toString();
}
/**
* 回调验证签名
*/
public String getCallbackSignature(int timestamp, String nonce, String msg,String token) {
List<String> sortedString = new ArrayList<>();
sortedString.add(String.valueOf(timestamp));
sortedString.add(nonce);
sortedString.add(msg);
sortedString.add(token);
Collections.sort(sortedString);
StringBuilder sb = new StringBuilder();
sortedString.forEach(sb::append);
return getSha1(sb.toString().getBytes());
}
public String getSha1(byte[] input) {
MessageDigest mDigest;
try {
mDigest = MessageDigest.getInstance("SHA1");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return "";
}
byte[] result = mDigest.digest(input);
StringBuilder sb = new StringBuilder();
for (byte b : result) {
sb.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
}
return sb.toString();
}
/**
* 手机号登陆信息解密
*/
public String decrypt(String encryptedData, String sessionKey, String iv) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidKeyException {
Base64.Decoder decoder = Base64.getDecoder();
byte[] sessionKeyBytes = decoder.decode(sessionKey);
byte[] ivBytes = decoder.decode(iv);
byte[] encryptedBytes = decoder.decode(encryptedData);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec skeySpec = new SecretKeySpec(sessionKeyBytes, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivSpec);
byte[] ret = cipher.doFinal(encryptedBytes);
return new String(ret);
}
}

133
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/utils/KSPayUtil.java

@ -0,0 +1,133 @@
package com.bnyer.pay.utils;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;
import static javax.crypto.Cipher.DECRYPT_MODE;
/**
* @author :WXC
* @Date :2023/04/23
* @description :快手支付工具类
*/
@Slf4j
@Component
public class KSPayUtil {
/**
* 快手小程序返回的加密数据的解密函数
*
* @param sessionKey 有效的sessionKey通过 login code 置换
* @param encryptedData 返回的加密数据base64编码
* @param iv 返回的加密IVbase64编码
* @return 返回解密的字符串数据
*/
public String decrypt(String sessionKey, String encryptedData, String iv) {
// Base64解码数据
byte[] aesKey = Base64.decodeBase64(sessionKey);
byte[] ivBytes = Base64.decodeBase64(iv);
byte[] cipherBytes = Base64.decodeBase64(encryptedData);
byte[] plainBytes = decrypt0(aesKey, ivBytes, cipherBytes);
return new String(plainBytes, StandardCharsets.UTF_8);
}
/**
* AES解密函数. 使用 AES/CBC/PKCS5Padding 模式
*
* @param aesKey 密钥长度16
* @param iv 偏移量长度16
* @param cipherBytes 密文信息
* @return 明文
*/
private byte[] decrypt0(byte[] aesKey, byte[] iv, byte[] cipherBytes) {
SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv);
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(DECRYPT_MODE, keySpec, ivSpec);
return cipher.doFinal(cipherBytes);
} catch (Exception e) {
log.error("decrypt error:{}", e.getMessage());
throw new RuntimeException(e);
}
}
/**
* https://mp.kuaishou.com/docs/develop/server/epay/interfaceDefinition.html
* <p>
* https://mp.kuaishou.com/docs/develop/server/payment/serverSignature.html
* 支付签名
*/
public String buildMd5(Map<String, String> dataMap, String appSecret) {
String signStr = genSignStr(dataMap);
return DigestUtils.md5Hex(signStr + appSecret);
}
private String genSignStr(Map<String, String> data) {
StringBuilder sb = new StringBuilder();
data.keySet().stream().sorted()
.filter(key -> StringUtils.isNotBlank(key) && StringUtils.isNotEmpty(data.get(key)))
.forEach(key -> {
sb.append(key);
sb.append("=");
sb.append(data.get(key));
sb.append("&");
});
if (sb.length() > 0) {
sb.deleteCharAt(sb.length() - 1);
}
return sb.toString();
}
/**
* 获取参数 Map 的签名结果
* https://mp.kuaishou.com/docs/develop/server/epay/appendix.html
*
* @param signParamsMap 含义见上述示例
* @return 返回签名结果
*/
public String calcSign(Map<String, Object> signParamsMap, String secret) {
// 去掉 value 为空的
Map<String, Object> trimmedParamMap = signParamsMap.entrySet()
.stream()
.filter(item -> !Strings.isNullOrEmpty(item.getValue().toString()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
// 按照字母排序
Map<String, Object> sortedParamMap = trimmedParamMap.entrySet()
.stream()
.sorted(Map.Entry.comparingByKey())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(oldValue, newValue) -> oldValue, LinkedHashMap::new));
// 组装成待签名字符串。(注,引用了guava工具)
String paramStr = Joiner.on("&").withKeyValueSeparator("=").join(sortedParamMap.entrySet());
String signStr = paramStr + secret;
// 生成签名返回。(注,引用了commons-codec工具)
return DigestUtils.md5Hex(signStr);
}
}

107
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/utils/PayRestTemplateUtil.java

@ -0,0 +1,107 @@
package com.bnyer.pay.utils;
import com.alibaba.fastjson.JSONObject;
import com.bnyer.common.core.domain.KspayConfig;
import com.bnyer.pay.constant.KSPayConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
/**
* @author :WXC
* @Date :2023/04/24
* @description :
*/
@Slf4j
@Component
public class PayRestTemplateUtil {
@Autowired
private RestTemplate restTemplate;
/**
* 快手小程序post请求
* code2session
*/
public String ksPostRequestUrlencoded(JSONObject jsonObject, String url) {
String result = "";
try {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<String> formEntity = new HttpEntity<>(MessageFormat.format("js_code={0}&app_id={1}&&app_secret={2}", jsonObject.get("js_code"), jsonObject.get("appid"), jsonObject.get("secret")), headers);
result = restTemplate.postForObject(url, formEntity, String.class);
} catch (Exception e) {
log.error("快手小程序post请求异常{}", url);
log.error("post请求异常:{}", e.getMessage());
e.printStackTrace();
}
return result;
}
/**
* 快手小程序获取accessToken
*/
public String ksPostRequestUrlencoded(KspayConfig kspayConfig) {
String result = "";
try {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<String> formEntity = new HttpEntity<>(MessageFormat.format("app_id={0}&app_secret={1}&&grant_type={2}"
, kspayConfig.getAppid(), kspayConfig.getSecret(), "client_credentials"), headers);
result = restTemplate.postForObject(KSPayConstants.GET_ACCESS_TOKEN, formEntity, String.class);
} catch (Exception e) {
log.error("快手小程序post请求异常{}", KSPayConstants.GET_ACCESS_TOKEN);
log.error("post请求异常:{}", e.getMessage());
e.printStackTrace();
}
return result;
}
/**
* 快手
* 支付 退款 结算
*/
public String ksPostRequestJson(JSONObject jsonObject, String url, String appId, String accessToken) {
String result = "";
try {
Map<String, String> map = new HashMap<>();
map.put("app_id", appId);
map.put("access_token", accessToken);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Object> formEntity = new HttpEntity<>(jsonObject, headers);
result = restTemplate.postForObject(url + "/?app_id={app_id}&access_token={access_token}", formEntity, String.class, map);
} catch (Exception e) {
log.error("快手小程序post请求异常{}", url);
log.error("post请求异常:{}", e.getMessage());
e.printStackTrace();
}
return result;
}
/**
* 抖音小程序post请求
*/
public String dyPostRequest(JSONObject jsonObject, String url) {
String result = "";
try {
HttpHeaders headers = new HttpHeaders();
//所有的请求需要用JSON格式发送
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Object> formEntity = new HttpEntity<>(jsonObject, headers);
result = restTemplate.postForObject(url, formEntity, String.class);
} catch (Exception e) {
log.error("抖音小程序post请求异常{}", url);
e.printStackTrace();
}
return result;
}
}

11
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/vo/PayInOrderVo.java

@ -39,9 +39,12 @@ public class PayInOrderVo {
@ApiModelProperty(value = "支付宝")
private String outStr;
@ApiModelProperty(value = "单号")
private String orderId;
@ApiModelProperty(value = "内部系统支付单号")
private String payId;
@ApiModelProperty(value = "调微信支付接口地址")
private String mwebUrl;
@ApiModelProperty(value = "第三方返回支付单号,抖音快手支付需要用到")
private String payOrderId;
@ApiModelProperty(value = "快手、抖音支付")
private String orderToken;
}

21
bnyer-services/bnyer-pay/src/main/resources/com/bnyer/pay/mapper/DypayConfigMapper.xml

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bnyer.pay.mapper.DypayConfigMapper">
<resultMap id="BaseResultMap" type="com.bnyer.common.core.domain.DypayConfig">
<!--@mbg.generated-->
<!--@Table pay_dypay_config-->
<id column="id" jdbcType="BIGINT" property="id" />
<result column="appid" jdbcType="VARCHAR" property="appid" />
<result column="salt" jdbcType="VARCHAR" property="salt" />
<result column="token" jdbcType="VARCHAR" property="token" />
<result column="backurl" jdbcType="VARCHAR" property="backurl" />
<result column="status" jdbcType="CHAR" property="status" />
<result column="remark" jdbcType="VARCHAR" property="remark" />
<result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
<result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
</resultMap>
<sql id="Base_Column_List">
<!--@mbg.generated-->
id, appid,salt, token, backurl, `status`, remark, create_time, update_time
</sql>
</mapper>

20
bnyer-services/bnyer-pay/src/main/resources/com/bnyer/pay/mapper/KspayConfigMapper.xml

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bnyer.pay.mapper.KspayConfigMapper">
<resultMap id="BaseResultMap" type="com.bnyer.common.core.domain.KspayConfig">
<!--@mbg.generated-->
<!--@Table pay_kspay_config-->
<id column="id" jdbcType="BIGINT" property="id" />
<result column="appid" jdbcType="VARCHAR" property="appid" />
<result column="secret" jdbcType="VARCHAR" property="secret" />
<result column="backurl" jdbcType="VARCHAR" property="backurl" />
<result column="status" jdbcType="CHAR" property="status" />
<result column="remark" jdbcType="VARCHAR" property="remark" />
<result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
<result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
</resultMap>
<sql id="Base_Column_List">
<!--@mbg.generated-->
id, appid, secret, backurl, `status`, remark, create_time, update_time
</sql>
</mapper>

18
bnyer-services/bnyer-pay/src/main/resources/com/bnyer/pay/mapper/PayInfoMapper.xml

@ -11,7 +11,9 @@
<result column="single_status" jdbcType="INTEGER" property="singleStatus" />
<result column="single_time" jdbcType="TIMESTAMP" property="singleTime" />
<result column="pay_type" jdbcType="VARCHAR" property="payType" />
<result column="pay_channel" jdbcType="VARCHAR" property="payChannel" />
<result column="pay_no" jdbcType="VARCHAR" property="payNo" />
<result column="trade_no" jdbcType="VARCHAR" property="tradeNo" />
<result column="appid" jdbcType="VARCHAR" property="appid" />
<result column="goods_subject" jdbcType="VARCHAR" property="goodsSubject" />
<result column="goods_desc" jdbcType="VARCHAR" property="goodsDesc" />
@ -30,7 +32,7 @@
</resultMap>
<sql id="Base_Column_List">
<!--@mbg.generated-->
id, pay_id, order_id, pay_status,single_status,single_time, pay_type, pay_no, appid, goods_subject, goods_desc,
id, pay_id, order_id, pay_status,single_status,single_time, pay_type, pay_channel, pay_no, trade_no, appid, goods_subject, goods_desc,
pay_amount, pay_time, scene_code, ip, third_code, third_msg, third_no, remark, create_time,
update_time,sort,is_show
</sql>
@ -38,9 +40,19 @@
<!-- 修改支付订单状态和对账信息-->
<update id="editPayInfoForSingle">
update pay_pay_info
<if test="payNo != null and payNo != ''">
set pay_no = #{payNo},
</if>
<if test="tradeNo != null and tradeNo != ''">
set trade_no = #{tradeNo},
</if>
<if test="payTime != null and payTime != ''">
set pay_time = STR_TO_DATE(#{payTime},'%Y%m%d%H%i%s'),
</if>
<if test="payChannel != null and payChannel != ''">
set pay_channel = #{payChannel},
</if>
set order_status = 1001,
set pay_time = STR_TO_DATE(#{payTime},'%Y%m%d%H%i%s'),
set pay_no = #{payNo}
set single_status = 1003,
set single_time = now()
where pay_id = #{payId}

Loading…
Cancel
Save