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.beans.factory.annotation.Qualifier; 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 @Qualifier("huifuRestTemplate") 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 requestEntity = new HttpEntity<>(requestJson.toJSONString(), headers); log.info("发送斗拱提现请求到: {}", ENCASHMENT_URL); ResponseEntity 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; } } }