记得上下班打卡 | git大法好,push需谨慎

Commit 1350071c authored by wangyifan's avatar wangyifan

收钱吧 回调验签

parent c684296a
package com.liquidnet.service.goblin.param.shouqianba.callback;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(value = "CallbackParams", description = "收钱吧微信小店订单推送请求体")
public class CallbackParams {
@ApiModelProperty(value = "推送事件ID,全局唯一", example = "2024013000594610646")
private Long eventId;
@ApiModelProperty(value = "推送时间戳,当前时间毫秒值", example = "1706679507593")
private Long timestamp;
@ApiModelProperty(value = "随机字串,用于签名", example = "j9xd5kc7ixcryy7at5rrrv24xty783th")
private String nonce;
@ApiModelProperty(value = "订单数据 JSON字符串 (详见 SqbOrderContentDTO)")
private String content;
@ApiModelProperty(value = "报文签名", example = "bryyeKJ6opUothNTz1s3+F98LOl2Teji6T4XcEAejxdt...")
private String signature;
}
package com.liquidnet.service.goblin.param.shouqianba.callback;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
@ApiModel(value = "SqbVoucherStatusDTO", description = "收钱吧券状态推送内容详情")
public class CouponCallbackContent {
@ApiModelProperty(value = "订单Sn")
private String orderSn;
@ApiModelProperty(value = "商城Sn")
private String mallSn;
@ApiModelProperty(value = "商户Sn")
private String merchantSn;
@ApiModelProperty(value = "门店id")
private String storeId;
@ApiModelProperty(value = "券列表信息")
private List<VoucherItem> voucherList;
@Data
@ApiModel("VoucherItem")
public static class VoucherItem {
@ApiModelProperty(value = "券号")
private String voucherNo;
@ApiModelProperty(value = "类型")
private String type;
@ApiModelProperty(value = "券状态 (0未核销, 1已核销, 2未核销退款, 3已核销退款中, 4未核销退款完成, 5已核销退款完成, 6已失效)")
private Byte targetState;
@ApiModelProperty(value = "券原状态")
private Byte sourceState;
@ApiModelProperty(value = "券名称")
private String voucherName;
@ApiModelProperty(value = "操作时间 (yyyy-MM-dd HH:mm:ss)")
private String operationTime;
@ApiModelProperty(value = "券对应的商品信息")
private VoucherProductInfo item;
@ApiModelProperty(value = "券二维码地址")
private String qrCodeUrl;
@ApiModelProperty(value = "券二维码内容")
private String qrCodeContent;
@ApiModelProperty(value = "原金额 (单位:分)")
private Long oriAmount;
@ApiModelProperty(value = "实收金额 (单位:分)")
private Long receivedAmount;
@ApiModelProperty(value = "核销信息 (已核销会有这部分)")
private RedeemInfo redeemInfo;
}
@Data
@ApiModel("VoucherProductInfo")
public static class VoucherProductInfo {
@ApiModelProperty(value = "spuId")
private Long spuId;
@ApiModelProperty(value = "skuId")
private Long skuId;
@ApiModelProperty(value = "商品标题")
private String title;
@ApiModelProperty(value = "商品图片地址")
private String img;
}
@Data
@ApiModel("RedeemInfo")
public static class RedeemInfo {
@ApiModelProperty(value = "操作来源 (如: EXTERN)")
private String redeemSource;
@ApiModelProperty(value = "操作appId")
private String redeemAppId;
@ApiModelProperty(value = "客户端编码")
private String redeemClientSn;
@ApiModelProperty(value = "外部编码 (appId + 请求号)")
private String redeemExternalOrderSn;
@ApiModelProperty(value = "操作商户 (userId)")
private String operatorMerchantId;
}
}
package com.liquidnet.service.goblin.param.shouqianba.callback;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
@Data
@ApiModel(value = "OrderCallbackContent", description = "收钱吧订单推送 Content 具体数据")
public class OrderCallbackContent {
@ApiModelProperty(value = "订单号", example = "71655367906162")
private String orderSn;
@ApiModelProperty(value = "支付流水号列表", example = "[\"7894259297368060\"]")
private List<String> payTsnList;
@ApiModelProperty(value = "终端号", example = "2101056320290541156")
private String terminalSn;
@ApiModelProperty(value = "门店号", example = "21590000000630325")
private String storeSn;
@ApiModelProperty(value = "商城名称", example = "智能商城0206-17:35")
private String mallName;
@ApiModelProperty(value = "订单金额", example = "2")
private BigDecimal orderAmount;
@ApiModelProperty(value = "创建时间", example = "2024-02-20 10:46:32")
private String createdAt;
@ApiModelProperty(value = "订单状态码 0:已创建 10:已支付 35:已完成 40:已取消", example = "35")
private Integer orderStateCode;
@ApiModelProperty(value = "商品明细列表")
private List<Item> items;
@ApiModelProperty(value = "收集的信息 JSON字符串", example = "[{\"type\":\"text\",\"form\":\"basic\",\"fieldName\":\"姓名\",\"content\":\"\"}]")
private String collectedInfo;
@ApiModelProperty(value = "预订单编码")
private List<String> preOrderSns;
@ApiModelProperty(value = "订单签名")
private String orderSignature;
@ApiModelProperty(value = "支付的代客下单数据")
private List<PreOrder> preOrderList;
@Data
@ApiModel(value = "商品明细")
public static class Item {
@ApiModelProperty(value = "商品标题")
private String title;
@ApiModelProperty(value = "规格描述")
private String skuDesc;
@ApiModelProperty(value = "数量")
private String quantity;
@ApiModelProperty(value = "单位")
private String unit;
}
@Data
@ApiModel(value = "采集数据")
public static class CollectedInfo {
@ApiModelProperty(value = "控件类型")
private String type;
@ApiModelProperty(value = "控件格式")
private String form;
@ApiModelProperty(value = "字段名")
private String fieldName;
@ApiModelProperty(value = "采集数据内容")
private String content;
}
@Data
@ApiModel(value = "支付的代客下单数据")
public static class PreOrder {
@ApiModelProperty(value = "预订单id")
private String preOrderId;
@ApiModelProperty(value = "预订单编号")
private String preOrderSn;
@ApiModelProperty(value = "请求号")
private String requestId;
}
}
package com.liquidnet.service.goblin.param.shouqianba.callback;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
@ApiModel(value = "SqbRefundContentDTO", description = "收钱吧退款推送内容详情")
public class RefundCallbackContent {
@ApiModelProperty(value = "售后单号 (全局唯一)", example = "71582143744560")
private String ticketSn;
@ApiModelProperty(value = "订单号", example = "2101056320290513285")
private String orderSn;
@ApiModelProperty(value = "原状态")
private Integer sourceState;
@ApiModelProperty(value = "目标状态 (20:完成, 15:退款完成, 0:创建, 10:退款中, 30:取消退款)", example = "15")
private Integer targetState;
@ApiModelProperty(value = "下单金额 (单位:分)", example = "100")
private Long amount;
@ApiModelProperty(value = "申请金额 (单位:分)", example = "100")
private Long applyAmount;
@ApiModelProperty(value = "请求号")
private String requestId;
@ApiModelProperty(value = "来源 (如: EXTERN)")
private String requestSource;
@ApiModelProperty(value = "退款的商品信息")
private SqbRefundItemContainer item;
@Data
@ApiModel("SqbRefundItemContainer")
public static class SqbRefundItemContainer {
@ApiModelProperty(value = "退款类型 (1:商品, 2:金额)")
private Byte type;
@ApiModelProperty(value = "商品明细列表")
private List<SqbRefundItemDetail> items;
}
@Data
@ApiModel("SqbRefundItemDetail")
public static class SqbRefundItemDetail {
@ApiModelProperty(value = "spu id")
private String spuId;
@ApiModelProperty(value = "sku id")
private String skuId;
@ApiModelProperty(value = "标题")
private String title;
@ApiModelProperty(value = "数量")
private String quantity;
@ApiModelProperty(value = "退款类型 (0:正常商品, 1:自定义商品)")
private Byte type;
}
}
package com.liquidnet.service.goblin.service; package com.liquidnet.service.goblin.service;
import com.liquidnet.service.goblin.param.shouqianba.callback.CallbackParams;
import com.liquidnet.service.goblin.param.shouqianba.request.*; import com.liquidnet.service.goblin.param.shouqianba.request.*;
import com.liquidnet.service.goblin.param.shouqianba.response.data.*; import com.liquidnet.service.goblin.param.shouqianba.response.data.*;
...@@ -95,4 +96,11 @@ public interface IGoblinShouQianBaService { ...@@ -95,4 +96,11 @@ public interface IGoblinShouQianBaService {
* @return * @return
*/ */
List<MallProductsQueryData> queryMallProducts(MallProductsQueryRequest request); List<MallProductsQueryData> queryMallProducts(MallProductsQueryRequest request);
/**
* 验签
* @param callbackParams
* @return
*/
boolean verifySignature(CallbackParams callbackParams);
} }
package com.liquidnet.service.goblin.controller; package com.liquidnet.service.goblin.controller;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.service.base.ResponseDto; import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.param.shouqianba.callback.CallbackParams;
import com.liquidnet.service.goblin.param.shouqianba.callback.CouponCallbackContent;
import com.liquidnet.service.goblin.param.shouqianba.callback.OrderCallbackContent;
import com.liquidnet.service.goblin.param.shouqianba.callback.RefundCallbackContent;
import com.liquidnet.service.goblin.service.IGoblinShouQianBaService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
...@@ -14,22 +22,66 @@ import org.springframework.web.bind.annotation.RestController; ...@@ -14,22 +22,66 @@ import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/sqb") @RequestMapping("/sqb")
public class GoblinShouqianbaController { public class GoblinShouqianbaController {
@Autowired
private IGoblinShouQianBaService shouQianBaService;
@ApiOperation("下单回调") @ApiOperation("下单回调")
@PostMapping("/order/callback") @PostMapping("/order/callback")
public ResponseDto<Boolean> orderCallback() { public ResponseDto<Boolean> orderCallback(@RequestBody CallbackParams callbackParams) {
if (null == callbackParams) {
log.error("下单回调参数为空");
return ResponseDto.failure("下单回调参数为空");
}
boolean verified = shouQianBaService.verifySignature(callbackParams);
if (!verified) {
log.error("下单回调验签失败");
return ResponseDto.failure("验签失败");
}
OrderCallbackContent orderCallbackContent = JsonUtils.fromJson(callbackParams.getContent(), OrderCallbackContent.class);
log.info("下单回调content: {}", callbackParams.getContent());
//TODO 业务
return ResponseDto.success(); return ResponseDto.success();
} }
@ApiOperation("退款回调") @ApiOperation("退款回调")
@PostMapping("/refund/callback") @PostMapping("/refund/callback")
public ResponseDto<Boolean> refundCallback() { public ResponseDto<Boolean> refundCallback(@RequestBody CallbackParams callbackParams) {
if (null == callbackParams) {
log.error("退款回调参数为空");
return ResponseDto.failure("退款回调参数为空");
}
boolean verified = shouQianBaService.verifySignature(callbackParams);
if (!verified) {
log.error("退款回调验签失败");
return ResponseDto.failure("验签失败");
}
RefundCallbackContent refundCallbackContent = JsonUtils.fromJson(callbackParams.getContent(), RefundCallbackContent.class);
log.info("退款回调content: {}", callbackParams.getContent());
//TODO 业务
return ResponseDto.success(); return ResponseDto.success();
} }
@ApiOperation("券状态回调") @ApiOperation("券状态回调")
@PostMapping("/coupon/callback") @PostMapping("/coupon/callback")
public ResponseDto<Boolean> couponCallback() { public ResponseDto<Boolean> couponCallback(@RequestBody CallbackParams callbackParams) {
if (null == callbackParams) {
log.error("券状态回调参数为空");
return ResponseDto.failure("券状态回调参数为空");
}
boolean verified = shouQianBaService.verifySignature(callbackParams);
if (!verified) {
log.error("券状态回调验签失败");
return ResponseDto.failure("验签失败");
}
CouponCallbackContent callbackContent = JsonUtils.fromJson(callbackParams.getContent(), CouponCallbackContent.class);
log.info("券状态回调content: {}", callbackParams.getContent());
//TODO 业务
return ResponseDto.success(); return ResponseDto.success();
} }
......
...@@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; ...@@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.liquidnet.commons.lang.util.HttpUtil; import com.liquidnet.commons.lang.util.HttpUtil;
import com.liquidnet.commons.lang.util.MD5Utils; import com.liquidnet.commons.lang.util.MD5Utils;
import com.liquidnet.service.goblin.config.properties.ShouqianbaProperties; import com.liquidnet.service.goblin.config.properties.ShouqianbaProperties;
import com.liquidnet.service.goblin.param.shouqianba.callback.CallbackParams;
import com.liquidnet.service.goblin.param.shouqianba.request.*; import com.liquidnet.service.goblin.param.shouqianba.request.*;
import com.liquidnet.service.goblin.param.shouqianba.response.*; import com.liquidnet.service.goblin.param.shouqianba.response.*;
import com.liquidnet.service.goblin.param.shouqianba.response.data.*; import com.liquidnet.service.goblin.param.shouqianba.response.data.*;
...@@ -223,17 +224,12 @@ public class GoblinShouQianBaServiceImpl implements IGoblinShouQianBaService { ...@@ -223,17 +224,12 @@ public class GoblinShouQianBaServiceImpl implements IGoblinShouQianBaService {
/** /**
* 收钱吧回调推送验签 * 收钱吧回调推送验签
* *
* @param signature 收钱吧推送来的签名 (signature) * @param callbackParams@return 验签是否通过
* @param eventId 事件ID (eventId)
* @param timestamp 时间戳 (timestamp)
* @param nonce 随机数 (nonce)
* @param content 业务数据报文 (content)
* @return 验签是否通过
*/ */
public boolean verifySignature(String signature, String eventId, String timestamp, String nonce, String content) { public boolean verifySignature(CallbackParams callbackParams) {
// 签名原串 = eventId + timestamp + nonce + content // 签名原串 = eventId + timestamp + nonce + content
String plaintext = eventId + timestamp + nonce + content; String plaintext = callbackParams.getEventId() + callbackParams.getTimestamp() + callbackParams.getNonce() + callbackParams.getContent();
return verifySignatureSHA256WithRSA(plaintext, signature, shouqianbaProperties.getPublicKey()); return verifySignatureSHA256WithRSA(plaintext, callbackParams.getSignature(), shouqianbaProperties.getPublicKey());
} }
public boolean verifySignatureSHA256WithRSA(String plaintext, String inputSignature, String pubKey) { public boolean verifySignatureSHA256WithRSA(String plaintext, String inputSignature, String pubKey) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment