Просмотр исходного кода

Merge branch 'release_toaliyun_real_push_order' of https://git.malls.iformall.com/server/formallProject into release_toaliyun_real_push_order

release_toaliyun_real
lin 4 лет назад
Родитель
Сommit
b3c072fcd6
8 измененных файлов: 287 добавлений и 30 удалений
  1. +1
    -1
      mallinkAdmin/src/main/java/com/iformall/config/ShiroConfig.java
  2. +76
    -0
      mallinkAdmin/src/main/java/com/iformall/controller/basic/WxProjectConfigController.java
  3. +60
    -23
      mallinkCApi/src/main/java/com/iformall/controller/WxOrderController.java
  4. +18
    -3
      mallinkService/src/main/java/com/iformall/douyin/pay/impl/BaseTtPayServiceImpl.java
  5. +41
    -1
      mallinkService/src/main/java/com/iformall/douyin/payv2/auth/CertificatesVerifier.java
  6. +1
    -1
      mallinkService/src/main/java/com/iformall/douyin/payv2/config/TtPayConfig.java
  7. +1
    -1
      mallinkService/src/main/java/com/iformall/douyin/payv2/result/BaseTtPayResult.java
  8. +89
    -0
      mallinkService/src/main/java/com/iformall/douyin/payv2/util/PemUtils.java

+ 1
- 1
mallinkAdmin/src/main/java/com/iformall/config/ShiroConfig.java Просмотреть файл

@@ -147,7 +147,7 @@ public class ShiroConfig {
filterChainDefinitionMap.put("/wxDeviceScreenAd/**", "anon");
// filterChainDefinitionMap.put("/role/**", "corsFilter,token");
//商场初始化init
// filterChainDefinitionMap.put("/wxProjectConfig/**", "anon");
filterChainDefinitionMap.put("/wxProjectConfig/**", "anon");

// //test
// filterChainDefinitionMap.put("/merchantPoi/spuSync", "anon");


+ 76
- 0
mallinkAdmin/src/main/java/com/iformall/controller/basic/WxProjectConfigController.java Просмотреть файл

@@ -10,10 +10,13 @@ import com.iformall.controller.base.BaseController;
import com.iformall.domain.po.*;
import com.iformall.domain.po.base.TenantEntity;
import com.iformall.domain.vo.WxWeappInfo;
import com.iformall.douyin.pay.TtPayService;
import com.iformall.douyin.payv2.request.CallBackSettingsRequest;
import com.iformall.enums.*;
import com.iformall.service.*;
import com.iformall.shiro.PasswordHelper;
import com.iformall.utils.Constant;
import com.iformall.utils.MaUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
@@ -94,6 +97,9 @@ public class WxProjectConfigController extends BaseController {
@Autowired
private WechatWebProperties wechatWebProperties;

@Autowired
private MaUtil maUtil;

@Autowired
@Qualifier("openRedisTemplate")
RedisTemplate<String, String> openRedisTemplate;
@@ -774,4 +780,74 @@ public class WxProjectConfigController extends BaseController {
}
}

@ApiOperation("获取抖音支付2.0 回调接口配置")
@GetMapping("/ttcallback/query/settings")
@SystemControllerLog(description = "")
@TenantIgnore
public ResultData ttcallbackQuerySettings(String appid) {
logger.debug("[" + getIpAddr() + "] WxProjectConfigController::ttcallbackQuerySettings");
try {
WxAppinfo appinfo = wxAppinfoService.getByAppId(appid);
if(appinfo == null){
return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(),"未找到对应的小程序");
}
if(!EnumAppPlat.TOUTIAO.getCode().equals(appinfo.getPlat()) || !EnumAppType.C.getCode().equals(appinfo.getType())){
return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(),"该小程序不支持");
}
WxPayAccount payAccount = wxPayAccountService.getById(appinfo.getPayId());
if(payAccount == null){
return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(),"未找到对应的支付数据");
}
TtPayService ttPayService = maUtil.getTtPayService(appinfo, payAccount);
CallBackSettingsRequest callBackSettingsRequest = ttPayService.querySettings();
return new ResultData(callBackSettingsRequest);
}catch (Exception e){
logger.error(e.getMessage(),e);
return new ResultData(ErrorCode.SYS_SERVER_ERROR);
}
}

