package com.tourguide.common.huifu.biz;
|
|
import com.alibaba.fastjson.JSONObject;
|
import com.tourguide.common.huifu.properties.HuiFuProperties;
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.http.*;
|
import org.springframework.stereotype.Component;
|
import org.springframework.web.client.RestTemplate;
|
|
import java.math.BigDecimal;
|
import java.security.KeyFactory;
|
import java.security.PrivateKey;
|
import java.security.Signature;
|
import java.security.spec.PKCS8EncodedKeySpec;
|
import java.text.SimpleDateFormat;
|
import java.util.Base64;
|
import java.util.Date;
|
|
/**
|
* 斗拱主动提现业务。
|
*
|
* 备注:该类只向斗拱发起提现,导游余额扣减、提现单状态变更应在本项目业务服务中幂等完成。
|
*/
|
@Component
|
public class DouGongWithdrawBiz {
|
private static final Logger log = LoggerFactory.getLogger(DouGongWithdrawBiz.class);
|
|
@Autowired
|
private HuiFuProperties huiFuProperties;
|
@Autowired
|
private RestTemplate restTemplate;
|
|
private static final String ENCASHMENT_URL = "https://api.huifu.com/v2/trade/settlement/encashment";
|
/**
|
* 主动取现接口。
|
*
|
* @param cashAmt 取现金额,单位元
|
* @param huifuId 取现方汇付 ID
|
* @param tokenNo 绑定取现卡后返回的取现卡序列号
|
* @return 斗拱响应结果
|
*/
|
public JSONObject encashment(BigDecimal cashAmt, String huifuId, String tokenNo) {
|
try {
|
log.info("开始调用斗拱提现接口,取现方ID: {}, 取现金额: {}", huifuId, cashAmt);
|
|
JSONObject data = new JSONObject();
|
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
|
String reqDate = dateFormat.format(new Date());
|
|
data.put("req_date", reqDate);
|
data.put("req_seq_id", DouGongRequestSupport.generateForHuiFu("TX"));
|
data.put("cash_amt", cashAmt.toString());
|
data.put("huifu_id", huifuId);
|
// 备注:提现到账周期先固定 D1,后续如果后台提供提现规则,再从配置或提现申请中读取。
|
data.put("into_acct_date_type", "D1");
|
data.put("token_no", tokenNo);
|
data.put("notify_url", huiFuProperties.getCallbackPath() + "/base/dougong/encashment");
|
|
String dataStr = data.toJSONString();
|
log.info("业务参数data: {}", dataStr);
|
|
JSONObject requestJson = new JSONObject();
|
requestJson.put("sys_id", huiFuProperties.getSysId());
|
requestJson.put("product_id", huiFuProperties.getProductId());
|
requestJson.put("data", dataStr);
|
|
// 备注:主动提现接口按完整报文签名,不复用支付创建接口只签 data 的逻辑。
|
String signData = requestJson.toJSONString();
|
log.info("待签名报文: {}", signData);
|
|
String signature = sign(signData, huiFuProperties.getRsaPrivateKey());
|
if (signature == null) {
|
log.error("签名生成失败");
|
return createErrorResult("签名生成失败");
|
}
|
|
requestJson.put("sign", signature);
|
|
HttpHeaders headers = new HttpHeaders();
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
|
HttpEntity<String> requestEntity = new HttpEntity<>(requestJson.toJSONString(), headers);
|
|
log.info("发送斗拱提现请求到: {}", ENCASHMENT_URL);
|
ResponseEntity<String> response = restTemplate.exchange(
|
ENCASHMENT_URL,
|
HttpMethod.POST,
|
requestEntity,
|
String.class
|
);
|
|
log.info("斗拱提现接口响应状态: {}, 响应内容: {}", response.getStatusCode(), response.getBody());
|
|
// 解析响应
|
JSONObject result = JSONObject.parseObject(response.getBody());
|
return result;
|
|
} catch (Exception e) {
|
log.error("调用斗拱提现接口异常: {}", e.getMessage(), e);
|
return createErrorResult("调用接口异常: " + e.getMessage());
|
}
|
}
|
|
|
private JSONObject createErrorResult(String errorMsg) {
|
JSONObject result = new JSONObject();
|
result.put("code", "9999");
|
result.put("msg", errorMsg);
|
result.put("success", false);
|
return result;
|
}
|
public static String sign(String data, String privateKeyBase64) {
|
// Base64 --> Key
|
try {
|
byte[] bytes = Base64.getDecoder().decode(privateKeyBase64);
|
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
|
KeyFactory keyFactory;
|
keyFactory = KeyFactory.getInstance("RSA");
|
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
|
// Sign
|
Signature signature = Signature.getInstance("SHA256WithRSA");
|
signature.initSign(privateKey);
|
signature.update(data.getBytes("UTF-8"));
|
return Base64.getEncoder().encodeToString(signature.sign());
|
} catch (Exception e) {
|
return null;
|
}
|
}
|
}
|