33 changed files with 1416 additions and 190 deletions
@ -0,0 +1,21 @@ |
|||
package com.bnyer.common.rocketmq.domain.pay; |
|||
|
|||
import lombok.Getter; |
|||
import lombok.NoArgsConstructor; |
|||
import lombok.Setter; |
|||
|
|||
/** |
|||
* @author :WXC |
|||
* @description : |
|||
*/ |
|||
@Getter |
|||
@Setter |
|||
@NoArgsConstructor |
|||
public class ThirdPayOrderPushMessage { |
|||
|
|||
/** |
|||
* 支付单号 |
|||
*/ |
|||
private String payId; |
|||
|
|||
} |
|||
@ -0,0 +1,220 @@ |
|||
package com.bnyer.pay.bean.bo; |
|||
|
|||
import com.bnyer.pay.enums.EnumDyOrderStatus; |
|||
import com.google.gson.annotations.SerializedName; |
|||
import lombok.Getter; |
|||
import lombok.NoArgsConstructor; |
|||
import lombok.Setter; |
|||
|
|||
import java.io.Serializable; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 抖音推送订单入参 |
|||
*/ |
|||
@Getter |
|||
@Setter |
|||
@NoArgsConstructor |
|||
public class DyPushOrderBo implements Serializable { |
|||
|
|||
private static final long serialVersionUID = 1866990048913415295L; |
|||
|
|||
/** |
|||
* 第三方在抖音开放平台申请的 ClientKey |
|||
* 注意:POI 订单必传 |
|||
* 是否必填:否 |
|||
*/ |
|||
@SerializedName(value = "client_key") |
|||
private String clientKey; |
|||
|
|||
/** |
|||
* 服务端 API 调用标识,通过 getAccessToken 获取 |
|||
* 是否必填:是 |
|||
*/ |
|||
@SerializedName(value = "access_token") |
|||
private String accessToken; |
|||
|
|||
/** |
|||
* POI 店铺同步时使用的开发者侧店铺 ID,购买店铺 ID,长度 < 256 byte |
|||
* |
|||
* 注意:POI 订单必传 |
|||
* 是否必填:否 |
|||
*/ |
|||
@SerializedName(value = "ext_shop_id") |
|||
private String extShopId; |
|||
|
|||
/** |
|||
* 做订单展示的字节系 app 名称,目前为固定值“douyin” |
|||
* 是否必填:是 |
|||
*/ |
|||
@SerializedName(value = "app_name") |
|||
private String appName; |
|||
|
|||
/** |
|||
* 小程序用户的 open_id,通过 code2Session 获取 |
|||
* 是否必填:是 |
|||
*/ |
|||
@SerializedName(value = "open_id") |
|||
private String openId; |
|||
|
|||
/** |
|||
* json string,根据不同订单类型有不同的结构体,请参见 order_detail 字段说明(json string) |
|||
* 是否必填:是 |
|||
*/ |
|||
@SerializedName(value = "order_detail") |
|||
private String orderDetail; |
|||
|
|||
/** |
|||
* 普通小程序订单订单状态,POI 订单可以忽略 |
|||
0:待支付 |
|||
1:已支付 |
|||
2:已取消(用户主动取消或者超时未支付导致的关单) |
|||
4:已核销(核销状态是整单核销,即一笔订单买了 3 个券,核销是指 3 个券核销的整单) |
|||
5:退款中 |
|||
6:已退款 |
|||
8:退款失败 |
|||
注意:普通小程序订单必传,担保支付分账依赖该状态 |
|||
是否必填:否 |
|||
*/ |
|||
@SerializedName(value = "order_status") |
|||
private Integer orderStatus; |
|||
|
|||
/** |
|||
* 订单类型,枚举值: |
|||
* |
|||
* 0:普通小程序订单(非POI订单) |
|||
* 9101:团购券订单(POI 订单) |
|||
* 9001:景区门票订单(POI订单) |
|||
* 是否必填:是 |
|||
*/ |
|||
@SerializedName(value = "order_type") |
|||
private Integer orderType; |
|||
|
|||
/** |
|||
* 订单信息变更时间,13 位毫秒级时间戳 |
|||
* 是否必填:是 |
|||
*/ |
|||
@SerializedName(value = "update_time") |
|||
private Integer updateTime; |
|||
|
|||
/** |
|||
* 自定义字段,用于关联具体业务场景下的特殊参数,长度 < 2048byte |
|||
* 是否必填:否 |
|||
*/ |
|||
@SerializedName(value = "extra") |
|||
private String extra; |
|||
|
|||
@Getter |
|||
@Setter |
|||
@NoArgsConstructor |
|||
public static class OrderDetails implements Serializable { |
|||
private static final long serialVersionUID = 3320384952853603934L; |
|||
/** |
|||
* 开发者侧业务单号。用作幂等控制。该订单号是和担保支付的支付单号绑定的,也就是预下单时传入的 out_order_no 字段,长度 <= 64byte |
|||
* 是否必填:是 |
|||
*/ |
|||
@SerializedName(value = "order_id") |
|||
private String orderId; |
|||
|
|||
/** |
|||
* 订单创建的时间,13 位毫秒时间戳 |
|||
* 是否必填:是 |
|||
*/ |
|||
@SerializedName(value = "create_time") |
|||
private Integer createTime; |
|||
|
|||
/** |
|||
* 订单状态,建议采用以下枚举值: |
|||
* |
|||
* 待支付 |
|||
* 已支付 |
|||
* 已取消 |
|||
* 已超时 |
|||
* 已核销 |
|||
* 退款中 |
|||
* 已退款 |
|||
* 退款失败 |
|||
* 是否必填:是 |
|||
*/ |
|||
@SerializedName(value = "status") |
|||
private EnumDyOrderStatus status; |
|||
|
|||
/** |
|||
* 订单商品总数 |
|||
* 是否必填:是 |
|||
*/ |
|||
@SerializedName(value = "amount") |
|||
private Integer amount; |
|||
|
|||
/** |
|||
* 订单总价,单位为分 |
|||
* 是否必填:是 |
|||
*/ |
|||
@SerializedName(value = "total_price") |
|||
private Integer totalPrice; |
|||
|
|||
/** |
|||
* 小程序订单详情页 path,长度<=1024 byte |
|||
* 是否必填:是 |
|||
*/ |
|||
@SerializedName(value = "detail_url") |
|||
private String detailUrl; |
|||
|
|||
/** |
|||
* 子订单商品列表,不可为空 |
|||
* 是否必填:是 |
|||
*/ |
|||
@SerializedName(value = "item_list") |
|||
private List<Goods> itemList; |
|||
|
|||
@Getter |
|||
@Setter |
|||
@NoArgsConstructor |
|||
public static class Goods implements Serializable { |
|||
|
|||
private static final long serialVersionUID = 1418159270677457444L; |
|||
|
|||
/** |
|||
* 开发者侧商品 ID,长度 <= 64 byte |
|||
* 是否必填:是 |
|||
*/ |
|||
@SerializedName(value = "item_code") |
|||
private String itemCode; |
|||
|
|||
/** |
|||
* 子订单商品图片 URL,长度 <= 512 byte |
|||
* 是否必填:是 |
|||
*/ |
|||
@SerializedName(value = "img") |
|||
private String img; |
|||
|
|||
/** |
|||
* 子订单商品介绍标题,长度 <= 256 byte |
|||
* 是否必填:是 |
|||
*/ |
|||
@SerializedName(value = "title") |
|||
private String title; |
|||
|
|||
/** |
|||
* 子订单商品介绍副标题,长度 <= 256 byte |
|||
* 是否必填:否 |
|||
*/ |
|||
@SerializedName(value = "sub_title") |
|||
private String subTitle; |
|||
|
|||
/** |
|||
* 单类商品的数目 |
|||
* 是否必填:否 |
|||
*/ |
|||
@SerializedName(value = "amount") |
|||
private Integer amount; |
|||
|
|||
/** |
|||
* 单类商品的总价,单位为分 |
|||
* 是否必填:是 |
|||
*/ |
|||
@SerializedName(value = "price") |
|||
private Integer price; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,93 @@ |
|||
package com.bnyer.pay.bean.bo; |
|||
|
|||
import com.bnyer.common.core.enums.EnumPayStatus; |
|||
import lombok.Getter; |
|||
import lombok.NoArgsConstructor; |
|||
import lombok.Setter; |
|||
|
|||
import java.util.Date; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author :WXC |
|||
* @description : |
|||
*/ |
|||
@Getter |
|||
@Setter |
|||
@NoArgsConstructor |
|||
public class PushOrderBo { |
|||
|
|||
/** |
|||
* 开发者侧业务单号 |
|||
*/ |
|||
private String payId; |
|||
|
|||
/** |
|||
* 订单创建的时间 |
|||
*/ |
|||
private Date createTime; |
|||
|
|||
/** |
|||
* 订单变更的时间 |
|||
*/ |
|||
private Date updateTime; |
|||
|
|||
/** |
|||
* 支付状态 |
|||
*/ |
|||
private EnumPayStatus payStatus; |
|||
|
|||
/** |
|||
* 商品总数 |
|||
*/ |
|||
private Integer goodsNum; |
|||
|
|||
/** |
|||
* 订单总价,单位为分 |
|||
*/ |
|||
private Integer totalPrice; |
|||
|
|||
/** |
|||
* 商品列表 |
|||
*/ |
|||
private List<Goods> goodsList; |
|||
|
|||
/** |
|||
* 商品列表 |
|||
*/ |
|||
@Getter |
|||
@Setter |
|||
@NoArgsConstructor |
|||
public static class Goods{ |
|||
/** |
|||
* 开发者侧商品 ID |
|||
*/ |
|||
private String itemCode; |
|||
|
|||
/** |
|||
* 子订单商品图片 URL |
|||
*/ |
|||
private String img; |
|||
|
|||
/** |
|||
* 子订单商品介绍标题 |
|||
*/ |
|||
private String title; |
|||
|
|||
/** |
|||
* 子订单商品介绍副标题 |
|||
*/ |
|||
private String subTitle; |
|||
|
|||
/** |
|||
* 单类商品的数目 |
|||
*/ |
|||
private Integer goodsNum; |
|||
|
|||
/** |
|||
* 单类商品的总价,单位为分 |
|||
*/ |
|||
private Integer price; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
package com.bnyer.pay.bean.dto; |
|||
|
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import lombok.Getter; |
|||
import lombok.NoArgsConstructor; |
|||
import lombok.Setter; |
|||
|
|||
import javax.validation.constraints.NotBlank; |
|||
|
|||
/** |
|||
* @author :WXC |
|||
* @description : |
|||
*/ |
|||
@Getter |
|||
@Setter |
|||
@NoArgsConstructor |
|||
public class PushOrderDto { |
|||
|
|||
/** |
|||
* 支付单号 |
|||
*/ |
|||
@NotBlank(message = "支付单号不能为空") |
|||
@ApiModelProperty(value = "支付单号") |
|||
private String payId; |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
package com.bnyer.pay.enums; |
|||
|
|||
import com.bnyer.common.core.enums.EnumPayStatus; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Getter; |
|||
|
|||
import java.util.Arrays; |
|||
|
|||
/** |
|||
* @author :WXC |
|||
* @Date :2023/05/05 |
|||
* @description : |
|||
*/ |
|||
@Getter |
|||
@AllArgsConstructor |
|||
public enum EnumDyOrderStatus { |
|||
待支付(1,EnumPayStatus.NO_PAY), |
|||
已支付(2,EnumPayStatus.SUCCESS), |
|||
已取消(3,null), |
|||
已超时(4,null), |
|||
已核销(5,null), |
|||
退款中(6,null), |
|||
已退款(7,null), |
|||
退款失败(8,null), |
|||
; |
|||
|
|||
private final int status; |
|||
|
|||
private final EnumPayStatus payStatus; |
|||
|
|||
public static EnumDyOrderStatus getEnumDyOrderStatusByPayStatus(EnumPayStatus status) { |
|||
return Arrays.stream(values()) |
|||
.filter(dyOrderStatus -> dyOrderStatus.getPayStatus() == status) |
|||
.findFirst().orElseThrow(() -> new SecurityException("status 未匹配上对应的支付状态")); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
package com.bnyer.pay.exception; |
|||
|
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
|
|||
/** |
|||
* @author :WXC |
|||
* @description : |
|||
*/ |
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
public class PayException extends Exception { |
|||
private static final long serialVersionUID = -7296122902130895085L; |
|||
|
|||
/** |
|||
* 自定义错误讯息. |
|||
*/ |
|||
private String customErrorMsg; |
|||
|
|||
/** |
|||
* 错误代码. |
|||
*/ |
|||
private String errCode; |
|||
|
|||
/** |
|||
* 错误代码描述. |
|||
*/ |
|||
private String errCodeDes; |
|||
|
|||
|
|||
public PayException(String customErrorMsg) { |
|||
super(customErrorMsg); |
|||
this.customErrorMsg = customErrorMsg; |
|||
} |
|||
|
|||
public PayException(String customErrorMsg, Throwable tr) { |
|||
super(customErrorMsg, tr); |
|||
this.customErrorMsg = customErrorMsg; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,67 @@ |
|||
package com.bnyer.pay.listener; |
|||
|
|||
import com.bnyer.common.core.exception.ServiceException; |
|||
import com.bnyer.common.rocketmq.constant.RocketMqTopic; |
|||
import com.bnyer.common.rocketmq.domain.BnyerMessage; |
|||
import com.bnyer.common.rocketmq.domain.pay.ThirdPayOrderPushMessage; |
|||
import com.bnyer.common.rocketmq.handle.EnhanceMessageHandler; |
|||
import com.bnyer.pay.bean.dto.PushOrderDto; |
|||
import com.bnyer.pay.service.UnifiedPayService; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; |
|||
import org.apache.rocketmq.spring.core.RocketMQListener; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import javax.annotation.Resource; |
|||
|
|||
/** |
|||
* @author :WXC |
|||
* @description :第三方支付订单推送 |
|||
*/ |
|||
@Slf4j |
|||
@Component |
|||
@RocketMQMessageListener(topic = RocketMqTopic.THIRD_PAY_ORDER_PUSH_TOPIC,consumerGroup = RocketMqTopic.THIRD_PAY_ORDER_PUSH_TOPIC) |
|||
public class ThirdPayOrderPushConsumer extends EnhanceMessageHandler implements RocketMQListener<BnyerMessage> { |
|||
|
|||
|
|||
@Resource |
|||
private UnifiedPayService unifiedPayService; |
|||
|
|||
@Override |
|||
protected void handleMessage(BnyerMessage message) throws Exception { |
|||
super.dispatchMessage(message); |
|||
try { |
|||
ThirdPayOrderPushMessage thirdPayOrderPushMessage = message.getObject(ThirdPayOrderPushMessage.class); |
|||
PushOrderDto pushOrderDto = new PushOrderDto(); |
|||
pushOrderDto.setPayId(thirdPayOrderPushMessage.getPayId()); |
|||
unifiedPayService.pushOrder(pushOrderDto); |
|||
} catch (ServiceException e) { |
|||
log.error("订单推送失败:"+e.getMessage()); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
protected void handleMaxRetriesExceeded(BnyerMessage message) { |
|||
|
|||
} |
|||
|
|||
@Override |
|||
protected boolean filter(BnyerMessage message) { |
|||
return false; |
|||
} |
|||
|
|||
@Override |
|||
protected boolean isRetry() { |
|||
return true; |
|||
} |
|||
|
|||
@Override |
|||
protected boolean throwException() { |
|||
return false; |
|||
} |
|||
|
|||
@Override |
|||
public void onMessage(BnyerMessage message) { |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,182 @@ |
|||
package com.bnyer.pay.manager; |
|||
|
|||
import com.alibaba.fastjson.JSON; |
|||
import com.alibaba.fastjson.JSONObject; |
|||
import com.bnyer.common.core.constant.RedisKeyConstant; |
|||
import com.bnyer.common.core.domain.DypayConfig; |
|||
import com.bnyer.common.core.utils.SpringUtils; |
|||
import com.bnyer.common.core.utils.StringUtils; |
|||
import com.bnyer.common.redis.service.RedisService; |
|||
import com.bnyer.pay.constant.DYPayConstants; |
|||
import com.bnyer.pay.exception.PayException; |
|||
import com.google.gson.JsonElement; |
|||
import com.google.gson.JsonObject; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import me.chanjar.weixin.common.util.json.GsonParser; |
|||
import org.springframework.http.HttpEntity; |
|||
import org.springframework.http.HttpHeaders; |
|||
import org.springframework.http.MediaType; |
|||
import org.springframework.web.client.RestTemplate; |
|||
|
|||
import java.text.MessageFormat; |
|||
import java.util.Objects; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
/** |
|||
* @author :WXC |
|||
* @Date :2023/04/24 |
|||
* @description : |
|||
*/ |
|||
@Slf4j |
|||
public class DyPayManager { |
|||
|
|||
private RestTemplate restTemplate; |
|||
|
|||
private RedisService redisService; |
|||
|
|||
private final DypayConfig dypayConfig; |
|||
|
|||
private final int MAX_RETRY_TIMES = 2; |
|||
|
|||
public DyPayManager(DypayConfig dypayConfig){ |
|||
this.dypayConfig = dypayConfig; |
|||
init(); |
|||
} |
|||
|
|||
private void init() { |
|||
this.restTemplate = SpringUtils.getBean(RestTemplate.class); |
|||
this.redisService = SpringUtils.getBean(RedisService.class); |
|||
} |
|||
|
|||
/** |
|||
* 抖音小程序获取accessToken |
|||
* 为了保障应用的数据安全,只能在开发者服务器使用 AppSecret,如果小程序存在泄露 AppSecret 的问题,字节小程序平台将有可能下架该小程序,并暂停该小程序相关服务。 |
|||
* |
|||
* access_token 是小程序的全局唯一调用凭据,开发者调用小程序支付时需要使用 access_token。access_token 的有效期为 2 个小时,需要定时刷新 access_token,重复获取会导致之前一次获取的 access_token 的有效期缩短为 5 分钟。 |
|||
*/ |
|||
public String getAccessToken(boolean isRefresh) throws PayException { |
|||
if (!isRefresh){ |
|||
Object cacheObject = redisService.getCacheObject(RedisKeyConstant.DY_ACCESS_TOKEN_KEY); |
|||
if (Objects.nonNull(cacheObject)){ |
|||
return cacheObject.toString(); |
|||
} |
|||
} |
|||
String requestStr = ""; |
|||
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}" |
|||
, dypayConfig.getAppid(), dypayConfig.getAppSecret(), "client_credentials"), headers); |
|||
String resultStr = restTemplate.postForObject(DYPayConstants.GET_ACCESS_TOKEN, formEntity, String.class); |
|||
String url = DYPayConstants.GET_ACCESS_TOKEN; |
|||
requestStr = formEntity.getBody(); |
|||
if (StringUtils.isBlank(resultStr) || !resultStr.startsWith("{")){ |
|||
log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, requestStr, resultStr); |
|||
throw convertDyException(GsonParser.parse("第三方返回格式有误!")); |
|||
} |
|||
JSONObject resultObj = JSONObject.parseObject(resultStr); |
|||
String errNo = StringUtils.isNotBlank(resultObj.getString("err_no"))?resultObj.getString("err_no"):resultObj.getString("err_code"); |
|||
if ("0".equals(errNo)) { |
|||
log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, resultObj, resultStr); |
|||
//存入redis
|
|||
String accessToken = resultObj.getJSONObject("data").getString("access_token"); |
|||
String expiresIn = resultObj.getJSONObject("data").getString("expires_in"); |
|||
redisService.setCacheObject(RedisKeyConstant.DY_ACCESS_TOKEN_KEY,accessToken,Long.parseLong(expiresIn), TimeUnit.SECONDS); |
|||
return accessToken; |
|||
}else { |
|||
throw convertDyException(GsonParser.parse(resultStr)); |
|||
} |
|||
} catch (Exception e) { |
|||
log.error("\n【请求地址】:{}\n【请求数据】:{}\n【异常信息】:{}", DYPayConstants.GET_ACCESS_TOKEN, requestStr, e.getMessage()); |
|||
throw (e instanceof PayException) ? (PayException) e : new PayException(e.getMessage(), e); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* post请求 |
|||
*/ |
|||
public String postRequest(String requestStr, String url) throws PayException { |
|||
return postRequest(0,requestStr,url); |
|||
} |
|||
|
|||
/** |
|||
* post请求 |
|||
*/ |
|||
public String postRequest(int retryTimes, String requestStr, String url) throws PayException { |
|||
String resultStr = ""; |
|||
try { |
|||
HttpHeaders headers = new HttpHeaders(); |
|||
//所有的请求需要用JSON格式发送
|
|||
headers.setContentType(MediaType.APPLICATION_JSON); |
|||
HttpEntity<Object> formEntity = new HttpEntity<>(requestStr, headers); |
|||
resultStr = restTemplate.postForObject(url, formEntity, String.class); |
|||
if (StringUtils.isBlank(resultStr) || !resultStr.startsWith("{")){ |
|||
log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, requestStr, resultStr); |
|||
throw new PayException("第三方返回格式有误!"); |
|||
} |
|||
JSONObject resultObj = JSONObject.parseObject(resultStr); |
|||
String errNo = StringUtils.isNotBlank(resultObj.getString("err_no"))?resultObj.getString("err_no"):resultObj.getString("err_code"); |
|||
if ("0".equals(errNo)) { |
|||
log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, resultObj, resultStr); |
|||
return resultStr; |
|||
}else { |
|||
throw convertDyException(GsonParser.parse(resultStr)); |
|||
} |
|||
}catch (PayException e){ |
|||
log.error("\n【请求地址】:{}\n【请求数据】:{}\n【异常信息】:{}", url, requestStr, e.getMessage()); |
|||
if (retryTimes > MAX_RETRY_TIMES){ |
|||
throw new PayException("抖音接口调用失败,已超过最大重试次数"); |
|||
} |
|||
//如果是access_token过期,刷新token在重试
|
|||
if ("40004".equals(e.getErrCode())){ |
|||
String accessToken = refreshAccessToken(); |
|||
JSONObject requestJsonObj = JSON.parseObject(requestStr); |
|||
requestJsonObj.put("access_token",accessToken); |
|||
requestStr = requestJsonObj.toJSONString(); |
|||
} |
|||
retryTimes ++; |
|||
postRequest(retryTimes,requestStr,url); |
|||
} |
|||
return resultStr; |
|||
} |
|||
|
|||
/** |
|||
* 刷新access_token |
|||
*/ |
|||
public String refreshAccessToken() throws PayException { |
|||
return refreshAccessToken(0); |
|||
} |
|||
|
|||
/** |
|||
* 刷新access_token |
|||
* @param retryTimes |
|||
*/ |
|||
public String refreshAccessToken(int retryTimes) throws PayException { |
|||
String accessToken = ""; |
|||
try { |
|||
accessToken = getAccessToken( true); |
|||
} catch (PayException e) { |
|||
if (retryTimes > MAX_RETRY_TIMES){ |
|||
throw new PayException("抖音接口获取AccessToken调用失败,已超过最大重试次数"); |
|||
} |
|||
retryTimes ++; |
|||
refreshAccessToken(retryTimes); |
|||
} |
|||
return accessToken; |
|||
} |
|||
|
|||
/** |
|||
* 转换异常 |
|||
* @param jsonObject |
|||
* @return |
|||
*/ |
|||
private PayException convertDyException(JsonObject jsonObject) { |
|||
JsonElement codeElement = jsonObject.get("err_no") != null ? jsonObject.get("err_no"):jsonObject.get("err_code"); |
|||
String code = codeElement == null ? null : codeElement.getAsString(); |
|||
String message = jsonObject.get("error_msg").getAsString(); |
|||
PayException payException = new PayException(message); |
|||
payException.setErrCode(code); |
|||
payException.setErrCodeDes(message); |
|||
return payException; |
|||
} |
|||
} |
|||
@ -0,0 +1,186 @@ |
|||
package com.bnyer.pay.manager; |
|||
|
|||
import com.alibaba.fastjson.JSONObject; |
|||
import com.bnyer.common.core.constant.RedisKeyConstant; |
|||
import com.bnyer.common.core.domain.KspayConfig; |
|||
import com.bnyer.common.core.utils.SpringUtils; |
|||
import com.bnyer.common.core.utils.StringUtils; |
|||
import com.bnyer.common.redis.service.RedisService; |
|||
import com.bnyer.pay.constant.KSPayConstants; |
|||
import com.bnyer.pay.exception.PayException; |
|||
import com.google.gson.JsonElement; |
|||
import com.google.gson.JsonObject; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import me.chanjar.weixin.common.util.json.GsonParser; |
|||
import org.springframework.http.HttpEntity; |
|||
import org.springframework.http.HttpHeaders; |
|||
import org.springframework.http.MediaType; |
|||
import org.springframework.web.client.RestTemplate; |
|||
|
|||
import java.text.MessageFormat; |
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
import java.util.Objects; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
/** |
|||
* @author :WXC |
|||
* @Date :2023/04/24 |
|||
* @description : |
|||
*/ |
|||
@Slf4j |
|||
public class KsPayManager { |
|||
|
|||
private RestTemplate restTemplate; |
|||
|
|||
private RedisService redisService; |
|||
|
|||
private final KspayConfig kspayConfig; |
|||
|
|||
private final int MAX_RETRY_TIMES = 2; |
|||
|
|||
public KsPayManager(KspayConfig kspayConfig){ |
|||
this.kspayConfig = kspayConfig; |
|||
init(); |
|||
} |
|||
|
|||
private void init() { |
|||
this.restTemplate = SpringUtils.getBean(RestTemplate.class); |
|||
this.redisService = SpringUtils.getBean(RedisService.class); |
|||
} |
|||
|
|||
/** |
|||
* 该授权方式使用 OAuth2 的 client credentials 模式,获取小程序全局唯一后台接口调用凭据。 |
|||
* |
|||
* 说明:access_token48小时内有效,未超出有效截止时间,开发者重新调用获取新的access_token,则新老token同时有效。避免token过期,开发者可根据业务实际情况,注意间隔请求时间,不要超出48小时。 |
|||
* @param isRefresh |
|||
* @return |
|||
* @throws PayException |
|||
*/ |
|||
public String getAccessToken(boolean isRefresh) throws PayException { |
|||
if (!isRefresh){ |
|||
Object cacheObject = redisService.getCacheObject(RedisKeyConstant.KS_ACCESS_TOKEN_KEY); |
|||
if (Objects.nonNull(cacheObject)){ |
|||
return cacheObject.toString(); |
|||
} |
|||
} |
|||
String requestStr = ""; |
|||
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); |
|||
String resultStr = restTemplate.postForObject(KSPayConstants.GET_ACCESS_TOKEN, formEntity, String.class); |
|||
String url = KSPayConstants.GET_ACCESS_TOKEN; |
|||
requestStr = formEntity.getBody(); |
|||
if (StringUtils.isBlank(resultStr) || !resultStr.startsWith("{")){ |
|||
log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, requestStr, resultStr); |
|||
throw convertDyException(GsonParser.parse("第三方返回格式有误!")); |
|||
} |
|||
JSONObject resultObj = JSONObject.parseObject(resultStr); |
|||
String resultCode = resultObj.getString("result"); |
|||
if ("1".equals(resultCode)) { |
|||
log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, resultObj, resultStr); |
|||
//存入redis
|
|||
String accessToken = resultObj.getString("access_token"); |
|||
String expiresIn = resultObj.getString("expires_in"); |
|||
redisService.setCacheObject(RedisKeyConstant.KS_ACCESS_TOKEN_KEY,accessToken,Long.parseLong(expiresIn), TimeUnit.SECONDS); |
|||
return accessToken; |
|||
}else { |
|||
throw convertDyException(GsonParser.parse(resultStr)); |
|||
} |
|||
} catch (Exception e) { |
|||
log.error("\n【请求地址】:{}\n【请求数据】:{}\n【异常信息】:{}", KSPayConstants.GET_ACCESS_TOKEN, requestStr, e.getMessage()); |
|||
throw (e instanceof PayException) ? (PayException) e : new PayException(e.getMessage(), e); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* post请求 |
|||
*/ |
|||
public String postRequest(String requestStr, String url) throws PayException { |
|||
Map<String, String> urlParams = new HashMap<>(); |
|||
urlParams.put("app_id", kspayConfig.getAppid()); |
|||
urlParams.put("access_token", getAccessToken(false)); |
|||
return postRequest(0,urlParams,requestStr,url); |
|||
} |
|||
|
|||
/** |
|||
* post请求 |
|||
*/ |
|||
public String postRequest(int retryTimes,Map<String, String> urlParams, String requestStr, String url) throws PayException { |
|||
String resultStr = ""; |
|||
try { |
|||
HttpHeaders headers = new HttpHeaders(); |
|||
//所有的请求需要用JSON格式发送
|
|||
headers.setContentType(MediaType.APPLICATION_JSON); |
|||
HttpEntity<Object> formEntity = new HttpEntity<>(requestStr, headers); |
|||
resultStr = restTemplate.postForObject(url + "/?app_id={app_id}&access_token={access_token}", formEntity, String.class, urlParams); |
|||
if (StringUtils.isBlank(resultStr) || !resultStr.startsWith("{")){ |
|||
log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, requestStr, resultStr); |
|||
throw new PayException("第三方返回格式有误!"); |
|||
} |
|||
JSONObject resultObj = JSONObject.parseObject(resultStr); |
|||
String resultCode = resultObj.getString("result"); |
|||
if ("1".equals(resultCode)) { |
|||
log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, resultObj, resultStr); |
|||
return resultStr; |
|||
}else { |
|||
throw convertDyException(GsonParser.parse(resultStr)); |
|||
} |
|||
}catch (PayException e){ |
|||
log.error("\n【请求地址】:{}\n【请求数据】:{}\n【异常信息】:{}", url, requestStr, e.getMessage()); |
|||
if (retryTimes > MAX_RETRY_TIMES){ |
|||
throw new PayException("快手接口调用失败,已超过最大重试次数"); |
|||
} |
|||
//如果是access_token过期,刷新token在重试
|
|||
if ("10000011".equals(e.getErrCode())){ |
|||
urlParams.put("app_id", kspayConfig.getAppid()); |
|||
urlParams.put("access_token", refreshAccessToken()); |
|||
} |
|||
retryTimes ++; |
|||
postRequest(retryTimes,urlParams,requestStr,url); |
|||
} |
|||
return resultStr; |
|||
} |
|||
|
|||
/** |
|||
* 刷新access_token |
|||
*/ |
|||
public String refreshAccessToken() throws PayException { |
|||
return refreshAccessToken(0); |
|||
} |
|||
|
|||
/** |
|||
* 刷新access_token |
|||
* @param retryTimes |
|||
*/ |
|||
public String refreshAccessToken(int retryTimes) throws PayException { |
|||
String accessToken = ""; |
|||
try { |
|||
accessToken = getAccessToken( true); |
|||
} catch (PayException e) { |
|||
if (retryTimes > MAX_RETRY_TIMES){ |
|||
throw new PayException("快手接口获取AccessToken调用失败,已超过最大重试次数"); |
|||
} |
|||
retryTimes ++; |
|||
refreshAccessToken(retryTimes); |
|||
} |
|||
return accessToken; |
|||
} |
|||
|
|||
/** |
|||
* 转换异常 |
|||
* @param jsonObject |
|||
* @return |
|||
*/ |
|||
private PayException convertDyException(JsonObject jsonObject) { |
|||
JsonElement codeElement = jsonObject.get("result"); |
|||
String code = codeElement == null ? null : codeElement.getAsString(); |
|||
String message = jsonObject.get("error_msg").getAsString(); |
|||
PayException payException = new PayException(message); |
|||
payException.setErrCode(code); |
|||
payException.setErrCodeDes(message); |
|||
return payException; |
|||
} |
|||
} |
|||
@ -1,107 +1,269 @@ |
|||
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; |
|||
} |
|||
} |
|||
//package com.bnyer.pay.utils;
|
|||
//
|
|||
//import com.alibaba.fastjson.JSONObject;
|
|||
//import com.bnyer.common.core.constant.RedisKeyConstant;
|
|||
//import com.bnyer.common.core.domain.DypayConfig;
|
|||
//import com.bnyer.common.core.domain.KspayConfig;
|
|||
//import com.bnyer.common.core.exception.ServiceException;
|
|||
//import com.bnyer.common.core.utils.StringUtils;
|
|||
//import com.bnyer.common.redis.service.RedisService;
|
|||
//import com.bnyer.pay.constant.DYPayConstants;
|
|||
//import com.bnyer.pay.constant.KSPayConstants;
|
|||
//import com.bnyer.pay.exception.DyPayException;
|
|||
//import com.google.gson.JsonElement;
|
|||
//import com.google.gson.JsonObject;
|
|||
//import lombok.extern.slf4j.Slf4j;
|
|||
//import me.chanjar.weixin.common.util.json.GsonParser;
|
|||
//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;
|
|||
//import java.util.Objects;
|
|||
//import java.util.concurrent.TimeUnit;
|
|||
//
|
|||
///**
|
|||
// * @author :WXC
|
|||
// * @Date :2023/04/24
|
|||
// * @description :
|
|||
// */
|
|||
//@Slf4j
|
|||
//@Component
|
|||
//public class PayRestTemplateUtil {
|
|||
//
|
|||
// @Autowired
|
|||
// private RestTemplate restTemplate;
|
|||
//
|
|||
// @Autowired
|
|||
// private RedisService redisService;
|
|||
//
|
|||
// private final int MAX_RETRY_TIMES = 2;
|
|||
//
|
|||
// /**
|
|||
// * 快手小程序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,boolean isRefresh) {
|
|||
// if (!isRefresh){
|
|||
// Object cacheObject = redisService.getCacheObject(RedisKeyConstant.KS_ACCESS_TOKEN_KEY);
|
|||
// if (Objects.nonNull(cacheObject)){
|
|||
// return cacheObject.toString();
|
|||
// }
|
|||
// }
|
|||
// String accessToken = "";
|
|||
// 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);
|
|||
// accessToken = restTemplate.postForObject(KSPayConstants.GET_ACCESS_TOKEN, formEntity, String.class);
|
|||
// //存入redis
|
|||
// redisService.setCacheObject(RedisKeyConstant.KS_ACCESS_TOKEN_KEY,accessToken,2L, TimeUnit.HOURS);
|
|||
// } catch (Exception e) {
|
|||
// log.error("快手小程序post请求异常{}", KSPayConstants.GET_ACCESS_TOKEN);
|
|||
// log.error("post请求异常:{}", e.getMessage());
|
|||
// e.printStackTrace();
|
|||
// }
|
|||
// return accessToken;
|
|||
// }
|
|||
//
|
|||
// /**
|
|||
// * 快手
|
|||
// * 支付 退款 结算
|
|||
// */
|
|||
// 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;
|
|||
// }
|
|||
//
|
|||
// /**
|
|||
// * 抖音小程序获取accessToken
|
|||
// * 为了保障应用的数据安全,只能在开发者服务器使用 AppSecret,如果小程序存在泄露 AppSecret 的问题,字节小程序平台将有可能下架该小程序,并暂停该小程序相关服务。
|
|||
// *
|
|||
// * access_token 是小程序的全局唯一调用凭据,开发者调用小程序支付时需要使用 access_token。access_token 的有效期为 2 个小时,需要定时刷新 access_token,重复获取会导致之前一次获取的 access_token 的有效期缩短为 5 分钟。
|
|||
// */
|
|||
// public String dyGetAccessToken(DypayConfig dypayConfig,boolean isRefresh) throws DyPayException {
|
|||
// if (!isRefresh){
|
|||
// Object cacheObject = redisService.getCacheObject(RedisKeyConstant.DY_ACCESS_TOKEN_KEY);
|
|||
// if (Objects.nonNull(cacheObject)){
|
|||
// return cacheObject.toString();
|
|||
// }
|
|||
// }
|
|||
// String requestStr = "";
|
|||
// 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}"
|
|||
// , dypayConfig.getAppid(), dypayConfig.getAppSecret(), "client_credentials"), headers);
|
|||
// String resultStr = restTemplate.postForObject(DYPayConstants.GET_ACCESS_TOKEN, formEntity, String.class);
|
|||
// String url = DYPayConstants.GET_ACCESS_TOKEN;
|
|||
// requestStr = formEntity.getBody();
|
|||
// if (StringUtils.isBlank(resultStr) || !resultStr.startsWith("{")){
|
|||
// log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, requestStr, resultStr);
|
|||
// throw convertDyException(GsonParser.parse("第三方返回格式有误!"));
|
|||
// }
|
|||
// JSONObject resultObj = JSONObject.parseObject(resultStr);
|
|||
// String errNo = StringUtils.isNotBlank(resultObj.getString("err_no"))?resultObj.getString("err_no"):resultObj.getString("err_code");
|
|||
// if (!"0".equals(errNo)) {
|
|||
// log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, resultObj, resultStr);
|
|||
// //存入redis
|
|||
// String accessToken = resultObj.getJSONObject("data").getString("access_token");
|
|||
// String expiresIn = resultObj.getJSONObject("data").getString("expires_in");
|
|||
// redisService.setCacheObject(RedisKeyConstant.DY_ACCESS_TOKEN_KEY,accessToken,Long.parseLong(expiresIn), TimeUnit.SECONDS);
|
|||
// return accessToken;
|
|||
// }else {
|
|||
// throw convertDyException(GsonParser.parse(resultStr));
|
|||
// }
|
|||
// } catch (Exception e) {
|
|||
// log.error("\n【请求地址】:{}\n【请求数据】:{}\n【异常信息】:{}", DYPayConstants.GET_ACCESS_TOKEN, requestStr, e.getMessage());
|
|||
// throw (e instanceof DyPayException) ? (DyPayException) e : new DyPayException(e.getMessage(), e);
|
|||
// }
|
|||
// }
|
|||
//
|
|||
// /**
|
|||
// * 抖音小程序post请求
|
|||
// */
|
|||
// public String dyPostRequest(String requestStr, String url) throws DyPayException {
|
|||
// return dyPostRequest(0,requestStr,url);
|
|||
// }
|
|||
//
|
|||
// /**
|
|||
// * 抖音小程序post请求
|
|||
// */
|
|||
// public String dyPostRequest(int retryTimes, String requestStr, String url) throws DyPayException {
|
|||
// String resultStr = "";
|
|||
// try {
|
|||
// HttpHeaders headers = new HttpHeaders();
|
|||
// //所有的请求需要用JSON格式发送
|
|||
// headers.setContentType(MediaType.APPLICATION_JSON);
|
|||
// HttpEntity<Object> formEntity = new HttpEntity<>(requestStr, headers);
|
|||
// resultStr = restTemplate.postForObject(url, formEntity, String.class);
|
|||
// if (StringUtils.isBlank(resultStr) || !resultStr.startsWith("{")){
|
|||
// log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, requestStr, resultStr);
|
|||
// throw new DyPayException("第三方返回格式有误!");
|
|||
// }
|
|||
// JSONObject resultObj = JSONObject.parseObject(resultStr);
|
|||
// String errNo = StringUtils.isNotBlank(resultObj.getString("err_no"))?resultObj.getString("err_no"):resultObj.getString("err_code");
|
|||
// if (!"0".equals(errNo)) {
|
|||
// log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, resultObj, resultStr);
|
|||
// return resultStr;
|
|||
// }else {
|
|||
// throw convertDyException(GsonParser.parse(resultStr));
|
|||
// }
|
|||
// }catch (DyPayException e){
|
|||
// log.error("\n【请求地址】:{}\n【请求数据】:{}\n【异常信息】:{}", url, requestStr, e.getMessage());
|
|||
// String accessToken = refreshAccessToken(dypayConfig, requestStr, DYPayConstants.ORDER_PUSH, e);
|
|||
// if (retryTimes > MAX_RETRY_TIMES){
|
|||
// throw new DyPayException("抖音接口调用失败,已超过最大重试次数");
|
|||
// }
|
|||
// retryTimes ++;
|
|||
// dyPostRequest(retryTimes,requestStr,url);
|
|||
// }
|
|||
// return resultStr;
|
|||
// }
|
|||
//
|
|||
// /**
|
|||
// * token过期请求重试
|
|||
// * @param dypayConfig
|
|||
// * @param requestStr
|
|||
// * @param e
|
|||
// */
|
|||
// private String refreshAccessToken(DypayConfig dypayConfig,String url, String requestStr, DyPayException e) {
|
|||
// //如果token过期的话,刷新token并重试
|
|||
// String accessToken = "";
|
|||
// if ("4004".equals(e.getErrCode())){
|
|||
// try {
|
|||
// accessToken = dyRefreshAccessToken(dypayConfig);
|
|||
// } catch (DyPayException dyPayException) {
|
|||
// throw new ServiceException(dyPayException.getMessage());
|
|||
// }
|
|||
// }
|
|||
// String result = "";
|
|||
// if ("4004".equals(e.getErrCode())){
|
|||
// try {
|
|||
// result = dyPostRequest(requestStr, url);
|
|||
// } catch (DyPayException dyPayException) {
|
|||
// log.error("抖音支付:抖音订单推送接口调用异常,request:{},error{}", requestStr, e.getMessage());
|
|||
// }
|
|||
// }
|
|||
// return accessToken;
|
|||
// }
|
|||
//
|
|||
// /**
|
|||
// * 刷新抖音token
|
|||
// * @param dypayConfig
|
|||
// */
|
|||
// public String dyRefreshAccessToken(DypayConfig dypayConfig) throws DyPayException {
|
|||
// return dyRefreshAccessToken(0,dypayConfig);
|
|||
// }
|
|||
//
|
|||
// /**
|
|||
// * 刷新抖音token
|
|||
// * @param retryTimes
|
|||
// * @param dypayConfig
|
|||
// */
|
|||
// public String dyRefreshAccessToken(int retryTimes, DypayConfig dypayConfig) throws DyPayException {
|
|||
// String accessToken = "";
|
|||
// try {
|
|||
// accessToken = dyGetAccessToken(dypayConfig, true);
|
|||
// } catch (DyPayException e) {
|
|||
// if (retryTimes > MAX_RETRY_TIMES){
|
|||
// throw new DyPayException("抖音接口调用失败,已超过最大重试次数");
|
|||
// }
|
|||
// retryTimes ++;
|
|||
// dyRefreshAccessToken(retryTimes, dypayConfig);
|
|||
// }
|
|||
// return accessToken;
|
|||
// }
|
|||
//
|
|||
// /**
|
|||
// * 抖音转换异常
|
|||
// * @param jsonObject
|
|||
// * @return
|
|||
// */
|
|||
// private DyPayException convertDyException(JsonObject jsonObject) {
|
|||
// JsonElement codeElement = jsonObject.get("err_no") != null ? jsonObject.get("err_no"):jsonObject.get("err_code");
|
|||
// String code = codeElement == null ? null : codeElement.getAsString();
|
|||
// String message = jsonObject.get("error_msg").getAsString();
|
|||
// DyPayException dyPayException = new DyPayException(message);
|
|||
// dyPayException.setErrCode(code);
|
|||
// dyPayException.setErrCodeDes(message);
|
|||
// return dyPayException;
|
|||
// }
|
|||
//}
|
|||
|
|||
Loading…
Reference in new issue