@ApiOperation("配置抖音支付2.0 回调接口")
@PostMapping("/ttcallback/settings")
@SystemControllerLog(description = "商场集团-更新")
@TenantIgnore
public ResultData ttcallbackSettings(@RequestBody Map<String, String> map) {
logger.debug("[" + getIpAddr() + "] WxProjectConfigController::ttcallbackSettings");
String appid = map.get("appid");
String create_order_callback = map.get("create_order_callback");
String refund_callback = map.get("refund_callback");
String delivery_qrcode_redirect = map.get("delivery_qrcode_redirect");
if(StringUtils.isBlank(appid) || StringUtils.isBlank(create_order_callback)
|| StringUtils.isBlank(refund_callback)){
return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL);
}
try {
WxAppinfo appinfo = wxAppinfoService.getByAppId(appid);
if(appinfo == null){
return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(),"未找到对应的小程序");
}
if(!EnumAppPlat.TOUTIAO.getCode().equals(appinfo.getPlat()) || !EnumAppType.C.getCode().equals(appinfo.getType())){
return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(),"该小程序不支持");
}
WxPayAccount payAccount = wxPayAccountService.getById(appinfo.getPayId());
if(payAccount == null){
return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(),"未找到对应的支付数据");
}
TtPayService ttPayService = maUtil.getTtPayService(appinfo, payAccount);
CallBackSettingsRequest request = new CallBackSettingsRequest();
request.setCreateOrderCallback(create_order_callback);
request.setRefundCallback(refund_callback);
request.setDeliveryQrcodeRedirect(delivery_qrcode_redirect);
boolean b = ttPayService.callbackSettings(request);
if(b){
return new ResultData();
}else{
return new ResultData(ErrorCode.SYS_SERVER_ERROR);
}
}catch (Exception e){
logger.error(e.getMessage(),e);
return new ResultData(ErrorCode.SYS_SERVER_ERROR);
}
}

}

+ 60
- 23
mallinkCApi/src/main/java/com/iformall/controller/WxOrderController.java Просмотреть файл

@@ -1,7 +1,9 @@
package com.iformall.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader;
import com.github.pagehelper.PageInfo;
import com.iformall.annotation.AuthIgnore;
import com.iformall.common.ErrorCode;
import com.iformall.common.IdWorker;
import com.iformall.common.Result;
@@ -16,6 +18,7 @@ import com.iformall.domain.vo.WxOrderCouponPressVo;
import com.iformall.domain.vo.WxOrderCouponVo;
import com.iformall.enums.*;
import com.iformall.exception.MallinkException;
import com.iformall.interceptor.BodyReaderHttpServletRequestWrapper;
import com.iformall.service.*;
import com.iformall.service.order.OrderFactory;
import com.iformall.service.order.entity.WxComposeOrder;
@@ -34,10 +37,8 @@ import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import java.util.*;

@RestController
@RequestMapping("/api/order/")
@@ -138,27 +139,42 @@ public class WxOrderController extends BaseController {
list.add(dto);
return wxOrderService.composeSaveOrder(false,EnumComposeOrder.SINGLE,list,memberId,getPayWay(),getTenantInfo());
}

