...
|
...
|
@@ -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
|
|
|
* ×tamp=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
|
|
|
* ×tamp=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() + "¬ify_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() + "¬ify_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);
|
|
|
}
|
|
|
|
|
|
} |
...
|
...
|
|