You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					
					
						
							721 lines
						
					
					
						
							31 KiB
						
					
					
				
			
		
		
	
	
							721 lines
						
					
					
						
							31 KiB
						
					
					
				| package com.ynxbd.ali.helper;
 | |
| 
 | |
| import com.alibaba.fastjson.JSONObject;
 | |
| import com.alipay.api.AlipayClient;
 | |
| import com.alipay.api.DefaultAlipayClient;
 | |
| import com.alipay.api.request.AlipayUserInfoShareRequest;
 | |
| import com.alipay.api.response.AlipayUserInfoShareResponse;
 | |
| import com.alipay.easysdk.base.oauth.models.AlipaySystemOauthTokenResponse;
 | |
| import com.alipay.easysdk.factory.Factory;
 | |
| import com.alipay.easysdk.kernel.util.ResponseChecker;
 | |
| import com.alipay.easysdk.marketing.templatemessage.models.AlipayOpenAppMiniTemplatemessageSendResponse;
 | |
| import com.alipay.easysdk.payment.common.models.*;
 | |
| import com.alipay.easysdk.payment.facetoface.models.AlipayTradePayResponse;
 | |
| import com.ynxbd.ali.config.AliConfig;
 | |
| import com.ynxbd.common.bean.User;
 | |
| import com.ynxbd.common.bean.enums.HCardTypeEnum;
 | |
| import com.ynxbd.common.bean.enums.MerchantEnum;
 | |
| import com.ynxbd.common.bean.pay.Bill;
 | |
| import com.ynxbd.common.bean.pay.Order;
 | |
| import com.ynxbd.common.helper.common.*;
 | |
| import com.ynxbd.common.result.ResultEnum;
 | |
| import com.ynxbd.common.result.ServiceException;
 | |
| import com.ynxbd.wx.wxfactory.WxPayHelper;
 | |
| import com.ynxbd.wx.config.WeChatConfig;
 | |
| import lombok.extern.slf4j.Slf4j;
 | |
| import org.apache.commons.lang3.ObjectUtils;
 | |
| import org.apache.commons.lang3.StringUtils;
 | |
| 
 | |
| import java.math.BigDecimal;
 | |
| import java.math.RoundingMode;
 | |
| import java.nio.charset.Charset;
 | |
| import java.util.*;
 | |
| 
 | |
| /**
 | |
|  * @Author wsq
 | |
|  * @Date 2021/2/26 12:52
 | |
|  * @Copyright @ 2020 云南新八达科技有限公司 All rights reserved.
 | |
|  */
 | |
| @Slf4j
 | |
