萤石支付,是对第三方支付平台的二次封装。考虑到的目的可能有:1.合资公司的金钱交易需要走总部财务 2.二次封装对接更加容易,与第三方支付sdk的交互都由萤石平台封装完成了。
*支付流程归纳总结:创建萤石订单——>拉起支付——>前端完成支付——>支付回调——>更新系统订单信息
调用萤石支付或者微信支付的接口都会用到签名,不同的平台会有自己的签名规约。
微信授权使用的是OAuth2.0授权的方式。通过微信授权,可以获取微信用户在本小程序下对应的openid(在支付的时候会用到),也可以结合自己的登录接口使我们系统与微信账号做一个绑定,后续就不需要重复授权,直接登录就行。
https://api.weixin.qq.com/sns/jscode2session?
,请求方式为get。@Value("${wechat.pay.token:5dfe2115674e7115}")
public void setWechatPayToken(String str){
HmacSHA256Util.wechatPayToken = str;
}
/***
* 利用Apache的工具类实现SHA-256加密
* @param parameterMap 加密参数
* @return
*/
public static String encryptSHA256Str(Map<String, Object> parameterMap) {
if(parameterMap.isEmpty()){
return null;
}
String originalText = getAlphabeticalSort(parameterMap);
MessageDigest messageDigest;
String encdeStr = "";
try {
messageDigest = MessageDigest.getInstance("SHA-256");
byte[] hash = messageDigest.digest(originalText.getBytes(StandardCharsets.UTF_8));
encdeStr = Hex.encodeHexString(hash);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return encdeStr;
}
/**
* @description 根据map首字母进行排序
* @date 2022/12/27 15:47
* @user mengwei6
* @param parameterMap 请求参数
* @return
*/
private static String getAlphabeticalSort(Map<String, Object> parameterMap){
// 排序
Map<String, Object> sortedMap = parameterMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).collect(
Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(oldVal, newVal) -> oldVal,
LinkedHashMap::new
)
);
// 封装
StringBuilder sb = new StringBuilder();
sortedMap.entrySet().forEach(obj->{ sb.append(obj.getKey()).append(obj.getValue());});
return sb+wechatPayToken;
}
/**
* 创建萤石萤石订单,并返回订单号
* @param timestamp 时间戳
* @param mobile 下单人的手机号
* @return 预支付订单id
*/
private String getPreOrderId(Long timestamp,String mobile){
log.warn("开始创建萤石萤石订单...");
// 封装参数
Map<String,Object> paramMap = new HashMap<>();
paramMap.put("app",app);
paramMap.put("user_name","ezmall");
paramMap.put("mobile",mobile);
paramMap.put("ssid",ssid);
// 这个商品是萤石创建租户的时候默认上架的一个商品,非本系统交易的商品,此商品可以信息可以问萤石相关人员获取
paramMap.put("products","[{\"bn\":\"包月套餐1\",\"num\":1,\"price\":\"0.01\"}]");
paramMap.put("timestamp",timestamp);
// 签名(上面签名方法生成)
String sign = HmacSHA256Util.encryptSHA256Str(paramMap);
paramMap.put("sign",sign);
String response = HttpUtil.post(orderCreateUrl, paramMap);
log.warn("获取预支付订单号:{}",response);
OpenResultBO<String> result = JSON.parseObject(response, new TypeReference<OpenResultBO<String>>(){});
if (PortalConstant.SUCCESS_RETURN.equals(result.getCode()) && StringUtils.isNotEmpty(result.getData())){
Map map = JSON.parseObject(result.getData(), Map.class);
log.warn("创建萤石订单id为{}",map.get("order_id").toString());
return map.get("order_id").toString();
}else {
log.error("状态码:{},错误信息:{}",result.getCode(),result.getMsg());
throw new BaseRuntimeException(PortalResultCode.CREATE_YS_ORDER_FAIL);
}
}
/**
* 调用萤石接口拉起支付
* @param preOrderId 萤石订单id
* @param amount 金额
* @param openId 微信openid
* @return
*/
private String getPay(String preOrderId,String amount,String openId){
log.warn("开始拉起萤石支付接口...");
// 封装参数
Map<String,Object> paramMap = new HashMap<>();
paramMap.put("ssid",ssid);
paramMap.put("app",app);
YsOrderParam ysOrderParam = YsOrderParam.builder().order_no(preOrderId).amount(amount).total_amount(amount).build();
List<YsOrderParam> ysOrderList = Collections.singletonList(ysOrderParam);
String orderParam = JSONObject.toJSONString(ysOrderList);
paramMap.put("order",orderParam);
paramMap.put("pm_code","miniapppay");
ExtParam extParam = ExtParam.builder().open_id(openId).build();
paramMap.put("ext",JSONObject.toJSON(extParam));
// 签名
String sign = HmacSHA256Util.encryptSHA256Str(paramMap);
paramMap.put("sign",sign);
String response = HttpUtil.post(getPayUrl, paramMap);
log.warn("拉起萤石支付接口获取参数:{}",response);
OpenResultBO<String> result = JSON.parseObject(response, new TypeReference<OpenResultBO<String>>(){});
if (PortalConstant.SUCCESS_RETURN.equals(result.getCode()) && ObjectUtil.isNotEmpty(result.getData())){
log.warn("拉起萤石支付接口调用成功,获取参数为{}",result.getData());
return result.getData();
}else {
log.error("状态码:{},错误信息:{}",result.getCode(),result.getMsg());
throw new BaseRuntimeException(PortalResultCode.CALL_YS_PAY);
}
}
/**
* 查询订单状态是否已支付
* @param order 萤石支付订单号
* @return
*/
private boolean checkIsPay(String order){
// 封装参数
Map<String,Object> paramMap = new HashMap<>();
paramMap.put("app",app);
paramMap.put("ssid",ssid);
paramMap.put("order",order);
// 签名
String sign = HmacSHA256Util.encryptSHA256Str(paramMap);
paramMap.put("sign",sign);
String response = HttpUtil.get(queryOrderStatus, paramMap);
OpenResultBO<String> result = JSON.parseObject(response, new TypeReference<OpenResultBO<String>>(){});
if (PortalConstant.SUCCESS_RETURN.equals(result.getCode()) && StringUtils.isNotEmpty(result.getData())){
List<Map> mapList = JSON.parseArray(result.getData(), Map.class);
if (CollectionUtils.isEmpty(mapList)){
throw new BaseRuntimeException(PortalResultCode.ORDER_NOT_FOUND);
}
Map map = mapList.get(PortalConstant.ZERO);
return Integer.valueOf(String.valueOf(map.get("pay_status"))).equals(PortalConstant.ORDER_PAY_SUCCESS);
}else {
log.error("状态码:{},错误信息:{}",result.getCode(),result.getMsg());
throw new BaseRuntimeException(PortalResultCode.QUERY_ORDER_FAIL);
}
}
@ApiOperation(value = "订单通知-1.3", notes = "支付回调接口")
@PostMapping("/orderNotify")
public Map<String,Object> orderNotify(@Validated OrderNotifyParam param) {
log.warn("================开始获取支付回调==================");
Map<String,Object> resultMap = new HashMap<>();
if (param == null){
log.warn("未获取到回调参数");
resultMap.put("code",-1);
resultMap.put("data",Collections.emptyList());
resultMap.put("message","未获取到回调参数");
return resultMap;
}
log.warn("支付回调接收到的参数:{}",param.toString());
Map<String, Object> map = JSON.parseObject(JSON.toJSONString(param), new TypeReference<Map<String, Object>>() {
});
if (!Objects.isNull(map)){
log.warn("================开始校验支付回调签名==================");
map.remove("sign");
String sign = HmacSHA256Util.encryptSHA256Str(map);
if (StringUtils.isNotEmpty(sign) && sign.equals(param.getSign())){
if (param.getPay_status() != null && param.getOrder_id() != null){
if (param.getPay_status().equals(1)){
// 回调更新订单状态,次数更新
wechatUserOrderService.paySuccess(param.getOrder_id());
log.warn("================获取支付回调结束==================");
// 返回调用方
resultMap.put("code",200);
resultMap.put("data",Collections.emptyList());
return resultMap;
}
}
}else {
resultMap.put("code",PortalResultCode.SIGN_CHECK_ERROR.getCode());
resultMap.put("data",Collections.emptyList());
resultMap.put("message",PortalResultCode.SIGN_CHECK_ERROR.getMessage());
log.warn(PortalResultCode.SIGN_CHECK_ERROR.getMessage());
return resultMap;
}
}
resultMap.put("code",-1);
resultMap.put("data",Collections.emptyList());
resultMap.put("message","回调参数异常");
log.warn("回调参数异常");
return resultMap;
}