@AuthIgnore
@ApiOperation(value = "抖音支付2.0与下单回调推送订单", notes = "{\"couponChannelId\":\"String\",\"couponId\":\"String\",\"press\":\"String\",\"orderGroupId\":\"String\",\"formId\":\"String\"}")
@PostMapping("douyinPushOrder")
public ResultData douyinPushOrder(@RequestBody Map param) {
logger.info("save douyinPushOrder:"+param);
Long memberId;
try {
memberId = getMemberId();
} catch (Exception e) {
return new ResultData(Result.ERROR,e.getMessage());
}
List<PlatPushOrderSaveDto> list = new ArrayList<>();
PlatPushOrderSaveDto dto = new PlatPushOrderSaveDto();
/**
* TODO 从抖音推送的订单中取信息,每个订单里面有extParam,用来放第三方平台匹配的参数信息
* 注意,wx_batch_order中要存allExtParam, wx_order,wx_coupon_order中存储每个订单的参数信息,需要自己根据总参数信息进行拆分
*/
list.add(dto);
String allExtParam = "[]";
return wxOrderService.platPushSaveOrder(false,EnumComposeOrder.ONE_NUMBER_ORDER_BATCH,allExtParam,list,memberId,getPayWay(),getTenantInfo());
public Map<String,Object> douyinPushOrder(HttpServletRequest request) {
SignatureHeader header = new SignatureHeader();
header.setTimeStamp(request.getHeader("Byte-Timestamp"));
header.setNonce(request.getHeader("Byte-Nonce-Str"));
header.setSigned(request.getHeader("Byte-Signature"));

logger.info("支付2.0预下单回调---header{}"+header.toString());

String body = ((BodyReaderHttpServletRequestWrapper) request).getBody();
logger.info("支付2.0预下单回调---body{}"+body);

Map<String, Object> map = new HashMap();
map.put("err_no",1000);
map.put("err_tips","库存不足");
return map;

// Long memberId;
// try {
// memberId = getMemberId();
// } catch (Exception e) {
// return new ResultData(Result.ERROR,e.getMessage());
// }
//
// List<PlatPushOrderSaveDto> list = new ArrayList<>();
// PlatPushOrderSaveDto dto = new PlatPushOrderSaveDto();
// /**
// * TODO 从抖音推送的订单中取信息,每个订单里面有extParam,用来放第三方平台匹配的参数信息
// * 注意,wx_batch_order中要存allExtParam, wx_order,wx_coupon_order中存储每个订单的参数信息,需要自己根据总参数信息进行拆分
// */
// list.add(dto);
// String allExtParam = "[]";
// return wxOrderService.platPushSaveOrder(false,EnumComposeOrder.ONE_NUMBER_ORDER_BATCH,allExtParam,list,memberId,getPayWay(),getTenantInfo());
}
@@ -700,4 +716,25 @@ public class WxOrderController extends BaseController {
return new ResultData(refundOrder);
}

@AuthIgnore
@ApiOperation(value = "抖音支付2.0与退款回调推送订单", notes = "{\"couponChannelId\":\"String\",\"couponId\":\"String\",\"press\":\"String\",\"orderGroupId\":\"String\",\"formId\":\"String\"}")
@PostMapping("douyinRefundOrder")
public Map<String,Object> douyinRefundOrder(HttpServletRequest request) {
SignatureHeader header = new SignatureHeader();
header.setTimeStamp(request.getHeader("Byte-Timestamp"));
header.setNonce(request.getHeader("Byte-Nonce-Str"));
header.setSigned(request.getHeader("Byte-Signature"));

logger.info("支付2.0预下单回调---header{}"+header.toString());

String body = ((BodyReaderHttpServletRequestWrapper) request).getBody();
logger.info("支付2.0预下单回调---body{}"+body);

Map<String, Object> map = new HashMap();
map.put("err_no",1001);
map.put("err_tips","未找到支付信息");
return map;

}

}

+ 18
- 3
mallinkService/src/main/java/com/iformall/douyin/pay/impl/BaseTtPayServiceImpl.java Просмотреть файл