| public class AliHelper {
 | |
|     // 排重
 | |
|     public static final RepeatKeyHelper KEYS = new RepeatKeyHelper(60);
 | |
| 
 | |
| 
 | |
|     public static String getLink(String page, String query) {
 | |
|         if (ObjectUtils.isEmpty(page)) {
 | |
|             return null;
 | |
|         }
 | |
|         String link = "alipays://platformapi/startapp?appId=" + AliConfig.APP_ID + "&page=" + page;
 | |
|         if (!ObjectUtils.isEmpty(query)) {
 | |
|             link = link + query;
 | |
|         }
 | |
|         return link;
 | |
|     }
 | |
| 
 | |
|     public static boolean isAliUser(String openid) {
 | |
|         if (ObjectUtils.isEmpty(openid)) {
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         if (openid.length() < 16) {
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         return "2088".equals(openid.substring(0, 4));
 | |
|     }
 | |
| 
 | |
| 
 | |
|     public static AlipayClient getAliClient() {
 | |
|         return new DefaultAlipayClient(AliConfig.SERVICE_URL, AliConfig.APP_ID, AliConfig.APP_PRIVATE_KEY, "json", "UTF-8", AliConfig.ALI_PUBLIC_KEY, "RSA2");
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * 赋值用户信息
 | |
|      */
 | |
|     public static void copyUserInfo(User user, User userInfo) {
 | |
|         if (user == null || userInfo == null) {
 | |
|             log.info("[支付宝]用户信息赋值失败");
 | |
|             return;
 | |
|         }
 | |
|         user.setAvatar(userInfo.getAvatar());
 | |
|         user.setTrueName(userInfo.getTrueName());
 | |
|         user.setProvince(userInfo.getProvince());
 | |
|         user.setNickName(userInfo.getNickName());
 | |
|         user.setCity(userInfo.getCity());
 | |
|         user.setIdCardNo(userInfo.getIdCardNo());
 | |
|         user.setTel(userInfo.getTel());
 | |
|         user.setGender(userInfo.getGender());
 | |
|         user.setErrMsg(userInfo.getErrMsg());
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * (旧版)获取用户信息
 | |
|      *
 | |
|      * @param accessToken 授权获取到的accessToken
 | |
|      * @return 获取到的用户信息
 | |
|      */
 | |
|     public static User getUserInfo(String openid, String accessToken) {
 | |
|         User user = new User();
 | |
|         try {
 | |
|             AlipayClient alipayClient = getAliClient();
 | |
|             AlipayUserInfoShareRequest request = new AlipayUserInfoShareRequest();
 | |
|             AlipayUserInfoShareResponse response = alipayClient.execute(request, accessToken);
 | |
| 
 | |
|             log.info("【支付宝】openid={}, 获取用户信息:{}, resp={}", openid, (response.isSuccess() ? "成功" : "失败"), JsonHelper.toJsonString(response));
 | |
|             if (response.isSuccess()) {
 | |
|                 user.setAvatar(response.getAvatar());
 | |
|                 user.setTrueName(response.getUserName());
 | |
|                 user.setProvince(response.getProvince());
 | |
|                 user.setNickName(response.getNickName());
 | |
|                 user.setCity(response.getCity());
 | |
|                 user.setIdCardNo(response.getCertNo());
 | |
|                 HCardTypeEnum cardTypeEnum = HCardTypeEnum.toTypeByAliCode(response.getCertType());
 | |
|                 user.setCardTypeEnum(cardTypeEnum);
 | |
| 
 | |
|                 user.setTel(response.getMobile());
 | |
|                 String gender = response.getGender();
 | |
|                 if (!ObjectUtils.isEmpty(gender)) {
 | |
|                     user.setGender("m".equals(gender) ? "男" : "女");
 | |
|                 } else {
 | |
|                     if (HCardTypeEnum._01.equals(cardTypeEnum)) { // 身份证
 | |
|                         user.setGender(IDNumberHelper.getSex(response.getCertNo()));
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 if (ObjectUtils.isEmpty(user.getGender())) {
 | |
|                     user.setGender("男");
 | |
|                 }
 | |
|             } else {
 | |
|                 user.setErrMsg(response.getMsg() + "|" + response.getSubMsg());
 | |
|             }
 | |
|         } catch (Exception e) {
 | |
|             ErrorHelper.println(e);
 | |
|         }
 | |
|         return user;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     /**
 | |
|      * 获取授权访问令牌
 | |
|      */
 | |
|     public static User getToken(String code) {
 | |
|         User user = null;
 | |
|         try {
 | |
|             AliConfig.initConfig();
 | |
|             AlipaySystemOauthTokenResponse response = Factory.Base.OAuth().getToken(code);
 | |
| 
 | |
|             if (ResponseChecker.success(response)) {
 | |
|                 String userId = response.getUserId();
 | |
|                 String accessToken = response.getAccessToken();
 | |
|                 Long expiresIn = response.getExpiresIn();
 | |
|                 Long reExpiresIn = response.getReExpiresIn();
 | |
|                 user = new User();
 | |
|                 user.setOpenid(userId);
 | |
|                 user.setAccessToken(accessToken);
 | |
|                 user.setRefreshToken(response.getRefreshToken());
 | |
|                 user.setExpiresIn(expiresIn);
 | |
|                 user.setTime(new Date());
 | |
|             } else {
 | |
|                 log.info("[支付宝]认证失败,原因:{}, {}", response.msg, response.subMsg);
 | |
|             }
 | |
|         } catch (Exception e) {
 | |
|             ErrorHelper.println(e);
 | |
|         }
 | |
|         return user;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     /**
 | |
|      * 刷新授权访问令牌
 | |
|      *
 | |
|      * @param refreshToken 刷新令牌,上次换取访问令牌时得到,见出参的refresh_token字段
 | |
|      */
 | |
|     public static User refreshToken(String refreshToken) {
 | |
|         try {
 | |
|             AliConfig.initConfig();
 | |
|             AlipaySystemOauthTokenResponse response = Factory.Base.OAuth().refreshToken(refreshToken);
 | |
| 
 | |
|             if (ResponseChecker.success(response)) {
 | |
|                 log.info("【支付宝】刷新授权访问令牌成功");
 | |
|                 refreshToken = response.refreshToken;      // 刷新令牌。通过该令牌可以刷新access_token(20120823ac6ffdsdf2d84e7384bf983531473993)
 | |
|                 String userId = response.userId;           // 支付宝用户的唯一标识。以2088开头的16位数字。(2088102150477652)
 | |
|                 String accessToken = response.accessToken; // 访问令牌。通过该令牌调用需要授权类接口(20120823ac6ffaa4d2d84e7384bf983531473993)
 | |
|                 Long expiresIn = response.expiresIn;       // 访问令牌的有效时间,单位是秒。(3600)
 | |
|                 Long reExpiresIn = response.reExpiresIn;   // 刷新令牌的有效时间,单位是秒。(3600)
 | |
|                 refreshToken = response.getRefreshToken(); // 刷新令牌时间
 | |
| 
 | |
|                 User user = new User();
 | |
|                 user.setOpenid(userId);
 | |
|                 user.setAccessToken(accessToken);
 | |
|                 user.setExpiresIn(expiresIn);
 | |
|                 user.setRefreshToken(refreshToken);
 | |
|                 if (expiresIn != null) {
 | |
|                     Date date = DateHelper.strToDate(DateHelper.getMoveSeconds(DateHelper.getCurDateTime(), expiresIn - 200), DateHelper.DateEnum.yyyy_MM_dd_HH_mm_ss);
 | |
|                     user.setTime(date);
 | |
|                 }
 | |
|                 return user;
 | |
|             } else {
 | |
|                 log.info("【支付宝】刷新授权访问令牌 调用失败,原因:{}, {}", response.msg, response.subMsg);
 | |
|             }
 | |
|         } catch (Exception e) {
 | |
|             ErrorHelper.println(e);
 | |
|         }
 | |
|         return null;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     /**
 | |
|      * 创建订单
 | |
|      *
 | |
|      * @param subject     订单标题
 | |
|      * @param outTradeNo  商户订单号,64个字符以内,可包含字母、数字、下划线,需保证在商户端不重复
 | |
|      * @param totalAmount 订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000]
 | |
|      * @param buyerId     买家的支付宝唯一用户号(2088开头的16位纯数字)
 | |
|      * @param notifyType  回调类型
 | |
|      */
 | |
|     public static JSONObject createOrder(MerchantEnum merchantEnum, String subject, String outTradeNo, String totalAmount, String notifyType, String buyerId) {
 | |
|         log.info("【支付宝】创建交易 subject={}, outTradeNo={}, totalAmount={}, buyerId={}", subject, outTradeNo, totalAmount, buyerId);
 | |
|         if (ObjectUtils.isEmpty(subject)
 | |
|                 || ObjectUtils.isEmpty(outTradeNo)
 | |
|                 || ObjectUtils.isEmpty(totalAmount)
 | |
|                 || ObjectUtils.isEmpty(buyerId)
 | |
|                 || ObjectUtils.isEmpty(notifyType)
 | |
|                 || merchantEnum == null) {
 | |
|             log.info("【支付宝】创建交易,参数缺失");
 | |
|             return null;
 | |
|         }
 | |
| 
 | |
|         try {
 | |
|             AliConfig.initConfig();
 | |
|             Map<String, Object> extend_params = new HashMap<>();
 | |
|             // 返佣-->公司支付宝账号ID
 | |
|             extend_params.put("sys_service_provider_id", AliConfig.ISV_PARTNER_ID);
 | |
| 
 | |
|             AlipayTradeCreateResponse response = Factory.Payment.Common()
 | |
|                     // 调用asyncNotify扩展方法,可以为每次API调用,设置独立的异步通知地址 此处设置的异步通知地址的优先级高于全局Config中配置的异步通知地址
 | |
|                     .asyncNotify(WeChatConfig.getHttpsBaseUrl() + merchantEnum.getNotifyUrl(outTradeNo, notifyType))
 | |
|                     .optional("extend_params", extend_params)
 | |
|                     .create(subject, outTradeNo, totalAmount, buyerId);
 | |
| 
 | |
|             log.info("【支付宝】订单信息:{}", JsonHelper.toJsonString(response));
 | |
| 
 | |
|             if (ResponseChecker.success(response)) {
 | |
|                 JSONObject jsonObj = new JSONObject();
 | |
|                 jsonObj.put("tradeNo", response.getTradeNo());
 | |
|                 jsonObj.put("outTradeNo", response.getOutTradeNo());
 | |
|                 return jsonObj;
 | |
| 
 | |
|             } else {
 | |
|                 log.info("【支付宝】创建交易调用失败,原因:{}, {}", response.getMsg(), response.getSubMsg());
 | |
|             }
 | |
|         } catch (Exception e) {
 | |
|             ErrorHelper.println(e);
 | |
|         }
 | |
|         return null;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     /**
 | |
|      * 创建交易
 | |
|      *
 | |
|      * @param subject     订单标题
 | |
|      * @param outTradeNo  商户订单号,64个字符以内,可包含字母、数字、下划线,需保证在商户端不重复
 | |
|      * @param totalAmount 订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000]
 | |
|      * @param authCode    条码
 | |
|      */
 | |
|     public static Order payMicro(String subject, String outTradeNo, String totalAmount, String authCode) {
 | |
|         Order order = new Order();
 | |
|         if (!AliConfig.IS_PAY_ENABLE) {
 | |
|             order.setErrorMsg("支付宝盒子支付功能已关闭");
 | |
|             return order;
 | |
|         }
 | |
| 
 | |
|         log.info("【支付宝】[盒子支付] subject={}, outTradeNo={}, totalAmount={}, buyerId={}", subject, outTradeNo, totalAmount, authCode);
 | |
|         if (StringUtils.isEmpty(subject)
 | |
|                 || StringUtils.isEmpty(outTradeNo)
 | |
|                 || StringUtils.isEmpty(totalAmount)
 | |
|                 || StringUtils.isEmpty(authCode)) {
 | |
|             order.setErrorMsg("【支付宝】[盒子支付] 参数缺失");
 | |
|             return order;
 | |
|         }
 | |
| 
 | |
|         try {
 | |
|             AliConfig.initConfig();
 | |
|             AlipayTradePayResponse response = Factory.Payment.FaceToFace().pay(subject, outTradeNo, totalAmount, authCode);
 | |
|             log.info("【支付宝】[盒子支付] 订单信息:{}", JsonHelper.toJsonString(response));
 | |
| 
 | |
|             if (!ResponseChecker.success(response)) {
 | |
|                 order.setErrorMsg(response.getSubMsg() + "," + response.getMsg());
 | |
|                 log.info("【支付宝】[盒子支付] 调用失败,原因:{},{}", response.getMsg(), response.getSubMsg());
 | |
|                 return order;
 | |
|             }
 | |
| 
 | |
|             order.setSuccess(true); // 支付成功标识
 | |
|             order.setOpenid(response.buyerUserId);
 | |
|             order.setBankTransNo(response.tradeNo);
 | |
|             order.setOutTradeNo(response.outTradeNo);
 | |
|         } catch (Exception e) {
 | |
|             log.info("【支付宝】[盒子支付]异常 {}", e.getMessage());
 | |
|             order.setErrorMsg("网络异常或当前不支持支付宝");
 | |
|             ErrorHelper.println(e);
 | |
|         }
 | |
|         return order;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     /**
 | |
|      * 查询交易
 | |
|      *
 | |
|      * @param outTradeNo 商户订单号,64个字符以内,可包含字母、数字、下划线,需保证在商户端不重复
 | |
|      */
 | |
|     public static Order queryTransaction(String outTradeNo) {
 | |
|         Order order = new Order();
 | |
|         try {
 | |
|             log.info("【支付宝】查询订单 outTradeNo={}", outTradeNo);
 | |
|             String[] queryOptions = {
 | |
|                     "trade_settle_info"
 | |
|             };
 | |
| 
 | |
|             AliConfig.initConfig();
 | |
|             AlipayTradeQueryResponse response = Factory.Payment.Common()
 | |
|                     .optional("query_options", queryOptions)
 | |
|                     .query(outTradeNo);
 | |
| 
 | |
|             log.info("【支付宝】查询订单:{}", JsonHelper.toJsonString(response));
 | |
| 
 | |
|             if (ResponseChecker.success(response)) {
 | |
|                 String tradeStatus = response.tradeStatus; // TRADE_SUCCESS
 | |
|                 if ("TRADE_SUCCESS".equals(tradeStatus)) {
 | |
|                     order.setOpenid(response.buyerUserId);
 | |
|                     order.setOutTradeNo(outTradeNo);
 | |
|                     order.setInfo(response.getHttpBody());
 | |
|                     order.setBankTransNo(response.tradeNo);
 | |
|                     order.setTotalFee(new BigDecimal(response.totalAmount));
 | |
|                     // 查询成功
 | |
|                     order.setSuccess(true);
 | |
|                     return order;
 | |
|                 } else if ("TRADE_CLOSED".equals(response.tradeStatus)) {
 | |
|                     order.setOpenid(response.buyerUserId);
 | |
|                     order.setOutTradeNo(outTradeNo);
 | |
|                     order.setInfo(response.getHttpBody());
 | |
|                     order.setBankTransNo(response.tradeNo);
 | |
|                     order.setTotalFee(new BigDecimal(response.totalAmount));
 | |
|                     // 查询成功
 | |
|                     order.setSuccess(true);
 | |
|                     order.setRefund(true); // 发生退款
 | |
|                     order.setErrorMsg("交易超时关闭,或支付完成后全额退款");
 | |
|                     return order;
 | |
|                 }
 | |
|             } else {
 | |
|                 log.info("【支付宝】查询订单失败,原因:{}, {}", response.msg, response.subMsg);
 | |
|                 order.setErrorMsg(response.msg + response.subMsg);
 | |
|             }
 | |
|         } catch (Exception e) {
 | |
|             if (e.getMessage() != null && e.getMessage().contains("验签失败")) {
 | |
|                 order.setErrorMsg("[支付宝]订单不存在或验签失败");
 | |
|             } else {
 | |
|                 order.setErrorMsg("[支付宝]查询订单发生异常");
 | |
|                 ErrorHelper.println(e);
 | |
|             }
 | |
|         }
 | |
|         return order;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     /**
 | |
|      * 交易退款查询
 | |
|      *
 | |
|      * @param outTradeNo 商户订单号,64个字符以内,可包含字母、数字、下划线,需保证在商户端不重复
 | |
|      */
 | |
|     public static Bill queryRefund(String outTradeNo, String outRefundNo) throws ServiceException {
 | |
|         if (ObjectUtils.isEmpty(outTradeNo)) {
 | |
|             throw new ServiceException(ResultEnum.PARAM_IS_DEFECT);
 | |
|         }
 | |
| 
 | |
|         try {
 | |
|             log.info("【支付宝】交易退款查询 outTradeNo={}, outRefundNo={}", outTradeNo, outRefundNo);
 | |
| 
 | |
|             String[] queryOptions = {
 | |
|                     "refund_detail_item_list",
 | |
|                     "gmt_refund_pay"
 | |
|             };
 | |
|             AliConfig.initConfig();
 | |
|             AlipayTradeFastpayRefundQueryResponse response = Factory.Payment.Common()
 | |
|                     .optional("query_options", queryOptions)
 | |
|                     .queryRefund(outTradeNo, outRefundNo);
 | |
| 
 | |
|             log.info("【支付宝】交易退款查询:{}", JsonHelper.toJsonString(response));
 | |
| 
 | |
|             if (ResponseChecker.success(response)) {
 | |
|                 if ("REFUND_SUCCESS".equals(response.getRefundStatus())) { // 退款处理成功
 | |
|                     return null;
 | |
|                 }
 | |
|                 if (response.getRefundAmount() == null) {
 | |
|                     return null;
 | |
|                 }
 | |
|                 Bill bill = new Bill();
 | |
|                 bill.setMoney(new BigDecimal(response.getRefundAmount()));
 | |
|                 return bill;
 | |
|             } else {
 | |
|                 throw new ServiceException("【支付宝】查询交易调用失败,原因:" + response.msg + ", " + response.subMsg);
 | |
|             }
 | |
|         } catch (Exception e) {
 | |
|             ErrorHelper.println(e);
 | |
|             throw new ServiceException(ResultEnum.INTERFACE_ALI_INVOKE_ERROR);
 | |
|         }
 | |
|     }
 | |
| 
 | |
| 
 | |
| //    public static void main(String[] args) {
 | |
| //
 | |
| ////        /**  设置参数(全局只需设置一次) **/
 | |
| ////        Factory.setOptions(getOptions());
 | |
| ////        try {
 | |
| ////            /**  发起API调用  **/
 | |
| ////            AlipayTradeFastpayRefundQueryResponse response = Payment.Common()
 | |
| ////                    /** 调用agent扩展方法,设置app_auth_token,完成第三方代调用 **/
 | |
| ////                    //.agent("app_auth_token")
 | |
| ////
 | |
| ////                    /** 调用optional扩展方法,完成可选业务参数(biz_content下的可选字段)的设置 **/
 | |
| ////                    //.optional("trade_no", "4578916541244")
 | |
| ////
 | |
| ////                    /** 入参顺序为:
 | |
| ////                     * out_trade_no -> 商户请求支付时自己系统的订单号,与trade_no同时传入以trade_no为准
 | |
| ////                     * out_request_no -> 请求退款接口时,传入的退款请求号,如果在退款请求时未传入,则该值为创建交易时的商户订单号(out_trade_no)
 | |
| ////                     * **/
 | |
| ////                    .queryRefund("202103172200144", "123123123");
 | |
| ////
 | |
| ////            /** 获取接口调用结果 **/
 | |
| ////            System.out.println(response.getHttpBody());
 | |
| ////
 | |
| ////        } catch (Exception e) {
 | |
| ////            System.err.println("调用遭遇异常,原因:" + e.getMessage());
 | |
| ////            throw new RuntimeException(e.getMessage(), e);
 | |
| ////        }
 | |
| //    }
 | |
| 
 | |
| 
 | |
|     /**
 | |
|      * 交易通知去重
 | |
|      *
 | |
|      * @param outTradeNo 商户订单号,64个字符以内,可包含字母、数字、下划线,需保证在商户端不重复
 | |
|      */
 | |
|     public static Order payNotify(String outTradeNo) {
 | |
|         Order order = new Order();
 | |
|         // 已处理 去重
 | |
|         if (KEYS.isContainsKey(outTradeNo)) {
 | |
|             order.setErrorMsg("【支付宝】[重复请求]下单信息去重 outTradeNo=" + outTradeNo);
 | |
|             return order;
 | |
|         }
 | |
|         return queryTransaction(outTradeNo);
 | |
|     }
 | |
| 
 | |
| 
 | |
|     /**
 | |
|      * 交易退款
 | |
|      *
 | |
|      * @param outTradeNo   交易创建时传入的商户订单号
 | |
|      * @param refundAmount 需要退款的金额,该金额不能大于订单金额,单位为元,支持两位小数
 | |
|      */
 | |
|     public static Order refund(String outTradeNo, String outRefundNo, BigDecimal refundAmount, String refundDesc) {
 | |
|         Order orderRefund = new Order();
 | |
|         if (refundAmount == null) {
 | |
|             orderRefund.setErrorMsg("金额错误不能退费");
 | |
|             return orderRefund;
 | |
|         }
 | |
| 
 | |
|         log.info("【支付宝】交易退款 outTradeNo={}, refundAmount={}, money={}", outTradeNo, refundAmount, refundAmount.setScale(2, RoundingMode.DOWN));
 | |
|         try {
 | |
|             AliConfig.initConfig();
 | |
|             AlipayTradeRefundResponse response = Factory.Payment.Common()
 | |
|                     .optional("out_request_no", outRefundNo)
 | |
|                     .optional("refund_reason", refundDesc)
 | |
|                     .refund(outTradeNo, refundAmount.setScale(2, RoundingMode.DOWN).toString());
 | |
| 
 | |
| 
 | |
|             log.info("【支付宝】退费 {}", JsonHelper.toJsonString(response));
 | |
| //            String tradeNo = response.tradeNo; // 支付宝交易号
 | |
| //            String buyerLogonId = response.buyerLogonId;
 | |
| //            String fundChange = response.fundChange; // 本次退款是否发生了资金变化
 | |
| //            String refundFee = response.refundFee;    // 退款总金额	88.88
 | |
| //            String refundCurrency = response.refundCurrency; // 退款币种信息
 | |
| //            String storeName = response.storeName; // 交易在支付时候的门店名称 例:望湘园联洋店
 | |
| //            String buyerUserId = response.buyerUserId;// 买家在支付宝的用户id 例:2088101117955611
 | |
| 
 | |
| //            List<TradeFundBill> refund_detail_item_list = response.refundDetailItemList; // 退款使用的资金渠道。 只有在签约中指定需要返回资金明细,或者入参的query_options中指定时才返回该字段信息。
 | |
| //            present_refund_buyer_amount String 选填 11 本次退款金额中买家退款金额 88.88
 | |
| //            present_refund_discount_amount String 选填 11 本次退款金额中平台优惠退款金额 88.88
 | |
| //            present_refund_mdiscount_amount String 选填 11 本次退款金额中商家优惠退款金额 88.88
 | |
| //            has_deposit_back
 | |
| 
 | |
|             if (!ResponseChecker.success(response)) {
 | |
|                 log.info("【支付宝】交易退款调用失败,原因:{}, {}", response.msg, response.subMsg);
 | |
|                 orderRefund.setErrorMsg(response.msg + "," + response.subMsg);
 | |
|                 orderRefund.setRefundResult(orderRefund.getErrorMsg());
 | |
|                 return orderRefund;
 | |
|             }
 | |
| 
 | |
|             log.info("【支付宝】退款调用成功,返回:{}, {}", response.msg, response.subMsg);
 | |
|             orderRefund.setSuccess(true); // 退款成功标识
 | |
|             orderRefund.setRefundResult("OK");
 | |
| 
 | |
|         } catch (Exception e) {
 | |
|             orderRefund.setErrorMsg("【支付宝】网络异常或当前不支持支付宝");
 | |
|             String refundResult = orderRefund.getRefundResult();
 | |
|             orderRefund.setRefundResult(StringUtils.isEmpty(refundResult) ? orderRefund.getErrorMsg() : refundResult);
 | |
|             ErrorHelper.println(e);
 | |
|         }
 | |
|         return orderRefund;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     /**
 | |
|      * 关闭交易
 | |
|      *
 | |
|      * @param outTradeNo 交易创建时传入的商户订单号
 | |
|      */
 | |
|     public static boolean close(String outTradeNo) {
 | |
|         log.info("【支付宝】关闭交易, outTradeNo={}", outTradeNo);
 | |
|         try {
 | |
|             AliConfig.initConfig();
 | |
|             AlipayTradeCloseResponse response = Factory.Payment.Common().close(outTradeNo);
 | |
| 
 | |
|             log.info("【支付宝】关闭交易信息: {}", JsonHelper.toJsonString(response));
 | |
| 
 | |
|             if (ResponseChecker.success(response)) {
 | |
|                 String tradeNo = response.tradeNo;
 | |
|                 log.info("【支付宝】关闭交易成功,tradeNo={}", tradeNo);
 | |
|                 return true;
 | |
|             } else {
 | |
|                 log.info("【支付宝】关闭交易调用失败,原因:{}, {}", response.msg, response.subMsg);
 | |
|             }
 | |
|         } catch (Exception e) {
 | |
|             ErrorHelper.println(e);
 | |
|             return false;
 | |
|         }
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     /**
 | |
|      * 撤销交易
 | |
|      *
 | |
|      * @param outTradeNo 交易创建时传入的商户订单号
 | |
|      * @return
 | |
|      */
 | |
|     public static boolean cancel(String outTradeNo) {
 | |
|         log.info("【支付宝】撤销交易, outTradeNo={}", outTradeNo);
 | |
|         try {
 | |
|             AliConfig.initConfig();
 | |
|             AlipayTradeCancelResponse response = Factory.Payment.Common().cancel(outTradeNo);
 | |
| 
 | |
|             log.info("【支付宝】撤销交易信息: {}", JsonHelper.toJsonString(response));
 | |
|             if (ResponseChecker.success(response)) {
 | |
|                 return true;
 | |
|             } else {
 | |
|                 log.info("【支付宝】撤销交易调用失败,原因:{}, {}", response.msg, response.subMsg);
 | |
|             }
 | |
|         } catch (Exception e) {
 | |
|             ErrorHelper.println(e);
 | |
|             return false;
 | |
|         }
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     /**
 | |
|      * 查询对账单下载
 | |
|      *
 | |
|      * @param billType 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单
 | |
|      * @param billDate 账单时间:日账单格式为yyyy-MM-dd,最早可下载2016年1月1日开始的日账单;月账单格式为yyyy-MM,最早可下载2016年1月开始的月账单
 | |
|      * @return
 | |
|      */
 | |
|     public static String downloadBill(String billType, String billDate) {
 | |
|         log.info("【支付宝】查询对账单下载地址, billType={}, billDate={}", billType, billDate);
 | |
|         try {
 | |
|             AliConfig.initConfig();
 | |
|             AlipayDataDataserviceBillDownloadurlQueryResponse response = Factory.Payment.Common()
 | |
|                     .downloadBill(billType, billDate);
 | |
| 
 | |
|             log.info("【支付宝】查询对账单下载响应: {}", JsonHelper.toJsonString(response));
 | |
| 
 | |
|             if (ResponseChecker.success(response)) {
 | |
|                 return response.getBillDownloadUrl();
 | |
|             } else {
 | |
|                 log.info("【支付宝】查询对账单下载地址调用失败,原因:{}, {}", response.msg, response.subMsg);
 | |
|             }
 | |
|         } catch (Exception e) {
 | |
|             ErrorHelper.println(e);
 | |
|             return null;
 | |
|         }
 | |
|         return null;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * 异步通知验签
 | |
|      *
 | |
|      * @param parameters 异步通知中收到的待验签的所有参数
 | |
|      * @return 是否成功
 | |
|      */
 | |
|     public static boolean verifyNotify(Map<String, String> parameters) {
 | |
|         log.info("【支付宝】异步通知验签, parameters={}", parameters);
 | |
|         try {
 | |
|             AliConfig.initConfig();
 | |
|             Boolean flag = Factory.Payment.Common().verifyNotify(parameters);
 | |
|             log.info(flag ? "【支付宝】通知成功" : "【支付宝】异步通知失败");
 | |
| 
 | |
|             return flag;
 | |
|         } catch (Exception e) {
 | |
|             ErrorHelper.println(e);
 | |
|             return false;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * 异步通知验签
 | |
|      *
 | |
|      * @return 是否成功
 | |
|      */
 | |
|     /**
 | |
|      * @param toUserId
 | |
|      * @param fromId         *支付消息模板:需传入用户发生的交易行为的支付宝交易号 trade_no;
 | |
|      *                       *表单提交模板:需传入用户在小程序触发表单提交事件获得的表单号;
 | |
|      *                       *刷脸消息模板:需传入在IOT刷脸后得到的ftoken等,用于信息发送的校验。
 | |
|      *                       说明:订阅消息模板无需传入本参数。
 | |
|      * @param userTemplateId
 | |
|      * @param page
 | |
|      * @param data
 | |
|      * @return
 | |
|      */
 | |
|     public static boolean templateMessage(String toUserId, String fromId, String userTemplateId, String page, String data) {
 | |
|         log.info("【支付宝】模板消息推送");
 | |
|         try {
 | |
|             AliConfig.initConfig();
 | |
|             AlipayOpenAppMiniTemplatemessageSendResponse response = Factory.Marketing.TemplateMessage().send(toUserId, fromId, null, null, null);
 | |
|             if (ResponseChecker.success(response)) {
 | |
|                 return true;
 | |
|             } else {
 | |
|                 log.info("【支付宝】模板消息推送失败,原因:{}, {}", response.msg, response.subMsg);
 | |
|             }
 | |
|         } catch (Exception e) {
 | |
|             ErrorHelper.println(e);
 | |
|             return false;
 | |
|         }
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     public synchronized static List<Bill> queryBill(String billType, String billDate, boolean isCompose) throws ServiceException {
 | |
|         if (!DateHelper.isValidDate(billDate, DateHelper.DateEnum.yyyy_MM_dd)) {
 | |
|             throw new ServiceException(ResultEnum.PARAM_DATE_ERROR);
 | |
|         }
 | |
| 
 | |
|         if (!WxPayHelper.ALL.equals(billType) && !WxPayHelper.REFUND.equals(billType) && !WxPayHelper.SUCCESS.equals(billType)) {
 | |
|             throw new ServiceException(ResultEnum.PARAM_IS_INVALID);
 | |
|         }
 | |
| 
 | |
|         String billDownUrl = downloadBill("trade", billDate);
 | |
|         if (billDownUrl == null) {
 | |
|             throw new ServiceException(ResultEnum.INTERFACE_ALI_INVOKE_ERROR);
 | |
|         }
 | |
| 
 | |
|         List<Bill> bilList = new ArrayList<>();
 | |
| 
 | |
|         String respUrl = billDownUrl.substring(billDownUrl.indexOf("downloadFileName") + 17);
 | |
|         String zipFileName = respUrl.substring(0, billDownUrl.indexOf(".zip")) + ".zip";
 | |
| 
 | |
|         String fileName = zipFileName.substring(0, zipFileName.indexOf(".csv")) + "_业务明细.csv";
 | |
|         String filePath = FileHelper.downLoadZip(billDownUrl, "C:\\Users\\Administrator\\Desktop\\test", zipFileName);
 | |
|         String data = FileHelper.readZip(filePath, fileName, Charset.forName("GBK"), false);
 | |
| 
 | |
|         String[] dataArr = data.split("\\n");
 | |
|         if (dataArr.length == 0) {
 | |
|             return bilList;
 | |
|         }
 | |
| 
 | |
|         Bill billItem;
 | |
|         String money;
 | |
|         String[] titleArr = dataArr[0].split(",");
 | |
|         if (titleArr.length == 0) {
 | |
|             return bilList;
 | |
|         }
 | |
|         for (int i = 5; i < (dataArr.length - 4); i++) {
 | |
|             String[] item = dataArr[i].split(",");
 | |
|             billItem = new Bill();
 | |
|             billItem.setTradeTime(dataHandle(item[5]));
 | |
|             billItem.setOutTradeNo(dataHandle(item[1]));
 | |
|             billItem.setBankTransNo(dataHandle(item[0]));
 | |
|             String tradeState = dataHandle(item[2]);
 | |
|             try {
 | |
|                 money = dataHandle(item[11]);
 | |
|                 if (money != null) {
 | |
|                     billItem.setMoney(new BigDecimal(money));
 | |
|                 }
 | |
|                 if ("退款".equals(tradeState)) { // 退款
 | |
|                     if (WxPayHelper.REFUND.equals(billType) || WxPayHelper.ALL.equals(billType)) {
 | |
|                         billItem.setTradeState(WxPayHelper.REFUND);
 | |
|                         bilList.add(billItem);
 | |
|                     }
 | |
|                 } else if ("交易".equals(tradeState)) { // 成功
 | |
|                     if (WxPayHelper.SUCCESS.equals(billType) || WxPayHelper.ALL.equals(billType)) {
 | |
|                         billItem.setTradeState(WxPayHelper.SUCCESS);
 | |
|                         bilList.add(billItem);
 | |
|                     }
 | |
|                 }
 | |
|             } catch (Exception e) {
 | |
|                 ErrorHelper.println(e);
 | |
|                 throw new ServiceException(ResultEnum.INTERFACE_ALI_INVOKE_ERROR);
 | |
|             }
 | |
|         }
 | |
|         return bilList;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     public static String dataHandle(String data) {
 | |
|         if (ObjectUtils.isEmpty(data)) {
 | |
|             return null;
 | |
|         }
 | |
|         return data.trim();
 | |
|     }
 | |
| }
 | |
| 
 |