Authored by LUOXC

ADD btn status query

... ... @@ -82,7 +82,20 @@ public class OrderHelpController {
@RequestMapping(value = "/transferQueryAsOriginalResult")
public ApiResponse transferQuery(String buyerOrderCode) {
return new ApiResponse.ApiResponseBuilder().code(200).data(alipayService.transferQueryAsOriginalResult(buyerOrderCode)).message("处理成功").build();
return new ApiResponse.ApiResponseBuilder()
.code(200)
.data(alipayService.transferQueryAsOriginalResult(buyerOrderCode))
.message("处理成功")
.build();
}
@RequestMapping(value = "/transferTtnStatusQuery")
public ApiResponse transferTtnStatusQuery(String buyerOrderCode) {
return new ApiResponse.ApiResponseBuilder()
.code(200)
.data(alipayService.transferTtnStatusQuery(buyerOrderCode))
.message("处理成功")
.build();
}
@IgnoreSession
... ...
... ... @@ -50,6 +50,7 @@ import com.yohoufo.order.service.pay.AbstractPayService;
import com.yohoufo.order.service.pay.alipay.AlipayCrossBorderService;
import com.yohoufo.order.service.pay.alipay.AlipayOuyinService;
import com.yohoufo.order.service.pay.alipay.bean.AlipayQueryCustomsResponse;
import com.yohoufo.order.service.pay.alipay.bean.TtnStatusQueryResponse;
import com.yohoufo.order.service.pay.unionpay.JsUnionpayService;
import com.yohoufo.order.service.pay.wallet.WalletPayService;
import com.yohoufo.order.service.pay.weixin.MiniappWeixinPayService;
... ...
... ... @@ -13,7 +13,6 @@ import com.yohobuy.ufo.model.order.common.Payment;
import com.yohobuy.ufo.model.user.resp.AuthorizeResultRespVO;
import com.yohoufo.common.utils.DateUtil;
import com.yohoufo.common.utils.HttpClient;
import com.yohoufo.common.utils.StringUtil;
import com.yohoufo.common.utils.WXUtil;
import com.yohoufo.dal.order.OrdersPayHbfqMapper;
import com.yohoufo.dal.order.model.OrdersPayHbfq;
... ... @@ -25,6 +24,7 @@ import com.yohoufo.order.model.PayRefundBo;
import com.yohoufo.order.model.TransferData;
import com.yohoufo.order.service.PaymentSupportService;
import com.yohoufo.order.service.pay.AbstractPayService;
import com.yohoufo.order.service.pay.alipay.bean.TtnStatusQueryResponse;
import com.yohoufo.order.service.support.codegenerator.OrderCodeGenerator;
import com.yohoufo.order.service.transfer.TransferChannel;
import com.yohoufo.order.service.transfer.TransferResult;
... ... @@ -49,195 +49,196 @@ import java.util.function.BiConsumer;
public abstract class AbstractAlipayService extends AbstractPayService {
private static final Logger logger = LoggerFactory.getLogger("alipayLogger");
private static final Logger logger = LoggerFactory.getLogger("alipayLogger");
@Autowired
private HttpClient httpClient;
@Autowired
private HttpClient httpClient;
protected abstract AlipaySignatureSetting getAlipaySignatureSetting();
protected abstract AlipaySignatureSetting getAlipaySignatureSetting();
protected final AlipaySignatureHelper helper() {
return factory.newAlipaySignatureHelper(getAlipaySignatureSetting());
}
protected final AlipaySignatureHelper helper() {
return factory.newAlipaySignatureHelper(getAlipaySignatureSetting());
}
protected final String getAppId() {
return getAlipaySignatureSetting().getAppId();
}
protected final String getAppId() {
return getAlipaySignatureSetting().getAppId();
}
protected final String getPartnerId() {
return getAlipaySignatureSetting().getPartnerId();
}
protected final String getPartnerId() {
return getAlipaySignatureSetting().getPartnerId();
}
protected final String getAlipaySignType() {
return getAlipaySignatureSetting().getRsaType();
}
protected final String getAlipaySignType() {
return getAlipaySignatureSetting().getRsaType();
}
protected String getAccountUserName() {
return "";
}
protected String getAccountUserName() {
return "";
}
protected String getAccountEmail() {
return "";
}
protected String getAccountEmail() {
return "";
}
@Autowired
private AlipaySignatureHelperFactory factory;
@Autowired
private AlipaySignatureHelperFactory factory;
@Autowired
PaymentSupportService paymentSupportService;
@Autowired
PaymentSupportService paymentSupportService;
@Autowired
OrderCodeGenerator orderCodeGenerator;
@Autowired
OrderCodeGenerator orderCodeGenerator;
@Autowired
private OrdersPayHbfqMapper ordersPayHbfqDao;
@Autowired
private OrdersPayHbfqMapper ordersPayHbfqDao;
@Autowired
@Getter(AccessLevel.PROTECTED)
@Accessors(fluent = true)
private AlipayConfig alipayConfig;
@Autowired
@Getter(AccessLevel.PROTECTED)
@Accessors(fluent = true)
private AlipayConfig alipayConfig;
@Value("${alipay.notifyurl}")
private String notifyURL;
@Value("${alipay.notifyurl}")
private String notifyURL;
@Value("${alipay.transfer.notifyurl}")
private String transferNotifyURL;
@Value("${alipay.transfer.notifyurl}")
private String transferNotifyURL;
public String getNotifyURL(){
return notifyURL;
}
public String getNotifyURL() {
return notifyURL;
}
public PayQueryBo payQuery(String tradeNo, int orderCreateTime) {
Map<String, String> queryParams = buildOpenApiQueryParams(tradeNo);
String respTxt = sendOpenApiRequest(tradeNo, queryParams, alipayConfig.openapiUrl());
QueryBo queryBo = JSON.parseObject(JSONObject.toJSONString(JSON.parseObject(respTxt).getJSONObject("alipay_trade_query_response")), QueryBo.class);
public PayQueryBo payQuery(String tradeNo, int orderCreateTime) {
Map<String, String> queryParams = buildOpenApiQueryParams(tradeNo);
String respTxt = sendOpenApiRequest(tradeNo, queryParams, alipayConfig.openapiUrl());
QueryBo queryBo = JSON.parseObject(JSONObject.toJSONString(JSON.parseObject(respTxt).getJSONObject("alipay_trade_query_response")), QueryBo.class);
// 查询结果转换成 共通的对象
PayQueryBo payQueryBo = new PayQueryBo();
// 查询结果转换成 共通的对象
PayQueryBo payQueryBo = new PayQueryBo();
if ("10000".equals(queryBo.getCode())){
// 订单支付时的商户号
payQueryBo.setPayOrderCode(queryBo.getOut_trade_no());
payQueryBo.setTradeNo(queryBo.getTrade_no());
if ("10000".equals(queryBo.getCode())) {
// 订单支付时的商户号
payQueryBo.setPayOrderCode(queryBo.getOut_trade_no());
payQueryBo.setTradeNo(queryBo.getTrade_no());
if ("TRADE_SUCCESS".equals(queryBo.getTrade_status())) {
payQueryBo.setPayStatus(true);
if ("TRADE_SUCCESS".equals(queryBo.getTrade_status())) {
payQueryBo.setPayStatus(true);
}else if("TRADE_CLOSED".equals(queryBo.getTrade_status())){
payQueryBo.setRefundStatus(true);
}
} else if ("TRADE_CLOSED".equals(queryBo.getTrade_status())) {
payQueryBo.setRefundStatus(true);
}
payQueryBo.setAmount(Double.parseDouble(queryBo.getTotal_amount()));
payQueryBo.setBuyerId(queryBo.getBuyer_user_id());
}
payQueryBo.setAmount(Double.parseDouble(queryBo.getTotal_amount()));
payQueryBo.setBuyerId(queryBo.getBuyer_user_id());
}
return payQueryBo;
}
return payQueryBo;
}
public JSONObject prepayRequest(OrderInfo orderInfo) {
logger.info("prepayRequest orderInfo is {}", orderInfo);
public JSONObject prepayRequest(OrderInfo orderInfo) {
logger.info("prepayRequest orderInfo is {}", orderInfo);
Map<String, String> queryParams = buildOpenApiPayParams(orderInfo);
StringBuilder res = new StringBuilder();
for (String key : queryParams.keySet()){
res.append(key);
res.append("=");
res.append(queryParams.get(key));
res.append("&");
}
Map<String, String> queryParams = buildOpenApiPayParams(orderInfo);
StringBuilder res = new StringBuilder();
for (String key : queryParams.keySet()) {
res.append(key);
res.append("=");
res.append(queryParams.get(key));
res.append("&");
}
JSONObject rr = new JSONObject();
rr.put("payParams", res.toString().substring(0, res.toString().length()-1));
JSONObject rr = new JSONObject();
rr.put("payParams", res.toString().substring(0, res.toString().length() - 1));
logger.info("[{}] prepay request: {}", orderInfo.getOrderCode(), rr);
return rr;
logger.info("[{}] prepay request: {}", orderInfo.getOrderCode(), rr);
return rr;
/**
* app_id=2015052600090779
* &biz_content=%7B%22timeout_express%22%3A%2230m%22%2C%22seller_id%22%3A%22%22%2C%22product_code%22%3A%22QUICK_MSECURITY_PAY%22%2C%22total_amount%22%3A%220.02%22%2C%22subject%22%3A%221%22%2C%22body%22%3A%22%E6%88%91%E6%98%AF%E6%B5%8B%E8%AF%95%E6%95%B0%E6%8D%AE%22%2C%22out_trade_no%22%3A%22314VYGIAGG7ZOYY%22%7D
* &charset=utf-8
* &method=alipay.trade.app.pay&sign_type=RSA2
* &timestamp=2016-08-15%2012%3A12%3A15
* &version=1.0
* &sign=MsbylYkCzlfYLy9PeRwUUIg9nZPeN9SfXPNavUCroGKR5Kqvx0nEnd3eRmKxJuthNUx4ERCXe552EV9PfwexqW%2B1wbKOdYtDIb4%2B7PL3Pc94RZL0zKaWcaY3tSL89%2FuAVUsQuFqEJdhIukuKygrXucvejOUgTCfoUdwTi7z%2BZzQ%3D
*/
/**
* app_id=2015052600090779
* &biz_content=%7B%22timeout_express%22%3A%2230m%22%2C%22seller_id%22%3A%22%22%2C%22product_code%22%3A%22QUICK_MSECURITY_PAY%22%2C%22total_amount%22%3A%220.02%22%2C%22subject%22%3A%221%22%2C%22body%22%3A%22%E6%88%91%E6%98%AF%E6%B5%8B%E8%AF%95%E6%95%B0%E6%8D%AE%22%2C%22out_trade_no%22%3A%22314VYGIAGG7ZOYY%22%7D
* &charset=utf-8
* &method=alipay.trade.app.pay&sign_type=RSA2
* &timestamp=2016-08-15%2012%3A12%3A15
* &version=1.0
* &sign=MsbylYkCzlfYLy9PeRwUUIg9nZPeN9SfXPNavUCroGKR5Kqvx0nEnd3eRmKxJuthNUx4ERCXe552EV9PfwexqW%2B1wbKOdYtDIb4%2B7PL3Pc94RZL0zKaWcaY3tSL89%2FuAVUsQuFqEJdhIukuKygrXucvejOUgTCfoUdwTi7z%2BZzQ%3D
*/
}
}
/**
* 转账功能
* @param transferOrderCode
* @param alipayAccount
* @param transferAmount
* @return
* @throws Exception
*/
public JSONObject transferMoney(String transferOrderCode, String alipayUid, String alipayAccount, BigDecimal transferAmount) throws Exception{
/**
* 转账功能
*
* @param transferOrderCode
* @param alipayAccount
* @param transferAmount
* @return
* @throws Exception
*/
public JSONObject transferMoney(String transferOrderCode, String alipayUid, String alipayAccount, BigDecimal transferAmount) throws Exception {
Map<String, String> queryParams = buildTransferParams(transferOrderCode, alipayUid, alipayAccount, transferAmount);
String respTxt = sendOpenApiRequestWithException(transferOrderCode, queryParams);
Map<String, String> queryParams = buildTransferParams(transferOrderCode, alipayUid, alipayAccount, transferAmount);
String respTxt = sendOpenApiRequestWithException(transferOrderCode, queryParams);
JSONObject result = null;
JSONObject result = null;
if (StringUtils.isNotBlank(respTxt)){
JSONObject json = JSON.parseObject(respTxt);
result = json.getJSONObject("alipay_fund_trans_toaccount_transfer_response");
if (StringUtils.isNotBlank(respTxt)) {
JSONObject json = JSON.parseObject(respTxt);
result = json.getJSONObject("alipay_fund_trans_toaccount_transfer_response");
}
return result;
}
}
return result;
}
public class AlipayTransfer implements TransferChannel {
int uid;
public class AlipayTransfer implements TransferChannel {
int uid;
String transferOrderCode;
String alipayUid;
String alipayAccount;
BigDecimal transferAmount;
List<BiConsumer<Integer, AuthorizeResultRespVO>> rishWatcherConsumer;
List<BiConsumer<Integer, AuthorizeResultRespVO>> rishWatcherConsumer;
private AlipayTransfer(int uid){
this.uid = uid;
private AlipayTransfer(int uid) {
this.uid = uid;
}
public AlipayTransfer transferOrderCode(String transferOrderCode){
public AlipayTransfer transferOrderCode(String transferOrderCode) {
this.transferOrderCode = transferOrderCode;
return this;
}
public AlipayTransfer alipayUid(String alipayUid){
public AlipayTransfer alipayUid(String alipayUid) {
this.alipayUid = alipayUid;
return this;
}
public AlipayTransfer alipayAccount(String alipayAccount){
public AlipayTransfer alipayAccount(String alipayAccount) {
this.alipayAccount = alipayAccount;
return this;
}
public AlipayTransfer transferAmount(BigDecimal transferAmount){
public AlipayTransfer transferAmount(BigDecimal transferAmount) {
this.transferAmount = transferAmount;
return this;
}
public AlipayTransfer riskWatcher(List<BiConsumer<Integer, AuthorizeResultRespVO>> rishWatcherConsumer){
this.rishWatcherConsumer = rishWatcherConsumer;
return this;
}
public AlipayTransfer riskWatcher(List<BiConsumer<Integer, AuthorizeResultRespVO>> rishWatcherConsumer) {
this.rishWatcherConsumer = rishWatcherConsumer;
return this;
}
@Override
public TransferResult transfer() {
AuthorizeResultRespVO aar = AuthorizeResultRespVO.builder().alipayId(alipayUid)
.alipayAccount(alipayAccount).build();
AuthorizeResultRespVO aar = AuthorizeResultRespVO.builder().alipayId(alipayUid)
.alipayAccount(alipayAccount).build();
rishWatcherConsumer.stream().forEach(x-> x.accept(uid, aar));
rishWatcherConsumer.stream().forEach(x -> x.accept(uid, aar));
Map<String, String> queryParams = buildTransferParams(transferOrderCode, alipayUid, alipayAccount, transferAmount);
String respTxt;
try {
... ... @@ -250,7 +251,7 @@ public abstract class AbstractAlipayService extends AbstractPayService {
.msg("未知异常:" + e.getMessage())
.build();
}
if (StringUtils.isNotBlank(respTxt)){
if (StringUtils.isNotBlank(respTxt)) {
JSONObject json = JSON.parseObject(respTxt);
JSONObject result = json.getJSONObject("alipay_fund_trans_toaccount_transfer_response");
String code = result.getString("code");
... ... @@ -259,7 +260,7 @@ public abstract class AbstractAlipayService extends AbstractPayService {
String order_id = result.getString("order_id");
String pay_date = result.getString("pay_date");
if (StringUtils.equals("10000", code)) {
if(StringUtils.isNoneBlank(order_id)){
if (StringUtils.isNoneBlank(order_id)) {
return TransferResult.builder()
.code(200)
.msg(msg)
... ... @@ -267,7 +268,7 @@ public abstract class AbstractAlipayService extends AbstractPayService {
.tradeNo(order_id)
.payDate(pay_date)
.build();
}else {
} else {
return TransferResult.builder()
.code(599)
.msg("未知异常:code is 10000 but order_id is blank")
... ... @@ -286,93 +287,93 @@ public abstract class AbstractAlipayService extends AbstractPayService {
}
}
public AlipayTransfer newAlipayTransfer(int uid){
public AlipayTransfer newAlipayTransfer(int uid) {
return new AlipayTransfer(uid);
}
public AlipayExceedMillionTransferChannel newAlipayExceedMillionTransfer(int uid){
public AlipayExceedMillionTransferChannel newAlipayExceedMillionTransfer(int uid) {
return new AlipayExceedMillionTransferChannel(uid);
}
/**
* 转账功能
*
* @param transferOrderCode
* @param alipayAccount
* @param transferAmount
* @return
* @throws Exception
*/
public Map<String, String> transferMoneyWhenExceedMillion(String transferOrderCode, String businessId, String alipayUid, String alipayAccount, String userName, BigDecimal transferAmount) throws Exception {
Map<String, String> queryParams = buildTransferParamsWhenExceedMillion(transferOrderCode, businessId, alipayUid, alipayAccount, userName, transferAmount);
String respTxt = sendMApiRequestWithException(transferOrderCode, queryParams);
/**
* 转账功能
* @param transferOrderCode
* @param alipayAccount
* @param transferAmount
* @return
* @throws Exception
*/
public Map<String, String> transferMoneyWhenExceedMillion(String transferOrderCode, String businessId, String alipayUid, String alipayAccount, String userName, BigDecimal transferAmount) throws Exception{
Map<String, String> result;
Map<String, String> queryParams = buildTransferParamsWhenExceedMillion(transferOrderCode, businessId, alipayUid, alipayAccount, userName, transferAmount);
String respTxt = sendMApiRequestWithException(transferOrderCode, queryParams);
Map<String, String> result;
if (StringUtils.isNotBlank(respTxt)){
result = WXUtil.parseWXPayXml(respTxt);
} else {
result = new HashMap<>();
}
return result;
}
if (StringUtils.isNotBlank(respTxt)) {
result = WXUtil.parseWXPayXml(respTxt);
} else {
result = new HashMap<>();
}
return result;
}
public class AlipayExceedMillionTransferChannel implements TransferChannel{
int uid;
public class AlipayExceedMillionTransferChannel implements TransferChannel {
int uid;
String transferOrderCode;
String alipayUid;
String alipayAccount;
BigDecimal transferAmount;
String businessId;
String userName;
List<BiConsumer<Integer, AuthorizeResultRespVO>> riskWatcher;
private AlipayExceedMillionTransferChannel(int uid){
this.uid = uid;
List<BiConsumer<Integer, AuthorizeResultRespVO>> riskWatcher;
private AlipayExceedMillionTransferChannel(int uid) {
this.uid = uid;
}
public AlipayExceedMillionTransferChannel transferOrderCode(String transferOrderCode){
public AlipayExceedMillionTransferChannel transferOrderCode(String transferOrderCode) {
this.transferOrderCode = transferOrderCode;
return this;
}
public AlipayExceedMillionTransferChannel alipayUid(String alipayUid){
public AlipayExceedMillionTransferChannel alipayUid(String alipayUid) {
this.alipayUid = alipayUid;
return this;
}
public AlipayExceedMillionTransferChannel alipayAccount(String alipayAccount){
public AlipayExceedMillionTransferChannel alipayAccount(String alipayAccount) {
this.alipayAccount = alipayAccount;
return this;
}
public AlipayExceedMillionTransferChannel transferAmount(BigDecimal transferAmount){
public AlipayExceedMillionTransferChannel transferAmount(BigDecimal transferAmount) {
this.transferAmount = transferAmount;
return this;
}
public AlipayExceedMillionTransferChannel businessId(String businessId){
public AlipayExceedMillionTransferChannel businessId(String businessId) {
this.businessId = businessId;
return this;
}
public AlipayExceedMillionTransferChannel userName(String userName){
public AlipayExceedMillionTransferChannel userName(String userName) {
this.userName = userName;
return this;
}
public AlipayExceedMillionTransferChannel riskWatcher(List<BiConsumer<Integer, AuthorizeResultRespVO>> riskWatcher){
this.riskWatcher = riskWatcher;
return this;
}
public AlipayExceedMillionTransferChannel riskWatcher(List<BiConsumer<Integer, AuthorizeResultRespVO>> riskWatcher) {
this.riskWatcher = riskWatcher;
return this;
}
@Override
public TransferResult transfer() {
AuthorizeResultRespVO aar = AuthorizeResultRespVO.builder().alipayId(alipayUid)
.alipayAccount(alipayAccount).build();
riskWatcher.stream().forEach(x-> x.accept(uid, aar));
AuthorizeResultRespVO aar = AuthorizeResultRespVO.builder().alipayId(alipayUid)
.alipayAccount(alipayAccount).build();
riskWatcher.stream().forEach(x -> x.accept(uid, aar));
Map<String, String> queryParams = buildTransferParamsWhenExceedMillion(transferOrderCode, businessId, alipayUid, alipayAccount, userName, transferAmount);
String respTxt;
try {
... ... @@ -387,7 +388,7 @@ public abstract class AbstractAlipayService extends AbstractPayService {
}
Map<String, String> result;
if (StringUtils.isNotBlank(respTxt)){
if (StringUtils.isNotBlank(respTxt)) {
result = WXUtil.parseWXPayXml(respTxt);
if (StringUtils.equals("T", result.get("is_success"))) {
return TransferResult.builder()
... ... @@ -396,26 +397,26 @@ public abstract class AbstractAlipayService extends AbstractPayService {
.build();
} else {
logger.info("batch_trans_notify_no_pwd transferOrderCode {} transferAmount {} result {}", transferOrderCode, transferAmount, result);
logger.info("batch_trans_notify_no_pwd transferOrderCode {} transferAmount {} result {}", transferOrderCode, transferAmount, result);
String errorCode = result.get("error");
AlipayBatchTransferErrorEnum abtee;
//收集来的错误码
//TODO 错误码需要不断完善
if (Objects.nonNull(abtee =AlipayBatchTransferErrorEnum.getByCode(errorCode))) {
String desc = formatErrorDesc(abtee, transferOrderCode);
return TransferResult.builder()
.code(501)
.payErrorCode(errorCode)
.msg(desc)
.build();
} else {
return TransferResult.builder()
.code(599)
.payErrorCode(errorCode)
.msg("未知异常:error is " + errorCode)
.build();
}
AlipayBatchTransferErrorEnum abtee;
//收集来的错误码
//TODO 错误码需要不断完善
if (Objects.nonNull(abtee = AlipayBatchTransferErrorEnum.getByCode(errorCode))) {
String desc = formatErrorDesc(abtee, transferOrderCode);
return TransferResult.builder()
.code(501)
.payErrorCode(errorCode)
.msg(desc)
.build();
} else {
return TransferResult.builder()
.code(599)
.payErrorCode(errorCode)
.msg("未知异常:error is " + errorCode)
.build();
}
}
... ... @@ -429,694 +430,732 @@ public abstract class AbstractAlipayService extends AbstractPayService {
}
private static String formatErrorDesc(AlipayBatchTransferErrorEnum abtee, String transferOrderCode){
String desc = abtee.getDesc();
if (abtee.equals(AlipayBatchTransferErrorEnum.TRANS_NO_NOT_UNIQUE)){
desc = String.format(desc, transferOrderCode);
}
return desc;
}
public PayQueryBo transferQuery(String buyerOrderCode) {
JSONObject result = transferQueryAsOriginalResult(buyerOrderCode);
// 查询结果转换成 共通的对象
PayQueryBo payQueryBo = new PayQueryBo();
if ("10000".equals(result.getString("code")) && "SUCCESS".equals(result.getString("status"))) {
payQueryBo.setPayStatus(true);
} else {
payQueryBo.setPayStatus(false);
}
return payQueryBo;
}
public JSONObject transferQueryAsOriginalResult(String buyerOrderCode) {
String tradeNo = buyerOrderCode;
Map<String, String> queryParams = buildAlipayTransferQueryParams(tradeNo);
String respTxt = sendOpenApiRequest(tradeNo, queryParams, alipayConfig.openapiUrl());
JSONObject result = JSON.parseObject(respTxt).getJSONObject("alipay_fund_trans_order_query_response");
logger.info("查询转账{}结果为:{}", tradeNo, result);
return result;
}
/**
* 发送预下单请求
* @param orderCode
* @param paramMap
* @return
*/
public String sendOpenApiRequest(String orderCode, Map<String, String> paramMap, String url) {
logger.info("[{}] send openapi request: {}, {}", orderCode, paramMap, url);
String response = "";
try {
response = httpClient.postFormData(url, paramMap);
} catch (Exception e) {
logger.error("[{}] trade openapi send failed: {}", orderCode, e.getMessage());
return response;
}
logger.info("[{}] trade openapi resp: {}", orderCode, response);
return response;
}
private String sendOpenApiRequestWithException(String orderCode, Map<String, String> paramMap) throws Exception {
logger.info("[{}] send openapi request: {}", orderCode, paramMap);
String response = httpClient.postFormData(alipayConfig.openapiUrl(), paramMap);
logger.info("[{}] trade openapi resp: {}", orderCode, response);
return response;
}
private String sendMApiRequestWithException(String orderCode, Map<String, String> paramMap) throws Exception {
logger.info("[{}] send openapi request: {}", orderCode, paramMap);
StringBuilder paramStr = new StringBuilder();
for(Map.Entry<String, String> entry : paramMap.entrySet()) {
paramStr.append("&").append(entry.getKey()).append("=").append(URLEncoder.encode(entry.getValue(),AlipayConfig.input_charset));
}
String url = alipayConfig.mapiUrl() +"?"+paramStr.substring(1);
logger.info("[{}] send openapi requestUrl is: {}", orderCode, url);
String response = httpClient.get(url);
logger.info("[{}] trade openapi resp: {}", orderCode, response);
return response;
}
/**
* openapi支付查询
* @return
*/
private Map<String, String> buildOpenApiQueryParams(String orderCode) {
Map<String, String> params = new HashMap<String, String>();
params.put("app_id", getAppId());
params.put("method", "alipay.trade.query");
params.put("charset", AlipayConfig.input_charset);
params.put("sign_type", "RSA");
params.put("timestamp", DateUtil.getCurrentTime());
params.put("version", "1.0");
JSONObject bizJson = new JSONObject();
bizJson.put("out_trade_no", orderCode);
params.put("biz_content", bizJson.toJSONString());
String preSignStr = getOpenApiSignString(params,false);
params.put("sign", helper().signWithRsa(preSignStr,AlipayConfig.input_charset));
return params;
}
/**
* openapi支付查询
* @return
*/
private Map<String, String> buildAlipayTransferQueryParams(String orderCode) {
Map<String, String> params = new HashMap<String, String>();
params.put("app_id", getAppId());
params.put("method", "alipay.fund.trans.order.query");
params.put("charset", AlipayConfig.input_charset);
params.put("sign_type", "RSA");
params.put("timestamp", DateUtil.getCurrentTime());
params.put("version", "1.0");
JSONObject bizJson = new JSONObject();
bizJson.put("out_biz_no", orderCode);
params.put("biz_content", bizJson.toJSONString());
String preSignStr = getOpenApiSignString(params, false);
params.put("sign", helper().signWithRsa(preSignStr,AlipayConfig.input_charset));
return params;
}
/**
* 单笔转账
* @return
*/
private Map<String, String> buildTransferParams(String transferOrderCode, String alipayUid, String alipayAccount, BigDecimal transferAmount) {
Map<String, String> params = new HashMap<String, String>();
params.put("app_id", getAppId());
params.put("method", "alipay.fund.trans.toaccount.transfer");
params.put("charset", AlipayConfig.input_charset);
params.put("sign_type", "RSA");
params.put("timestamp", DateUtil.getCurrentTime());
params.put("version", "1.0");
JSONObject bizJson = new JSONObject();
bizJson.put("out_biz_no", transferOrderCode); // 商户转账唯一订单号
if(StringUtils.isNotBlank(alipayUid)) {
bizJson.put("payee_type", "ALIPAY_USERID");
bizJson.put("payee_account", alipayUid); // 支付宝uid
} else {
bizJson.put("payee_type", "ALIPAY_LOGONID");
bizJson.put("payee_account", alipayAccount); // 支付宝账号
}
bizJson.put("amount", transferAmount.toString());
params.put("biz_content", bizJson.toJSONString());
String preSignStr = getOpenApiSignString(params, false);
params.put("sign", helper().signWithRsa(preSignStr,AlipayConfig.input_charset));
return params;
}
/**
* 单笔转账
* 支持千万/日 转账
* 批量付款到支付宝账户
* https://docs.open.alipay.com/64/104804/
* @return
*/
private Map<String, String> buildTransferParamsWhenExceedMillion(String transferOrderCode, String businessId, String alipayUid, String alipayAccount, String userName, BigDecimal transferAmount) {
Map<String, String> params = new HashMap<String, String>();
params.put("service", "batch_trans_notify_no_pwd");
params.put("partner", getPartnerId());
params.put("_input_charset", AlipayConfig.input_charset);
params.put("notify_url", transferNotifyURL);
params.put("account_name", getAccountUserName());
params.put("pay_date", new SimpleDateFormat("YYYYMMdd").format(new Date()));
params.put("batch_no", transferOrderCode);
params.put("batch_num", "1");
params.put("batch_fee", transferAmount.toString());
params.put("email", getAccountEmail());
StringBuilder detailAppender = new StringBuilder();
detailAppender.append(businessId).append("^");
if (StringUtils.isNotBlank(alipayUid)) {
// 流水号1^收款方UserId1^付款金额1^备注说明1
params.put("detail_version", "1.1");
detailAppender.append(alipayUid).append("^");
} else {
// 流水号1^收款方账号1^收款账号姓名1^付款金额1^备注说明1
params.put("detail_version", "1.0");
detailAppender.append(alipayAccount).append("^").append(userName).append("^");
}
detailAppender.append(transferAmount).append("^UFO_ORDER");
params.put("detail_data", detailAppender.toString());
String preSignStr = getOpenApiSignString(params, false);
params.put("sign", helper().signWithRsa(preSignStr,AlipayConfig.input_charset));
params.put("sign_type", "RSA");
return params;
}
/**
* app支付结果
* @param orderInfo
* @return
*/
protected Map<String, String> buildOpenApiPayParams(OrderInfo orderInfo) {
long orderCode= orderInfo.getOrderCode();
String tradeNo = orderInfo.getOutTradeNo();
BigDecimal amount = orderInfo.getAmount();
int payExpireTime = orderInfo.getPayExpireTime();
int payment = orderInfo.getPayment();
int hbfqNums = orderInfo.getHbfqNums();
int uid = orderInfo.getUid();
//build
Map<String, String> params = new HashMap<String, String>();
params.put("app_id", getAppId());
params.put("method", "alipay.trade.app.pay");
params.put("charset", AlipayConfig.input_charset);
params.put("sign_type", "RSA");
params.put("timestamp", DateUtil.getCurrentTime());
params.put("version", "1.0");
params.put("notify_url", notifyURL);
if (Payment.isHB(payment)){
params.put("specified_channel", "pcredit"); // 花呗单渠道
}else if(Payment.isHBFQ(payment)){
HbfqEnum hbfqEnum = HbfqEnum.getByfqTerms(hbfqNums);
if(hbfqEnum == null) {
logger.warn("fqNums from req invalid, fqNums: {}, orderCode: {}", hbfqNums, tradeNo);
throw new ServiceException(ServiceError.ORDER_FQNUM_INVALID);
}
String outTradeNo = tradeNo + hbfqEnum.getTradeNoPostfix();
params.put("out_trade_no", outTradeNo);
params.put("extend_params", getHbfqParam(hbfqEnum.getFqTerms()).toJSONString());
// 记录分期
recordOrdersPayHbfq(outTradeNo, tradeNo, uid);
tradeNo = outTradeNo;
}
if (paymentSupportService.isForbidVirtualPayChannel(orderCode)){
params.put("disable_pay_channels", "credit_group"); // 禁用信用卡
}
JSONObject bizJson = new JSONObject(true);
bizJson.put("timeout_express", payExpireTime+"m"); //该订单允许的最晚付款时间
bizJson.put("seller_id", "");
bizJson.put("product_code", "QUICK_MSECURITY_PAY");
bizJson.put("total_amount", amount.toPlainString());
String subject = "ufoOrder-" + tradeNo;
bizJson.put("subject", subject);
bizJson.put("body", subject);
bizJson.put("out_trade_no", tradeNo);
params.put("biz_content", bizJson.toJSONString());
String preSignStr = getOpenApiSignString(params, false);
String sign = helper().signWithRsa(preSignStr,AlipayConfig.input_charset);
params.put("sign", URLEncoder.encode(sign));
// 对biz_content进行encode
String encodeUrl = URLEncoder.encode(bizJson.toJSONString());
params.put("biz_content", encodeUrl);
return params;
}
/**
* 花呗分期payOrderCode记录
* @return
*/
public boolean recordOrdersPayHbfq(String outTradeNo, String tradeNo, int uid) {
logger.info("recordOrdersPayHbfq outTradeNo: {}, tradeNo: {}, uid: {}", outTradeNo, tradeNo, uid);
OrdersPayHbfq record = new OrdersPayHbfq();
record.setFqTradeNo(outTradeNo);
record.setPayOrderCode(tradeNo);
record.setUid(uid);
try {
ordersPayHbfqDao.insertOnDuplicateUpdate(record);
return true;
} catch (Exception e) {
logger.warn("recordOrdersPayHbfq failed, req: {}, ex: ", record, e);
}
return false;
}
private JSONObject getHbfqParam(int fqNums) {
JSONObject json = new JSONObject();
json.put("hb_fq_num", String.valueOf(fqNums));
json.put("hb_fq_seller_percent", "0"); //用户承担手续费
return json;
}
/**
* 获取openapi支付宝待签名字符串
* @param paramsMap
* @return
*/
public static String getOpenApiSignString(Map<String, String> paramsMap, boolean ignoreSignType) {
if(null == paramsMap) {
return "";
}
//使用SortedMap将参数按名称的字典序排队,且滤去签名字段、空值字段
SortedMap<String, String> sortMap = new TreeMap<String, String>();
for(Map.Entry<String, String> entry : paramsMap.entrySet()) {
if("sign".equalsIgnoreCase(entry.getKey())) {
continue;
}
if (ignoreSignType && "sign_type".equalsIgnoreCase(entry.getKey())){
continue;
}
if(StringUtils.isEmpty(entry.getValue())) {
continue;
}
sortMap.put(entry.getKey(), entry.getValue());
}
return getUrlString(sortMap);
}
public PaymentData getPaymentData(Map<String, String> params) {
PaymentData payData = new PaymentData();
try {
String outTradeNo = params.get("out_trade_no");
payData.setOutTradeNo(outTradeNo);
// 花呗分期,获取订单号
OrdersPayHbfq hbfqRecord = ordersPayHbfqDao.selectByPrimaryKey(outTradeNo);
if (hbfqRecord != null) {
outTradeNo = hbfqRecord.getPayOrderCode();
payData.setOrderCode(outTradeNo);
} else {
OutTradeNoMeta outTradeNoMeta = OutTradeNoMeta.parse(outTradeNo);
payData.setOrderCode(outTradeNoMeta.getOrderCode());
payData.setPayLevel(outTradeNoMeta.getPayLevel());
}
payData.setTotalFee(Double.parseDouble(params.get("total_fee") != null ? params.get("total_fee") : params.get("total_amount")));
payData.setBankCode("");
payData.setBankName("");
payData.setPaymentTime(params.get("gmt_payment"));
payData.setCallbackTime(params.get("notify_time"));
payData.setTradeNo(params.get("trade_no")); //支付宝交易号
payData.setBankBillNo("");
payData.setMchId(params.get("seller_id"));
payData.setBuyerId(params.get("buyer_id"));
} catch (Exception e) {
logger.error("[{}] get payment data error: {}", params.get("out_trade_no"), e.getMessage());
throw new ServiceException(ServiceError.ORDER_PAYMENT_IS_EMPTY);
}
return payData;
}
public TransferData getTransferData(String data) {
if (StringUtils.isBlank(data)) {
return null;
}
if (data.endsWith("|")) {
data = data.substring(0, data.length() - 1);
}
List<String> dataList = Splitter.on("^").splitToList(data);
if (dataList.size() != 8) {
logger.error("[{}] getTransferData 支付宝回调数据字段个数有误", data);
return null;
}
// 1流水号^2收款方账号^3收款账号姓名^4付款金额^5成功标识(S)^6成功原因(null)^7支付宝内部流水号^8完成时间
try {
TransferData transferData = new TransferData();
String[] transferIdAndTradeBillId = dataList.get(0).split("_");
transferData.setTransferId(new Integer(transferIdAndTradeBillId[0]));
transferData.setTradeBillsId(new Integer(transferIdAndTradeBillId[1]));
transferData.setReceiveAccount(dataList.get(1));
transferData.setReceiveUserName(dataList.get(2));
transferData.setMoney(dataList.get(3));
transferData.setStatus(dataList.get(4));
transferData.setMessage(dataList.get(5));
transferData.setAlipayTradeId(dataList.get(6));
transferData.setTime(dataList.get(7));
return transferData;
} catch (NumberFormatException e) {
logger.error("[{}] getTransferData 支付宝回调数据格式有误", data);
return null;
}
}
public boolean notifyVerify(Map<String, String> paramsMap) {
return signVerify(paramsMap)
&& checkAtn(paramsMap.get("out_trade_no"), paramsMap.get("notify_id"));
}
public boolean notifyVerifyMApi(Map<String, String> params) {
String preSignStr = getSignString(params);
return helper().verifyWithMapiRsa(preSignStr,AlipayConfig.input_charset,params.get("sign"));
}
/**
* OpenApi退款
* @param refundBo
* @return
*/
public PayRefundBo refundOpenApi(PayRefundBo refundBo) {
logger.info("enter AliPayRefunder to refund tradeNo {}, amount {}", refundBo.getRefundOrderCode(), refundBo.getAmount());
Map<String, String> refundParams = buildOpenApiRefundParams(refundBo.getPayOrderCode(), refundBo.getRefundOrderCode(), refundBo.getAmount());
String respTxt = sendOpenApiRequest(String.valueOf(refundBo.getPayOrderCode()), refundParams, alipayConfig.openapiUrl());
//logger.info("refund response for alipay: {}", responseText);
PayRefundBo bo = refundOpenApiConvert(respTxt, refundBo);
logger.info("exit AliPayRefunder refund, refundStatus: {}, refundMsg", bo.getRefundStatus(), bo.getRefundMsg());
return bo;
}
/**
* 将xml转换为对象
*
* @return
*/
public PayRefundBo refundOpenApiConvert(String respText, PayRefundBo bo) {
if(StringUtils.isEmpty(respText)) {
bo.setRefundStatus(RefundContant.PAYMENT_REFUND_RESULTCODE_REQERR);
bo.setRefundMsg("退款申请请求失败");
return bo;
}
JSONObject respJson = JSON.parseObject(respText);
JSONObject tradeJson = respJson.getJSONObject("alipay_trade_refund_response");
if(tradeJson == null) {
bo.setRefundStatus(RefundContant.PAYMENT_REFUND_RESULTCODE_REQERR);
bo.setRefundMsg("退款申请请求失败");
return bo;
}
if(!AlipayConfig.OPENAPI_BIZ_SUCCESS.equals(tradeJson.getString("code"))) {
bo.setRefundStatus(RefundContant.PAYMENT_REFUND_RESULTCODE_REQERR);
String sub_code = tradeJson.getString("sub_code");
if ("ACQ.TRADE_HAS_FINISHED".equals(sub_code)) {
bo.setRefundMsg("退款申请失败:该交易已完结,不允许进行退款");
} else if ("ACQ.SELLER_BALANCE_NOT_ENOUGH".equals(sub_code)) {
bo.setRefundMsg("退款申请失败:商户支付宝账户余额不足,充值后重新发起退款即可 ");
} else {
bo.setRefundMsg("退款申请失败:" + sub_code);
}
return bo;
}
bo.setSerialNo(tradeJson.getString("trade_no"));
bo.setRefundStatus(RefundContant.PAYMENT_REFUND_RESULTCODE_SUCCESS);
bo.setRefundMsg("退款申请成功");
return bo;
}
/**
* openapi退款参数
* @param tradeNo
* @param refundNo
* @param amount
* @return
*/
private Map<String, String> buildOpenApiRefundParams(String tradeNo, String refundNo, double amount) {
Map<String, String> params = new HashMap<String, String>();
params.put("app_id", getAppId());
params.put("method", "alipay.trade.refund");
params.put("charset", AlipayConfig.input_charset);
params.put("sign_type", "RSA");
params.put("timestamp", DateUtil.getCurrentTime());
params.put("version", "1.0");
JSONObject bizJson = new JSONObject();
bizJson.put("out_trade_no", tradeNo);
bizJson.put("refund_amount", amount);
bizJson.put("out_request_no", refundNo);
params.put("biz_content", bizJson.toJSONString());
String preSignStr = getOpenApiSignString(params, false);
params.put("sign", helper().signWithRsa(preSignStr,AlipayConfig.input_charset));
return params;
}
/**
* 生成RSA签名
* @param paramsMap
* @return
*/
public String rsaSign(Map<String, String> paramsMap) {
String preSignStr = getSignString(paramsMap);
return helper().signWithRsa(preSignStr,AlipayConfig.input_charset);
}
/**
* 生成RSA签名
* @param preSignStr
* @return
*/
public String rsaSign(String preSignStr) {
return helper().signWithRsa(preSignStr,AlipayConfig.input_charset);
}
/**
* 支付宝回调签名验证
* @param paramsMap
* @return
*/
public boolean signVerify(Map<String, String> paramsMap) {
if (null == paramsMap) {
return false;
}
boolean result = false;
String signType = paramsMap.get("sign_type");
if ("RSA".equals(signType) || StringUtils.equalsIgnoreCase("md5", signType)) {
result = verify(paramsMap);
} else {
logger.warn("[{}] notification sign check: not support sign type {}", paramsMap.get("out_trade_no"), signType);
}
logger.info("[{}] notification sign check: {}", paramsMap.get("out_trade_no"), result);
return result;
}
/**
* 获取支付宝回调待签名字符串
* @param paramsMap
* @return
*/
private String getSignString(Map<String, String> paramsMap) {
return getSignString(paramsMap, StandardCharsets.UTF_8);
}
/**
* 获取支付宝回调待签名字符串
* @param paramsMap
* @return
*/
private String getSignString(Map<String, String> paramsMap, Charset charset) {
if(null == paramsMap) {
return "";
}
//使用SortedMap将参数按名称的字典序排队,且滤去签名字段、空值字段
SortedMap<String, String> sortMap = new TreeMap<String, String>();
for(Map.Entry<String, String> entry : paramsMap.entrySet()) {
if("sign".equalsIgnoreCase(entry.getKey())
|| "sign_type".equalsIgnoreCase(entry.getKey())) {
continue;
}
if(StringUtils.isEmpty(entry.getValue())) {
continue;
}
sortMap.put(entry.getKey(), new String(entry.getValue().getBytes(charset)));
}
return getUrlString(sortMap);
}
/**
* 验证RSA签名
* @param paramsMap
* @return
*/
public boolean verify(Map<String, String> paramsMap) {
String preSignStr = getSignString(paramsMap);
return helper().verify(preSignStr,AlipayConfig.input_charset,paramsMap.get("sign"));
}
/**
* 将请求参数转化为请求URL
* @param paramsMap
* @return
*/
private static String getUrlString(Map<String, String> paramsMap) {
if(null == paramsMap)
return "";
//拼接键值对
int i = 0;
StringBuilder strBuilder = new StringBuilder();
for(Map.Entry<String, String> entry : paramsMap.entrySet()){
strBuilder.append(entry.getKey()).append("=").append(entry.getValue());
if(i++ < paramsMap.size() - 1) {
strBuilder.append("&");
}
}
return strBuilder.toString();
}
/**
* 验证ATN有效性
* @param outTradeNo
* @param notifyId
* @return
*/
public boolean checkAtn(String outTradeNo, String notifyId) {
if(StringUtils.isEmpty(notifyId))
return false;
String result = obtainAtn(outTradeNo, notifyId);
//除非支付系统明确返回false,其余情况全部放通
if("false".equals(result)) {
logger.error("[{}] Atn check failed", outTradeNo);
return false;
}
return true;
}
/**
* 获取支付宝ATN结果
* @param outTradeNo
* @param notifyId
* @return
*/
private String obtainAtn(String outTradeNo, String notifyId) {
String verify_url = alipayConfig.mapiUrl() + "?service=notify_verify&partner=" + getPartnerId() + "&notify_id=" + notifyId;
String response = "";
logger.info("[{}] obtain atn begin, check url: {}", outTradeNo, verify_url);
try {
response = httpClient.get(verify_url);
} catch (Exception e) {
logger.error("[{}] obtain atn failed: {}", outTradeNo, e.getMessage());
}
logger.info("[{}] obtain atn end, result: {}", outTradeNo, response);
return response;
}
/**
* 支付宝APP支付信息构造器
* @author yoho
*
*/
public class AliMobliePayInfoBuilder {
StringBuilder payStrBuilder = new StringBuilder();
//支付宝支付接口"mobile.securitypay.pay"为老接口,签名支持无序
private Map<String, String> paramsMap = new HashMap<>();
public AliMobliePayInfoBuilder addParam(String key, String value) {
paramsMap.put(key, value);
return this;
}
public String buildSign() {
int count = 0;
for(Map.Entry<String, String> entry : paramsMap.entrySet()) {
if(count > 0)
payStrBuilder.append("&");
payStrBuilder.append(entry.getKey()).append("=").append("\"").append(entry.getValue()).append("\"");
count++;
}
String sign = "";
try {
sign = URLEncoder.encode(rsaSign(payStrBuilder.toString()), AlipayConfig.input_charset);
} catch (Exception e) {
logger.error("alipay sign failed: {}", e);
throw new ServiceException(ServiceError.ORDER_PAY_NOT_ALLOW);
}
appendParam("sign", sign);
appendParam("sign_type", AlipayConfig.sign_type);
return payStrBuilder.toString();
}
private void appendParam(String key, String value) {
payStrBuilder.append("&").append(key).append("=").append("\"").append(value).append("\"");
}
}
private static String formatErrorDesc(AlipayBatchTransferErrorEnum abtee, String transferOrderCode) {
String desc = abtee.getDesc();
if (abtee.equals(AlipayBatchTransferErrorEnum.TRANS_NO_NOT_UNIQUE)) {
desc = String.format(desc, transferOrderCode);
}
return desc;
}
public PayQueryBo transferQuery(String buyerOrderCode) {
JSONObject result = transferQueryAsOriginalResult(buyerOrderCode);
// 查询结果转换成 共通的对象
PayQueryBo payQueryBo = new PayQueryBo();
if ("10000".equals(result.getString("code")) && "SUCCESS".equals(result.getString("status"))) {
payQueryBo.setPayStatus(true);
} else {
payQueryBo.setPayStatus(false);
}
return payQueryBo;
}
public JSONObject transferQueryAsOriginalResult(String buyerOrderCode) {
String tradeNo = buyerOrderCode;
Map<String, String> queryParams = buildAlipayTransferQueryParams(tradeNo);
String respTxt = sendOpenApiRequest(tradeNo, queryParams, alipayConfig.openapiUrl());
JSONObject result = JSON.parseObject(respTxt).getJSONObject("alipay_fund_trans_order_query_response");
logger.info("查询转账{}结果为:{}", tradeNo, result);
return result;
}
/**
* 发送预下单请求
*
* @param orderCode
* @param paramMap
* @return
*/
public String sendOpenApiRequest(String orderCode, Map<String, String> paramMap, String url) {
logger.info("[{}] send openapi request: {}, {}", orderCode, paramMap, url);
String response = "";
try {
response = httpClient.postFormData(url, paramMap);
} catch (Exception e) {
logger.error("[{}] trade openapi send failed: {}", orderCode, e.getMessage());
return response;
}
logger.info("[{}] trade openapi resp: {}", orderCode, response);
return response;
}
private String sendOpenApiRequestWithException(String orderCode, Map<String, String> paramMap) throws Exception {
logger.info("[{}] send openapi request: {}", orderCode, paramMap);
String response = httpClient.postFormData(alipayConfig.openapiUrl(), paramMap);
logger.info("[{}] trade openapi resp: {}", orderCode, response);
return response;
}
private String sendMApiRequestWithException(String orderCode, Map<String, String> paramMap) throws Exception {
logger.info("[{}] send openapi request: {}", orderCode, paramMap);
StringBuilder paramStr = new StringBuilder();
for (Map.Entry<String, String> entry : paramMap.entrySet()) {
paramStr.append("&").append(entry.getKey()).append("=").append(URLEncoder.encode(entry.getValue(), AlipayConfig.input_charset));
}
String url = alipayConfig.mapiUrl() + "?" + paramStr.substring(1);
logger.info("[{}] send openapi requestUrl is: {}", orderCode, url);
String response = httpClient.get(url);
logger.info("[{}] trade openapi resp: {}", orderCode, response);
return response;
}
/**
* openapi支付查询
*
* @return
*/
private Map<String, String> buildOpenApiQueryParams(String orderCode) {
Map<String, String> params = new HashMap<String, String>();
params.put("app_id", getAppId());
params.put("method", "alipay.trade.query");
params.put("charset", AlipayConfig.input_charset);
params.put("sign_type", "RSA");
params.put("timestamp", DateUtil.getCurrentTime());
params.put("version", "1.0");
JSONObject bizJson = new JSONObject();
bizJson.put("out_trade_no", orderCode);
params.put("biz_content", bizJson.toJSONString());
String preSignStr = getOpenApiSignString(params, false);
params.put("sign", helper().signWithRsa(preSignStr, AlipayConfig.input_charset));
return params;
}
/**
* openapi支付查询
*
* @return
*/
private Map<String, String> buildAlipayTransferQueryParams(String orderCode) {
Map<String, String> params = new HashMap<String, String>();
params.put("app_id", getAppId());
params.put("method", "alipay.fund.trans.order.query");
params.put("charset", AlipayConfig.input_charset);
params.put("sign_type", "RSA");
params.put("timestamp", DateUtil.getCurrentTime());
params.put("version", "1.0");
JSONObject bizJson = new JSONObject();
bizJson.put("out_biz_no", orderCode);
params.put("biz_content", bizJson.toJSONString());
String preSignStr = getOpenApiSignString(params, false);
params.put("sign", helper().signWithRsa(preSignStr, AlipayConfig.input_charset));
return params;
}
/**
* 单笔转账
*
* @return
*/
private Map<String, String> buildTransferParams(String transferOrderCode, String alipayUid, String alipayAccount, BigDecimal transferAmount) {
Map<String, String> params = new HashMap<String, String>();
params.put("app_id", getAppId());
params.put("method", "alipay.fund.trans.toaccount.transfer");
params.put("charset", AlipayConfig.input_charset);
params.put("sign_type", "RSA");
params.put("timestamp", DateUtil.getCurrentTime());
params.put("version", "1.0");
JSONObject bizJson = new JSONObject();
bizJson.put("out_biz_no", transferOrderCode); // 商户转账唯一订单号
if (StringUtils.isNotBlank(alipayUid)) {
bizJson.put("payee_type", "ALIPAY_USERID");
bizJson.put("payee_account", alipayUid); // 支付宝uid
} else {
bizJson.put("payee_type", "ALIPAY_LOGONID");
bizJson.put("payee_account", alipayAccount); // 支付宝账号
}
bizJson.put("amount", transferAmount.toString());
params.put("biz_content", bizJson.toJSONString());
String preSignStr = getOpenApiSignString(params, false);
params.put("sign", helper().signWithRsa(preSignStr, AlipayConfig.input_charset));
return params;
}
/**
* 单笔转账
* 支持千万/日 转账
* 批量付款到支付宝账户
* https://docs.open.alipay.com/64/104804/
*
* @return
*/
private Map<String, String> buildTransferParamsWhenExceedMillion(String transferOrderCode, String businessId, String alipayUid, String alipayAccount, String userName, BigDecimal transferAmount) {
Map<String, String> params = new HashMap<String, String>();
params.put("service", "batch_trans_notify_no_pwd");
params.put("partner", getPartnerId());
params.put("_input_charset", AlipayConfig.input_charset);
params.put("notify_url", transferNotifyURL);
params.put("account_name", getAccountUserName());
params.put("pay_date", new SimpleDateFormat("YYYYMMdd").format(new Date()));
params.put("batch_no", transferOrderCode);
params.put("batch_num", "1");
params.put("batch_fee", transferAmount.toString());
params.put("email", getAccountEmail());
StringBuilder detailAppender = new StringBuilder();
detailAppender.append(businessId).append("^");
if (StringUtils.isNotBlank(alipayUid)) {
// 流水号1^收款方UserId1^付款金额1^备注说明1
params.put("detail_version", "1.1");
detailAppender.append(alipayUid).append("^");
} else {
// 流水号1^收款方账号1^收款账号姓名1^付款金额1^备注说明1
params.put("detail_version", "1.0");
detailAppender.append(alipayAccount).append("^").append(userName).append("^");
}
detailAppender.append(transferAmount).append("^UFO_ORDER");
params.put("detail_data", detailAppender.toString());
String preSignStr = getOpenApiSignString(params, false);
params.put("sign", helper().signWithRsa(preSignStr, AlipayConfig.input_charset));
params.put("sign_type", "RSA");
return params;
}
/**
* app支付结果
*
* @param orderInfo
* @return
*/
protected Map<String, String> buildOpenApiPayParams(OrderInfo orderInfo) {
long orderCode = orderInfo.getOrderCode();
String tradeNo = orderInfo.getOutTradeNo();
BigDecimal amount = orderInfo.getAmount();
int payExpireTime = orderInfo.getPayExpireTime();
int payment = orderInfo.getPayment();
int hbfqNums = orderInfo.getHbfqNums();
int uid = orderInfo.getUid();
//build
Map<String, String> params = new HashMap<String, String>();
params.put("app_id", getAppId());
params.put("method", "alipay.trade.app.pay");
params.put("charset", AlipayConfig.input_charset);
params.put("sign_type", "RSA");
params.put("timestamp", DateUtil.getCurrentTime());
params.put("version", "1.0");
params.put("notify_url", notifyURL);
if (Payment.isHB(payment)) {
params.put("specified_channel", "pcredit"); // 花呗单渠道
} else if (Payment.isHBFQ(payment)) {
HbfqEnum hbfqEnum = HbfqEnum.getByfqTerms(hbfqNums);
if (hbfqEnum == null) {
logger.warn("fqNums from req invalid, fqNums: {}, orderCode: {}", hbfqNums, tradeNo);
throw new ServiceException(ServiceError.ORDER_FQNUM_INVALID);
}
String outTradeNo = tradeNo + hbfqEnum.getTradeNoPostfix();
params.put("out_trade_no", outTradeNo);
params.put("extend_params", getHbfqParam(hbfqEnum.getFqTerms()).toJSONString());
// 记录分期
recordOrdersPayHbfq(outTradeNo, tradeNo, uid);
tradeNo = outTradeNo;
}
if (paymentSupportService.isForbidVirtualPayChannel(orderCode)) {
params.put("disable_pay_channels", "credit_group"); // 禁用信用卡
}
JSONObject bizJson = new JSONObject(true);
bizJson.put("timeout_express", payExpireTime + "m"); //该订单允许的最晚付款时间
bizJson.put("seller_id", "");
bizJson.put("product_code", "QUICK_MSECURITY_PAY");
bizJson.put("total_amount", amount.toPlainString());
String subject = "ufoOrder-" + tradeNo;
bizJson.put("subject", subject);
bizJson.put("body", subject);
bizJson.put("out_trade_no", tradeNo);
params.put("biz_content", bizJson.toJSONString());
String preSignStr = getOpenApiSignString(params, false);
String sign = helper().signWithRsa(preSignStr, AlipayConfig.input_charset);
params.put("sign", URLEncoder.encode(sign));
// 对biz_content进行encode
String encodeUrl = URLEncoder.encode(bizJson.toJSONString());
params.put("biz_content", encodeUrl);
return params;
}
/**
* 花呗分期payOrderCode记录
*
* @return
*/
public boolean recordOrdersPayHbfq(String outTradeNo, String tradeNo, int uid) {
logger.info("recordOrdersPayHbfq outTradeNo: {}, tradeNo: {}, uid: {}", outTradeNo, tradeNo, uid);
OrdersPayHbfq record = new OrdersPayHbfq();
record.setFqTradeNo(outTradeNo);
record.setPayOrderCode(tradeNo);
record.setUid(uid);
try {
ordersPayHbfqDao.insertOnDuplicateUpdate(record);
return true;
} catch (Exception e) {
logger.warn("recordOrdersPayHbfq failed, req: {}, ex: ", record, e);
}
return false;
}
private JSONObject getHbfqParam(int fqNums) {
JSONObject json = new JSONObject();
json.put("hb_fq_num", String.valueOf(fqNums));
json.put("hb_fq_seller_percent", "0"); //用户承担手续费
return json;
}
/**
* 获取openapi支付宝待签名字符串
*
* @param paramsMap
* @return
*/
public static String getOpenApiSignString(Map<String, String> paramsMap, boolean ignoreSignType) {
if (null == paramsMap) {
return "";
}
//使用SortedMap将参数按名称的字典序排队,且滤去签名字段、空值字段
SortedMap<String, String> sortMap = new TreeMap<String, String>();
for (Map.Entry<String, String> entry : paramsMap.entrySet()) {
if ("sign".equalsIgnoreCase(entry.getKey())) {
continue;
}
if (ignoreSignType && "sign_type".equalsIgnoreCase(entry.getKey())) {
continue;
}
if (StringUtils.isEmpty(entry.getValue())) {
continue;
}
sortMap.put(entry.getKey(), entry.getValue());
}
return getUrlString(sortMap);
}
public PaymentData getPaymentData(Map<String, String> params) {
PaymentData payData = new PaymentData();
try {
String outTradeNo = params.get("out_trade_no");
payData.setOutTradeNo(outTradeNo);
// 花呗分期,获取订单号
OrdersPayHbfq hbfqRecord = ordersPayHbfqDao.selectByPrimaryKey(outTradeNo);
if (hbfqRecord != null) {
outTradeNo = hbfqRecord.getPayOrderCode();
payData.setOrderCode(outTradeNo);
} else {
OutTradeNoMeta outTradeNoMeta = OutTradeNoMeta.parse(outTradeNo);
payData.setOrderCode(outTradeNoMeta.getOrderCode());
payData.setPayLevel(outTradeNoMeta.getPayLevel());
}
payData.setTotalFee(Double.parseDouble(params.get("total_fee") != null ? params.get("total_fee") : params.get("total_amount")));
payData.setBankCode("");
payData.setBankName("");
payData.setPaymentTime(params.get("gmt_payment"));
payData.setCallbackTime(params.get("notify_time"));
payData.setTradeNo(params.get("trade_no")); //支付宝交易号
payData.setBankBillNo("");
payData.setMchId(params.get("seller_id"));
payData.setBuyerId(params.get("buyer_id"));
} catch (Exception e) {
logger.error("[{}] get payment data error: {}", params.get("out_trade_no"), e.getMessage());
throw new ServiceException(ServiceError.ORDER_PAYMENT_IS_EMPTY);
}
return payData;
}
public TransferData getTransferData(String data) {
if (StringUtils.isBlank(data)) {
return null;
}
if (data.endsWith("|")) {
data = data.substring(0, data.length() - 1);
}
List<String> dataList = Splitter.on("^").splitToList(data);
if (dataList.size() != 8) {
logger.error("[{}] getTransferData 支付宝回调数据字段个数有误", data);
return null;
}
// 1流水号^2收款方账号^3收款账号姓名^4付款金额^5成功标识(S)^6成功原因(null)^7支付宝内部流水号^8完成时间
try {
TransferData transferData = new TransferData();
String[] transferIdAndTradeBillId = dataList.get(0).split("_");
transferData.setTransferId(new Integer(transferIdAndTradeBillId[0]));
transferData.setTradeBillsId(new Integer(transferIdAndTradeBillId[1]));
transferData.setReceiveAccount(dataList.get(1));
transferData.setReceiveUserName(dataList.get(2));
transferData.setMoney(dataList.get(3));
transferData.setStatus(dataList.get(4));
transferData.setMessage(dataList.get(5));
transferData.setAlipayTradeId(dataList.get(6));
transferData.setTime(dataList.get(7));
return transferData;
} catch (NumberFormatException e) {
logger.error("[{}] getTransferData 支付宝回调数据格式有误", data);
return null;
}
}
public boolean notifyVerify(Map<String, String> paramsMap) {
return signVerify(paramsMap)
&& checkAtn(paramsMap.get("out_trade_no"), paramsMap.get("notify_id"));
}
public boolean notifyVerifyMApi(Map<String, String> params) {
String preSignStr = getSignString(params);
return helper().verifyWithMapiRsa(preSignStr, AlipayConfig.input_charset, params.get("sign"));
}
/**
* OpenApi退款
*
* @param refundBo
* @return
*/
public PayRefundBo refundOpenApi(PayRefundBo refundBo) {
logger.info("enter AliPayRefunder to refund tradeNo {}, amount {}", refundBo.getRefundOrderCode(), refundBo.getAmount());
Map<String, String> refundParams = buildOpenApiRefundParams(refundBo.getPayOrderCode(), refundBo.getRefundOrderCode(), refundBo.getAmount());
String respTxt = sendOpenApiRequest(String.valueOf(refundBo.getPayOrderCode()), refundParams, alipayConfig.openapiUrl());
//logger.info("refund response for alipay: {}", responseText);
PayRefundBo bo = refundOpenApiConvert(respTxt, refundBo);
logger.info("exit AliPayRefunder refund, refundStatus: {}, refundMsg", bo.getRefundStatus(), bo.getRefundMsg());
return bo;
}
/**
* 将xml转换为对象
*
* @return
*/
public PayRefundBo refundOpenApiConvert(String respText, PayRefundBo bo) {
if (StringUtils.isEmpty(respText)) {
bo.setRefundStatus(RefundContant.PAYMENT_REFUND_RESULTCODE_REQERR);
bo.setRefundMsg("退款申请请求失败");
return bo;
}
JSONObject respJson = JSON.parseObject(respText);
JSONObject tradeJson = respJson.getJSONObject("alipay_trade_refund_response");
if (tradeJson == null) {
bo.setRefundStatus(RefundContant.PAYMENT_REFUND_RESULTCODE_REQERR);
bo.setRefundMsg("退款申请请求失败");
return bo;
}
if (!AlipayConfig.OPENAPI_BIZ_SUCCESS.equals(tradeJson.getString("code"))) {
bo.setRefundStatus(RefundContant.PAYMENT_REFUND_RESULTCODE_REQERR);
String sub_code = tradeJson.getString("sub_code");
if ("ACQ.TRADE_HAS_FINISHED".equals(sub_code)) {
bo.setRefundMsg("退款申请失败:该交易已完结,不允许进行退款");
} else if ("ACQ.SELLER_BALANCE_NOT_ENOUGH".equals(sub_code)) {
bo.setRefundMsg("退款申请失败:商户支付宝账户余额不足,充值后重新发起退款即可 ");
} else {
bo.setRefundMsg("退款申请失败:" + sub_code);
}
return bo;
}
bo.setSerialNo(tradeJson.getString("trade_no"));
bo.setRefundStatus(RefundContant.PAYMENT_REFUND_RESULTCODE_SUCCESS);
bo.setRefundMsg("退款申请成功");
return bo;
}
/**
* openapi退款参数
*
* @param tradeNo
* @param refundNo
* @param amount
* @return
*/
private Map<String, String> buildOpenApiRefundParams(String tradeNo, String refundNo, double amount) {
Map<String, String> params = new HashMap<String, String>();
params.put("app_id", getAppId());
params.put("method", "alipay.trade.refund");
params.put("charset", AlipayConfig.input_charset);
params.put("sign_type", "RSA");
params.put("timestamp", DateUtil.getCurrentTime());
params.put("version", "1.0");
JSONObject bizJson = new JSONObject();
bizJson.put("out_trade_no", tradeNo);
bizJson.put("refund_amount", amount);
bizJson.put("out_request_no", refundNo);
params.put("biz_content", bizJson.toJSONString());
String preSignStr = getOpenApiSignString(params, false);
params.put("sign", helper().signWithRsa(preSignStr, AlipayConfig.input_charset));
return params;
}
/**
* 生成RSA签名
*
* @param paramsMap
* @return
*/
public String rsaSign(Map<String, String> paramsMap) {
String preSignStr = getSignString(paramsMap);
return helper().signWithRsa(preSignStr, AlipayConfig.input_charset);
}
/**
* 生成RSA签名
*
* @param preSignStr
* @return
*/
public String rsaSign(String preSignStr) {
return helper().signWithRsa(preSignStr, AlipayConfig.input_charset);
}
/**
* 支付宝回调签名验证
*
* @param paramsMap
* @return
*/
public boolean signVerify(Map<String, String> paramsMap) {
if (null == paramsMap) {
return false;
}
boolean result = false;
String signType = paramsMap.get("sign_type");
if ("RSA".equals(signType) || StringUtils.equalsIgnoreCase("md5", signType)) {
result = verify(paramsMap);
} else {
logger.warn("[{}] notification sign check: not support sign type {}", paramsMap.get("out_trade_no"), signType);
}
logger.info("[{}] notification sign check: {}", paramsMap.get("out_trade_no"), result);
return result;
}
/**
* 获取支付宝回调待签名字符串
*
* @param paramsMap
* @return
*/
private String getSignString(Map<String, String> paramsMap) {
return getSignString(paramsMap, StandardCharsets.UTF_8);
}
/**
* 获取支付宝回调待签名字符串
*
* @param paramsMap
* @return
*/
private String getSignString(Map<String, String> paramsMap, Charset charset) {
if (null == paramsMap) {
return "";
}
//使用SortedMap将参数按名称的字典序排队,且滤去签名字段、空值字段
SortedMap<String, String> sortMap = new TreeMap<String, String>();
for (Map.Entry<String, String> entry : paramsMap.entrySet()) {
if ("sign".equalsIgnoreCase(entry.getKey())
|| "sign_type".equalsIgnoreCase(entry.getKey())) {
continue;
}
if (StringUtils.isEmpty(entry.getValue())) {
continue;
}
sortMap.put(entry.getKey(), new String(entry.getValue().getBytes(charset)));
}
return getUrlString(sortMap);
}
/**
* 验证RSA签名
*
* @param paramsMap
* @return
*/
public boolean verify(Map<String, String> paramsMap) {
String preSignStr = getSignString(paramsMap);
return helper().verify(preSignStr, AlipayConfig.input_charset, paramsMap.get("sign"));
}
/**
* 将请求参数转化为请求URL
*
* @param paramsMap
* @return
*/
private static String getUrlString(Map<String, String> paramsMap) {
if (null == paramsMap)
return "";
//拼接键值对
int i = 0;
StringBuilder strBuilder = new StringBuilder();
for (Map.Entry<String, String> entry : paramsMap.entrySet()) {
strBuilder.append(entry.getKey()).append("=").append(entry.getValue());
if (i++ < paramsMap.size() - 1) {
strBuilder.append("&");
}
}
return strBuilder.toString();
}
/**
* 验证ATN有效性
*
* @param outTradeNo
* @param notifyId
* @return
*/
public boolean checkAtn(String outTradeNo, String notifyId) {
if (StringUtils.isEmpty(notifyId))
return false;
String result = obtainAtn(outTradeNo, notifyId);
//除非支付系统明确返回false,其余情况全部放通
if ("false".equals(result)) {
logger.error("[{}] Atn check failed", outTradeNo);
return false;
}
return true;
}
/**
* 获取支付宝ATN结果
*
* @param outTradeNo
* @param notifyId
* @return
*/
private String obtainAtn(String outTradeNo, String notifyId) {
String verify_url = alipayConfig.mapiUrl() + "?service=notify_verify&partner=" + getPartnerId() + "&notify_id=" + notifyId;
String response = "";
logger.info("[{}] obtain atn begin, check url: {}", outTradeNo, verify_url);
try {
response = httpClient.get(verify_url);
} catch (Exception e) {
logger.error("[{}] obtain atn failed: {}", outTradeNo, e.getMessage());
}
logger.info("[{}] obtain atn end, result: {}", outTradeNo, response);
return response;
}
/**
* 支付宝APP支付信息构造器
*
* @author yoho
*/
public class AliMobliePayInfoBuilder {
StringBuilder payStrBuilder = new StringBuilder();
//支付宝支付接口"mobile.securitypay.pay"为老接口,签名支持无序
private Map<String, String> paramsMap = new HashMap<>();
public AliMobliePayInfoBuilder addParam(String key, String value) {
paramsMap.put(key, value);
return this;
}
public String buildSign() {
int count = 0;
for (Map.Entry<String, String> entry : paramsMap.entrySet()) {
if (count > 0)
payStrBuilder.append("&");
payStrBuilder.append(entry.getKey()).append("=").append("\"").append(entry.getValue()).append("\"");
count++;
}
String sign = "";
try {
sign = URLEncoder.encode(rsaSign(payStrBuilder.toString()), AlipayConfig.input_charset);
} catch (Exception e) {
logger.error("alipay sign failed: {}", e);
throw new ServiceException(ServiceError.ORDER_PAY_NOT_ALLOW);
}
appendParam("sign", sign);
appendParam("sign_type", AlipayConfig.sign_type);
return payStrBuilder.toString();
}
private void appendParam(String key, String value) {
payStrBuilder.append("&").append(key).append("=").append("\"").append(value).append("\"");
}
}
public TtnStatusQueryResponse transferTtnStatusQuery(String orderCode) {
try {
String response = httpClient.get(alipayConfig.mapiUrl() + "?" + buildBtnStatusQueryRequestBody(orderCode));
return TtnStatusQueryHelper.parseTtnStatusQueryResult(response);
} catch (Exception e) {
logger.error("[{}] obtain atn failed: {}", orderCode, e.getMessage());
throw new RuntimeException(e);
}
}
private String buildBtnStatusQueryRequestBody(String transferOrderCode) {
Map<String, String> params = new HashMap<>();
params.put("service", "btn_status_query");
params.put("partner", getPartnerId());
params.put("_input_charset", "utf-8");
params.put("batch_no", transferOrderCode);
params.put("email", "ouyin@yoho.cn");
String preSignStr = getOpenApiSignString(params, false);
String sign = helper().signWithMapiRsa(preSignStr, "utf-8");
params.put("sign", sign);
params.put("sign_type", "RSA");
return getUrlString(params);
}
}
... ...
package com.yohoufo.order.service.pay.alipay;
import com.yohoufo.order.service.pay.alipay.bean.TtnStatusQueryResponse;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Iterator;
public class TtnStatusQueryHelper {
public static TtnStatusQueryResponse parseTtnStatusQueryResult(String xml) {
try {
return parseTtnStatusQueryResult(xml.getBytes("utf-8"));
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException(e);
}
}
public static TtnStatusQueryResponse parseTtnStatusQueryResult(byte[] xml) {
InputStream inputStream = new ByteArrayInputStream(xml);
SAXReader reader = new SAXReader();
Document document;
try {
document = reader.read(inputStream);
} catch (DocumentException e) {
throw new IllegalStateException(e);
}
Element root = document.getRootElement();
Iterator<Element> it = root.elementIterator();
TtnStatusQueryResponse result = new TtnStatusQueryResponse();
while (it.hasNext()) {
Element element = it.next();
String name = element.getName();
if ("is_success".equals(name)) {
result.setSuccess("T".equals(element.getTextTrim()));
} else if ("error".equals(name)) {
result.setError(element.getTextTrim());
} else if ("response".equals(name)) {
Element order = element.element("order");
result.setBatchStatus(order.element("batch_status").getTextTrim());
result.setBtnNum(order.element("btn_num").getTextTrim());
result.setBtnSucSum(order.element("btn_suc_num").getTextTrim());
result.setBtnSum(order.element("btn_sum").getTextTrim());
result.setBtnSucSum(order.element("btn_succ_sum").getTextTrim());
result.parseTransferData(order.element("res_data").getTextTrim());
}
}
return result;
}
}
... ...
package com.yohoufo.order.service.pay.alipay.bean;
import lombok.Data;
import lombok.Value;
import org.apache.commons.lang3.StringUtils;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@Data
public class TtnStatusQueryResponse {
private boolean success;
private String error;
private String batchStatus;
private String btnNum;
private String btnSucNum;
private String btnSucSum;
private String btnSum;
private List<TransferData> transferDataList;
public TtnStatusQueryResponse() {
transferDataList = new ArrayList<>();
}
@Value
public static class TransferData {
private String outTradeNo;
private String status;
private BigDecimal amount;
}
/**
* @param resData eg:1085203_6843597^13688210977^张铭港^28.00^S^^20191220^10078471913498|
*/
public void parseTransferData(String resData) {
if (StringUtils.isEmpty(resData)) {
return;
}
String[] data = resData.split("|");
for (String e : data) {
if (StringUtils.isEmpty(e)) {
continue;
}
String[] fs = e.split("^");
if (fs.length != 8) {
continue;
}
transferDataList.add(new TransferData(fs[0], fs[4], new BigDecimal(fs[3])));
}
}
public boolean isTransferSuccess(String outTradeNo, BigDecimal amount) {
if (success && Objects.equals(batchStatus, "S")) {
return transferDataList.stream().filter(e -> StringUtils.equals(e.outTradeNo, outTradeNo))
.filter(e -> e.amount.compareTo(amount) == 0)
.filter(e -> Objects.equals(e.status, "S"))
.findAny().isPresent();
}
return false;
}
}
... ...