@@ -74,7 +74,12 @@ public abstract class BaseTtPayServiceImpl implements TtPayService {
public boolean callbackSettings(CallBackSettingsRequest request) throws TtPayException {
String url = String.format("%s/api/apps/trade/v2/settings", this.getPayBaseUrl());
String response = this.postV2(url, GSON.toJson(request));
return GSON.fromJson(response, BaseTtPayResult.class).isSuccess();
BaseTtPayResult baseTtPayResult = GSON.fromJson(response, BaseTtPayResult.class);
if(baseTtPayResult.isSuccess()){
return true;
}else{
throw new TtPayException(baseTtPayResult.getErrTips());
}
}

@Override
@@ -174,7 +179,12 @@ public abstract class BaseTtPayServiceImpl implements TtPayService {
public boolean pushDelivery(TtOrderPushDeliveryRequest request) throws TtPayException{
String url = String.format("%s/api/apps/trade/v2/push_delivery", this.getPayBaseUrl());
String response = this.postV2(url, GSON.toJson(request));
return GSON.fromJson(response, BaseTtPayResult.class).isSuccess();
BaseTtPayResult baseTtPayResult = GSON.fromJson(response, BaseTtPayResult.class);
if(baseTtPayResult.isSuccess()){
return true;
}else{
throw new TtPayException(baseTtPayResult.getErrTips());
}

}

@@ -209,7 +219,12 @@ public abstract class BaseTtPayServiceImpl implements TtPayService {
public boolean merchantAuditCallback(TtRefundAuditMsgRequest request) throws TtPayException{
String url = String.format("%s/api/apps/trade/v2/merchant_audit_callback", this.getPayBaseUrl());
String response = this.postV2(url, GSON.toJson(request));
return GSON.fromJson(response, BaseTtPayResult.class).isSuccess();
BaseTtPayResult baseTtPayResult = GSON.fromJson(response, BaseTtPayResult.class);
if(baseTtPayResult.isSuccess()){
return true;
}else{
throw new TtPayException(baseTtPayResult.getErrTips());
}
}

@Override


+ 41
- 1
mallinkService/src/main/java/com/iformall/douyin/payv2/auth/CertificatesVerifier.java Просмотреть файл

@@ -1,7 +1,11 @@
package com.iformall.douyin.payv2.auth;

import com.iformall.douyin.payv2.util.PemUtils;
import me.chanjar.weixin.common.error.WxRuntimeException;

import java.io.ByteArrayInputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;

@@ -19,7 +23,7 @@ public class CertificatesVerifier implements Verifier {
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initVerify(this.apiKey);
sign.update(message);
return sign.verify(Base64.getDecoder().decode(signature));
return sign.verify(Base64.getDecoder().decode(signature.getBytes(StandardCharsets.UTF_8)));
} catch (NoSuchAlgorithmException e) {
throw new WxRuntimeException("当前Java环境不支持SHA256withRSA", e);
} catch (SignatureException e) {
@@ -29,4 +33,40 @@ public class CertificatesVerifier implements Verifier {
}
}

// public static void main(String[] args) {
// String pubKey = "-----BEGIN PUBLIC KEY-----\n" +
// "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv8jSFriZ6l5vv0xVcpFJ\n" +
// "NxihPh3j6tVKrU2FBythCstyr/CwdpAp9AGXvcaW2x2kKiOMXXaRDo6OysjggsxK\n" +
// "5hh1fWDE3ML4Wi5tfAV64pvj8wn5XeHXGKtXcKD/ASQmUGnMKI0g8i2REm3Q7gzy\n" +
// "JH35URh+tU8wI2v8mDY9OCZ7Hmu8ImW3pibyYRYK0CEpa31+65gVaTeoaPlqTKR0\n" +
// "rwgxZM5nFcXxGtiTsH0O1GAJBqWpyVeOqH19vEVB/bROEb21bf65ZYhac2Z1lzWq\n" +
// "MW+/4knIDg6EH3/xZWFPPwAFZgpfkSQES/twPkN/59cZ8SfTwJ84k/KUOApvkihM\n" +
// "uQIDAQAB\n" +
// "-----END PUBLIC KEY-----";
// String timestamp = "1653906762";
// String nonce = "pztVmu9EhPgCv9Dvw31t68U9WhMcLXCb";
// String httpBody = "{\"err_no\":0,\"data\":{\"create_order_callback\":\"https://ctest.malls.iformall.com/C/api/order/douyinPushOrder\",\"refund_callback\":\"https://ctest.malls.iformall.com/C/api/order/douyinRefundOrder\",\"delivery_qrcode_redirect\":\"\"},\"resp_extra\":{\"logid\":\"202205301832420101581160381B3C09C0\"},\"err_tips\":\"success\"}";
// String signStr = "pZEQga7EpGI+NXEznLNZROViIBxwWaofnlstthQsu8MZiuTRUtool2SQhfLHkXwYzj5O8yj+luTbFUrhTfX+BzvcR3+fGrqMtgqGWp86a1hAg4e4pvoTHd5PAxUGRDb/gGxPoxLDgrS6cLKusdGi6mmwJbERveD9ThNV1RQGQo/BM3BeImr3fYdJ/ZU67iVkP6SEWkqRiEJhnQMaavy/B4LK3xI0A5BhWlGbhGUIqumLT903tIOhofXGA6PQJbDiU38J5EtMj/Z7l6+IXtbA/KO0IWbIZ96KFs6e8ZE6H18Xn7b0J7KDTjTelMt7iXczrLU2/e3QEcfn2/7+7DIRMQ==";
//
// StringBuffer buffer = new StringBuffer();
// buffer.append(timestamp).append("\n");
// buffer.append(nonce).append("\n");
// buffer.append(httpBody).append("\n");
// try {
// Signature sign = Signature.getInstance("SHA256withRSA");
// sign.initVerify(PemUtils.loadPublicKey(new ByteArrayInputStream(pubKey.getBytes("utf-8"))));
// sign.update(buffer.toString().getBytes(StandardCharsets.UTF_8));
// boolean verify = sign.verify(Base64.getDecoder().decode(signStr.getBytes(StandardCharsets.UTF_8)));
// System.out.println("-------"+verify);
// } catch (NoSuchAlgorithmException e) {
// e.printStackTrace();
// } catch (UnsupportedEncodingException e) {
// e.printStackTrace();
// } catch (SignatureException e) {
// e.printStackTrace();
// } catch (InvalidKeyException e) {
// e.printStackTrace();
// }
// }

}

+ 1
- 1
mallinkService/src/main/java/com/iformall/douyin/payv2/config/TtPayConfig.java Просмотреть файл

@@ -1,10 +1,10 @@
package com.iformall.douyin.payv2.config;

import com.github.binarywang.wxpay.v3.util.PemUtils;
import com.iformall.douyin.pay.exception.TtPayException;
import com.iformall.douyin.payv2.auth.CertificatesVerifier;
import com.iformall.douyin.payv2.auth.TtPayValidator;
import com.iformall.douyin.payv2.auth.Verifier;
import com.iformall.douyin.payv2.util.PemUtils;
import com.iformall.douyin.payv2.util.TtPayV2HttpClientBuilder;
import jodd.util.ResourcesUtil;
import lombok.Data;


+ 1
- 1
mallinkService/src/main/java/com/iformall/douyin/payv2/result/BaseTtPayResult.java Просмотреть файл

@@ -20,7 +20,7 @@ import java.io.Serializable;
*/
@Data
@NoArgsConstructor
public abstract class BaseTtPayResult<T> implements Serializable {
public class BaseTtPayResult<T> implements Serializable {

/**
* 返回状态码.


+ 89
- 0
mallinkService/src/main/java/com/iformall/douyin/payv2/util/PemUtils.java Просмотреть файл

@@ -0,0 +1,89 @@
package com.iformall.douyin.payv2.util;

import me.chanjar.weixin.common.error.WxRuntimeException;
import sun.misc.BASE64Decoder;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class PemUtils {

public static PrivateKey loadPrivateKey(InputStream inputStream) {
try {
ByteArrayOutputStream array = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) != -1) {
array.write(buffer, 0, length);
}

String privateKey = array.toString("utf-8")
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\\s+", "");

KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(
new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
} catch (NoSuchAlgorithmException e) {
throw new WxRuntimeException("当前Java环境不支持RSA", e);
} catch (InvalidKeySpecException e) {
throw new WxRuntimeException("无效的密钥格式");
} catch (IOException e) {
throw new WxRuntimeException("无效的密钥");
}
}

public static X509Certificate loadCertificate(InputStream inputStream) {
try {
CertificateFactory cf = CertificateFactory.getInstance("X509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream);
cert.checkValidity();
return cert;
} catch (CertificateExpiredException e) {
throw new WxRuntimeException("证书已过期", e);
} catch (CertificateNotYetValidException e) {
throw new WxRuntimeException("证书尚未生效", e);
} catch (CertificateException e) {
throw new WxRuntimeException("无效的证书", e);
}
}

public static PublicKey loadPublicKey(InputStream inputStream) {

try {
ByteArrayOutputStream array = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) != -1) {
array.write(buffer, 0, length);
}

String publicKey = array.toString("utf-8")
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "")
.replaceAll("\\s+", "");

KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePublic(
new X509EncodedKeySpec(new BASE64Decoder().decodeBuffer(publicKey)));

} catch (NoSuchAlgorithmException e) {
throw new WxRuntimeException("当前Java环境不支持RSA", e);
} catch (InvalidKeySpecException e) {
throw new WxRuntimeException("无效的密钥格式");
} catch (IOException e) {
throw new WxRuntimeException("无效的密钥");
}
}
}

Загрузка…
Отмена
Сохранить