本文主要是介绍对接支付宝(当面付-扫码支付,电脑网站支付),已通过测试,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
1.去蚂蚁金服开放平台下载demo,拿到相关jar
2.pom.xml文件
<!-- 加载jar-->
<!-- alipay-->
<dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId><version>1.10</version><scope>system</scope><systemPath>${project.basedir}/src/main/webapp/WEB-INF/lib/commons-codec-1.10.jar</systemPath>
</dependency>
<dependency><groupId>commons-configuration</groupId><artifactId>commons-configuration</artifactId><version>1.10</version><scope>system</scope><systemPath>${project.basedir}/src/main/webapp/WEB-INF/lib/commons-configuration-1.10.jar</systemPath>
</dependency>
<dependency><groupId>commons-lang</groupId><artifactId>commons-lang</artifactId><version>2.6</version><scope>system</scope><systemPath>${project.basedir}/src/main/webapp/WEB-INF/lib/commons-lang-2.6.jar</systemPath>
</dependency>
<dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.1.1</version><scope>system</scope><systemPath>${project.basedir}/src/main/webapp/WEB-INF/lib/commons-logging-1.1.1.jar</systemPath>
</dependency><dependency><groupId>com.aliyun</groupId><artifactId>alipay-sdk-java-3.3.0.jar</artifactId><version>3.3.0</version><scope>system</scope><systemPath>${project.basedir}/src/main/webapp/WEB-INF/lib/alipay-sdk-java-3.3.0.jar</systemPath>
</dependency>
<dependency><groupId>com.aliyun</groupId><artifactId>alipay-sdk-java-3.3.0-source.jar</artifactId><version>3.3.0</version><scope>system</scope><systemPath>${project.basedir}/src/main/webapp/WEB-INF/lib/alipay-sdk-java-3.3.0-source.jar</systemPath>
</dependency>
<dependency><groupId>com.aliyun</groupId><artifactId>alipay-trade-sdk-20161215.jar</artifactId><version>3.3.0</version><scope>system</scope><systemPath>${project.basedir}/src/main/webapp/WEB-INF/lib/alipay-trade-sdk-20161215.jar</systemPath>
</dependency>
<dependency><groupId>com.aliyun</groupId><artifactId>alipay-trade-sdk-20161215-source.jar</artifactId><version>3.3.0</version><scope>system</scope><systemPath>${project.basedir}/src/main/webapp/WEB-INF/lib/alipay-trade-sdk-20161215-source.jar</systemPath>
</dependency>
<dependency><groupId>org.hamcrest</groupId><artifactId>hamcrest-core</artifactId><version>1.3</version><scope>system</scope><systemPath>${project.basedir}/src/main/webapp/WEB-INF/lib/hamcrest-core-1.3.jar</systemPath>
</dependency><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><includeSystemScope>true</includeSystemScope></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding><compilerArguments><extdirs>${project.basedir}/src/main/webapp/WEB-INF/lib</extdirs></compilerArguments></configuration></plugin></plugins>
</build>
3.Alipay-config.properties
# 支付宝网关名
open_api_domain =
# 商户UID
pid =
# appId
appid = # RSA私钥、公钥和支付宝公钥(在蚂蚁金服开放平台下载公私钥生成工具生成并配置在账户里)
private_key =
public_key = #SHA1withRsa对应支付宝公钥
#alipay_public_key = MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDI6d306Q8fIfCOaTXyiUeJHkrIvYISRcc73s3vF1ZT7XN8RNPwJxo8pWaJMmvyTn9N4HQ632qJBVHf8sxHi/fEsraprwCtzvzQETrNRwVxLO5jVmRGi60j8Ue1efIlzPXV9je9mkjzOmdssymZkh2QhUrCmZYI/FCEa3/cNMW0QIDAQAB#SHA256withRsa对应支付宝公钥(在账户里面查看)
alipay_public_key = # 签名类型: RSA->SHA1withRsa,RSA2->SHA256withRsa
sign_type = RSA2
# 当面付最大查询次数和查询间隔(毫秒)
max_query_retry = 50
query_duration = 3000# 当面付最大撤销次数和撤销间隔(毫秒)
max_cancel_retry = 3
cancel_duration = 2000# 交易保障线程第一次调度延迟和调度间隔(秒)
heartbeat_delay = 5
heartbeat_duration = 900#支付超时时间 m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)
#该参数数值不接受小数点, 如 1.5h,可转换为 90m。
timeout_express=120m
#编码
charset = UTF-8#返回格式
format = json# 以下地址必须公网可以访问,否则无法通知。我用的内网穿透工具
# 电脑网站支付异步通知地址
page_notify_URL= http://qq1334713380.xicp.net:36576/xboot/pay/aliPayNotify/aliPay_page_notify# 电脑网站支付同步通知地址(电脑网站支付成功以后跳转页面)
return_URL= http://qq1334713380.xicp.net:36576/xboot/pay/aliPayReturn.html# 当面付-扫码支付 异步通知地址
precreate_notify= http://qq1334713380.xicp.net:36576/xboot/pay/aliPayNotify/aliPay_precreate_notify
4.AlipayConfig.java
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;/*** @ClassName: PropConfig* @Description: TODO(配置文件设置)* @author 君泓 junying.wjy* @date 2018年7月17日 下午4:23:20**/
public final class AlipayConfig {public static final String ALIPAY_DEMO = "alipay";public static final String ALIPAY_DEMO_VERSION = "alipay_JAVA_20180907104657";/*** 配置文件加载*/private static Properties prop;/*** 配置文件名称*/public static String CONFIG_FILE = "Alipay-Config.properties";/*** 配置文件相对路径*/public static String ALIPAY_CONFIG_PATH = File.separator + "etc" + File.separator + CONFIG_FILE;/*** 项目路径*/public static String PROJECT_PATH = "";private static Log logger = LogFactory.getLog(AlipayConfig.class);/*** 初始化配置值*/public static void initPropertis() {prop = new Properties();try {synchronized (prop) {InputStream inputStream = AlipayConfig.class.getClassLoader().getResourceAsStream(CONFIG_FILE);prop.load(inputStream);inputStream.close();}} catch (IOException e) {logger.error("日志 =============》: 配置文件Alipay-Config.properties找不到");e.printStackTrace();}}/*** 获取配置文件信息* * @return*/public static Properties getProperties() {if (prop == null){initPropertis();}return prop;}/*** 配置信息写入配置文件*/public static void writeConfig() {System.out.println("ALIPAY_CONFIG_PATH:"+ALIPAY_CONFIG_PATH);File file = new File(ALIPAY_CONFIG_PATH);if (file.exists()) {file.setReadable(true);file.setWritable(true);}String lineText = null;BufferedReader bufferedReader = null;BufferedWriter bw = null;StringBuffer stringBuffer = new StringBuffer();try {bufferedReader = new BufferedReader(new FileReader(ALIPAY_CONFIG_PATH));while ((lineText = bufferedReader.readLine()) != null) {if (lineText.startsWith("APP_ID")) {lineText = "APP_ID = " + prop.getProperty("APP_ID");} else if (lineText.startsWith("RSA2_PRIVATE_KEY")) {lineText = "RSA2_PRIVATE_KEY = " + prop.getProperty("RSA2_PRIVATE_KEY");} else if (lineText.startsWith("RSA2_PUBLIC_KEY")) {lineText = "RSA2_PUBLIC_KEY = " + prop.getProperty("RSA2_PUBLIC_KEY");} else if (lineText.startsWith("ALIPAY_RSA2_PUBLIC_KEY")) {lineText = "ALIPAY_RSA2_PUBLIC_KEY = " + prop.getProperty("ALIPAY_RSA2_PUBLIC_KEY");} else if (lineText.startsWith("NOTIFY_URL")) {lineText = "NOTIFY_URL = " + prop.getProperty("NOTIFY_URL");} else if (lineText.startsWith("RETURN_URL")) {lineText = "RETURN_URL = " + prop.getProperty("RETURN_URL");} else if (lineText.startsWith("SANDBOX_BUYER_EMAIL")) {lineText = "SANDBOX_BUYER_EMAIL = " + prop.getProperty("SANDBOX_BUYER_EMAIL");} else if (lineText.startsWith("SANBOX_BUYER_LOGON_PWD")) {lineText = "SANBOX_BUYER_LOGON_PWD = " + prop.getProperty("SANBOX_BUYER_LOGON_PWD");} else if (lineText.startsWith("SANBOX_BUYER_PAY_PWD")) {lineText = "SANBOX_BUYER_PAY_PWD = " + prop.getProperty("SANBOX_BUYER_PAY_PWD");} else if (lineText.startsWith("SANDBOX_SELLER_ID")) {lineText = "SANDBOX_SELLER_ID = " + prop.getProperty("SANDBOX_SELLER_ID");} else if (lineText.startsWith("SANDBOX_SELLER_EMAIL")) {lineText = "SANDBOX_SELLER_EMAIL = " + prop.getProperty("SANDBOX_SELLER_EMAIL");} else if (lineText.startsWith("SANDBOX_SELLER_LOGON_PWD")) {lineText = "SANDBOX_SELLER_LOGON_PWD = " + prop.getProperty("SANDBOX_SELLER_LOGON_PWD");} else if (lineText.startsWith("ALIPAY_GATEWAY_URL")) {lineText = "ALIPAY_GATEWAY_URL = " + prop.getProperty("ALIPAY_GATEWAY_URL");} else if (lineText.startsWith("CHARSET")) {lineText = "CHARSET = " + prop.getProperty("CHARSET");} else if (lineText.startsWith("FORMAT")) {lineText = "FORMAT = " + prop.getProperty("FORMAT");} else if (lineText.startsWith("SIGNTYPE")) {lineText = "SIGNTYPE = " + prop.getProperty("SIGNTYPE");}stringBuffer.append(lineText).append("\r\n");}bufferedReader.close();bw = new BufferedWriter(new FileWriter(ALIPAY_CONFIG_PATH));bw.write(stringBuffer.toString());bw.close();} catch (Exception e) {e.printStackTrace();}}public final static class Status {/*** 上传成功*/public final static String UPLOAD_SUCCESS = "UPLOAD_SUCCESS";/*** 上传失败*/public final static String UPLOAD_FAILED = "UPLOAD_FAILED";/*** 同样的公钥*/public final static String NOT_COVER = "NOT_COVER";}
}
5.DefaultAlipayClientFactory.java
import java.util.Properties;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;/*** @ClassName: DefaultAlipayClientFactory* @Description: TODO(公共请求参数拼接类)* @author 君泓 junying.wjy* @date 2018年7月23日 下午4:38:38**/
public class DefaultAlipayClientFactory {private static AlipayClient alipayClient = null;/*** 封装公共请求参数* * @return AlipayClient*/public static AlipayClient getAlipayClient() {if (alipayClient != null) {return alipayClient;}Properties prop = AlipayConfig.getProperties();// 网关String URL = prop.getProperty("open_api_domain");// 商户APP_IDString APP_ID = prop.getProperty("appid");// 商户RSA 私钥String APP_PRIVATE_KEY = prop.getProperty("private_key");// 请求方式 jsonString FORMAT = prop.getProperty("FORMAT");// 编码格式,目前只支持UTF-8String CHARSET = prop.getProperty("CHARSET");// 支付宝公钥String ALIPAY_PUBLIC_KEY = prop.getProperty("alipay_public_key");// 签名方式String SIGN_TYPE = prop.getProperty("sign_type");return new DefaultAlipayClient(URL, APP_ID, APP_PRIVATE_KEY, FORMAT, CHARSET, ALIPAY_PUBLIC_KEY, SIGN_TYPE);}
}
6.spring boot的启动类中添加:
@Primary
@Bean
public AlipayTradeService alipayTradeService() {AlipayTradeService tradeService = new AlipayTradeServiceImpl.ClientBuilder().build();return tradeService;
}
7.AlipayVo.java
@Data
public class AlipayVo implements Serializable {private static final long serialVersionUID = 1L;/*** 订单名称*/private String subject;/*** 商户网站唯一订单号*/private String out_trade_no;/*** 该笔订单允许的最晚付款时间*/private String timeout_express;/*** 付款金额*/private String total_amount;/*** 销售产品码,与支付宝签约的产品码名称*/private String product_code;}
7.AliPayServiceImpl.java(提供支付服务)
import cn.hutool.core.util.StrUtil;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.domain.TradeFundBill;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.*;
import com.alipay.demo.trade.config.Configs;
import com.alipay.demo.trade.model.ExtendParams;
import com.alipay.demo.trade.model.builder.AlipayTradePrecreateRequestBuilder;
import com.alipay.demo.trade.model.result.AlipayF2FPrecreateResult;
import com.alipay.demo.trade.service.AlipayTradeService;
import com.google.gson.Gson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import javax.servlet.http.HttpServletRequest;
import java.util.*;@SuppressWarnings("ALL")
@Service
public class AliPayServiceImpl implements IAliPayService {private static Logger log = LoggerFactory.getLogger(AliPayServiceImpl.class);/*** 支付宝当面付2.0服务*/@Autowiredprivate AlipayTradeService tradeService;@Autowiredprivate OrderDao orderDao;@Autowiredprivate IPayOrderFlowService payOrderFlowService;@Autowiredprivate PayOrderFlowDao payOrderFlowDao;@Autowiredprivate OrderService orderService;@Autowiredprivate UserDao userDao;@Autowiredprivate OrderRefundService orderRefundService;@Autowiredprivate DictDataDao dictDataDao;@Autowiredprivate DictDao dictDao;private Properties prop = AlipayConfig.getProperties();static {/** 一定要在创建AlipayTradeService之前调用Configs.init()设置默认参数* Configs会读取classpath下的zfbinfo.properties文件配置信息,如果找不到该文件则确认该文件是否在classpath目录*/Configs.init("Alipay-Config.properties");}/*** 当面付生成支付二维码** @param outTradeNo* @return*/@Overridepublic Map createNative(String outTradeNo) {// (必填) 商户网站订单系统中唯一订单号,64个字符以内,只能包含字母、数字、下划线,// 需保证商户系统端不能重复,建议通过数据库sequence生成,//订单号合法性校验Order order = PayCommonUtil.checkOutTradeNo(outTradeNo, true);// (必填) 订单标题,粗略描述用户的支付目的。如“xxx品牌xxx门店当面付扫码消费”String subject = "";// (必填) 订单总金额,单位为元,不能超过1亿元// 如果同时传入了【打折金额】,【不可打折金额】,【订单总金额】三者,则必须满足如下条件:【订单总金额】=【打折金额】+【不可打折金额】String totalAmount = order.getPrice() + "";// 卖家支付宝账号ID,用于支持一个签约账号下支持打款到不同的收款账号,(打款到sellerId对应的支付宝账号)// 如果该字段为空,则默认为与支付宝签约的商户的PID,也就是appid对应的PIDString sellerId = "";// (必填) 商户门店编号,通过门店号和商家后台可以配置精准到门店的折扣信息,详询支付宝技术支持String storeId = "lvmajiayonggong";// 业务扩展参数,目前可添加由支付宝分配的系统商编号(通过setSysServiceProviderId方法),详情请咨询支付宝技术支持ExtendParams extendParams = new ExtendParams();extendParams.setSysServiceProviderId(Configs.getPid());// 支付超时,定义为120分钟String timeoutExpress = prop.getProperty("timeout_express");// 创建扫码支付请求builder,设置请求参数AlipayTradePrecreateRequestBuilder builder = new AlipayTradePrecreateRequestBuilder().setSubject(subject).setTotalAmount(totalAmount).setOutTradeNo(outTradeNo).setSellerId(sellerId).setExtendParams(extendParams).setStoreId(storeId).setTimeoutExpress(timeoutExpress)//支付宝服务器主动通知商户服务器里指定的页面http路径.setNotifyUrl(prop.getProperty("precreate_notify"));AlipayF2FPrecreateResult result = tradeService.tradePrecreate(builder);switch (result.getTradeStatus()) {case SUCCESS:AlipayTradePrecreateResponse response = result.getResponse();Map responseMap = new HashMap(16);//二维码生成链接responseMap.put("codeUrl", response.getQrCode());//订单号responseMap.put("outTradeNo", outTradeNo);//金额responseMap.put("totalFee", order.getPrice());return responseMap;case FAILED:log.error("支付宝预下单失败!!!");ExceptionCast.cast(ResultEnum.ALI_PAY_TRADE_STATUS_FAILED);break;case UNKNOWN:log.error("系统异常,预下单状态未知!!!");ExceptionCast.cast(ResultEnum.ALI_PAY_TRADE_STATUS_UNKNOWN);break;default:log.error("不支持的交易状态,交易返回异常!!!");ExceptionCast.cast(ResultEnum.ALI_PAY_TRADE_STATUS_DEFAULT);break;}return null;}/*** 公共查询订单** @param outTradeNo* @return*/@Overridepublic Result tradeQuery(String outTradeNo) {//订单号合法性校验Order order = PayCommonUtil.checkOutTradeNo(outTradeNo, true);// 创建查询请求builder,设置请求参数AlipayClient alipayClient = DefaultAlipayClientFactory.getAlipayClient();AlipayTradeQueryRequest alipayRequest = new AlipayTradeQueryRequest();alipayRequest.setBizContent("{\"out_trade_no\":\"" + outTradeNo + "\"}");//增加一个定时器,防止无限轮询下去int i = 0;try {//轮询while (true) {i++;AlipayTradeQueryResponse response = null;response = alipayClient.execute(alipayRequest);if ("TRADE_SUCCESS".equals(response.getTradeStatus())) {//更新订单状态order.setPayTime(new Date());order.setPayType(OrderStatusName.ALI_PAY);orderDao.save(order);orderService.updateStatusBypay(order.getId());return ResultUtil.success("支付成功");} else if ("TRADE_CLOSED".equals(response.getTradeStatus())) {return ResultUtil.error("交易超时关闭或支付完成后全额退款");} else if ("UNKNOWN".equals(response.getTradeStatus())) {return ResultUtil.error("系统异常,订单支付状态未知!!!");}try {Thread.sleep(Integer.parseInt(prop.getProperty("query_duration")));} catch (InterruptedException e) {e.printStackTrace();}//防止无限的轮询下去,增加一个计数器if (i >= 20) {return ResultUtil.error("二维码超时");}}} catch (AlipayApiException e) {e.printStackTrace();}return ResultUtil.error("支付错误");}/*** 支付宝PC网站支付** @param outTradeNo* @return*/@Overridepublic Result createPagePay(String outTradeNo) {//订单号合法性校验Order order = PayCommonUtil.checkOutTradeNo(outTradeNo, true);//如果不使用数据对象,就需要自己去拼接字符串。详情参考官网demoAlipayVo vo = new AlipayVo();//订单号vo.setOut_trade_no(outTradeNo);//价格vo.setTotal_amount(order.getPrice() + "");//标题vo.setSubject("");//这个是固定的vo.setProduct_code("FAST_INSTANT_TRADE_PAY");//超时时间vo.setTimeout_express(prop.getProperty("timeout_express"));//转jsonString json = new Gson().toJson(vo);log.info("json: {}", json);//公共请求参数AlipayClient alipayClient = DefaultAlipayClientFactory.getAlipayClient();// 设置请求参数AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();alipayRequest.setReturnUrl(prop.getProperty("page_notify_URL"));alipayRequest.setNotifyUrl(prop.getProperty("return_URL"));alipayRequest.setBizContent(json);AlipayTradePagePayResponse response = null;try {response = alipayClient.pageExecute(alipayRequest);} catch (AlipayApiException e) {e.printStackTrace();if (e.getCause() instanceof java.security.spec.InvalidKeySpecException) {return ResultUtil.error("商户私钥格式不正确");}}log.info("result: {}", response.getBody());if (response.isSuccess()) {return ResultUtil.data(response, "请求成功");} else {return ResultUtil.error("支付宝预下单失败!!!");}}/*** 电脑网站和当面付的退款** @param outTradeNo 订单号* @param refundAmount 退款金额* @return*/@Overridepublic Result tradePageRefund(OrderRefund refund) {//公共请求参数AlipayClient alipayClient = DefaultAlipayClientFactory.getAlipayClient();//设置请求参数AlipayTradeRefundRequest alipayRequest = new AlipayTradeRefundRequest();Random random = new Random();int i = random.nextInt();// (必填) 退款原因,可以说明用户退款原因,方便为商家后台提供统计String refundReason = "正常退款";//标识一次退款请求,同一笔交易多次退款需要保证唯一,如需部分退款,则此参数必传String outRequestNo = i + "";alipayRequest.setBizContent("{\"out_trade_no\":\"" + refund.getOrderId().getOrderUUID() + "\","+ "\"refund_amount\":\"" + refund.getRufundPrice() + "\","+ "\"refund_reason\":\"" + refundReason + "\","+ "\"out_request_no\":\"" + outRequestNo + "\"}");AlipayTradeRefundResponse response = null;try {response = alipayClient.execute(alipayRequest);if ("Y".equals(response.getFundChange())) {//退款成功/*** 在这里处理你的业务逻辑*/}} catch (AlipayApiException e) {e.printStackTrace();ExceptionCast.cast(ResultEnum.INVALID_PARAM);}this.createPayOrderFlow(response, 1);return ResultUtil.error("退款失败");}/*** 将request中的参数转换成Map* @param request* @return*/public static Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {Map<String, String> retMap = new HashMap<String, String>();Set<Map.Entry<String, String[]>> entrySet = request.getParameterMap().entrySet();for (Map.Entry<String, String[]> entry : entrySet) {String name = entry.getKey();String[] values = entry.getValue();int valLen = values.length;if (valLen == 1) {retMap.put(name, values[0]);} else if (valLen > 1) {StringBuilder sb = new StringBuilder();for (String val : values) {sb.append(",").append(val);}retMap.put(name, sb.toString().substring(1));} else {retMap.put(name, "");}}return retMap;}
}
8.AliPayNotifyController.java(支付成功后的异步通知)
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.demo.trade.config.Configs;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.*;@Slf4j
@RestController
@Api(description = "支付宝支付异步回调接口")
@RequestMapping("/xboot/pay/aliPayNotify")
public class AliPayNotifyController {@Autowiredprivate OrderService orderService;private static Logger logger = LoggerFactory.getLogger(AliPayNotifyController.class);//构建线程池
private static final ThreadPoolExecutor EXECUTOR_SERVICE = new ThreadPoolExecutor(5,8,0,TimeUnit.MILLISECONDS,new ArrayBlockingQueue<>(10),new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(),new ThreadPoolExecutor.CallerRunsPolicy());@Autowiredprivate IPayOrderFlowService payOrderFlowService;@Autowiredprivate UserDao userDao;@Autowiredprivate OrderDao orderDao;static {Configs.init("Alipay-Config.properties");// 支付宝配置}/*** 扫码支付成功异步通知接口* <pre>* 第一步:验证签名,签名通过后进行第二步* 第二步:按一下步骤进行验证* 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,* 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),* 3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email),* 4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过,则表明本次通知是异常通知,务必忽略。* 在上述验证通过后商户必须根据支付宝不同类型的业务通知,正确的进行不同的业务处理,并且过滤重复的通知结果数据。* 在支付宝的业务通知中,只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时,支付宝才会认定为买家付款成功。* </pre>** @param* @return*/@PostMapping("aliPay_precreate_notify")public void aliPayPrecreateNotify(HttpServletRequest request, HttpServletResponse response) {// 将异步通知中收到的待验证所有参数都存放到map中Map<String, String> params = AliPayServiceImpl.convertRequestParamsToMap(request);System.out.println(params);String paramsJson = JSON.toJSONString(params);logger.info("支付宝回调,{}", paramsJson);ServletOutputStream outputStream = null;try {// 调用SDK验证签名boolean signVerified = AlipaySignature.rsaCheckV1(params, Configs.getAlipayPublicKey(),"UTF-8", Configs.getSignType());if (signVerified) {logger.info("支付宝回调签名认证成功");// 按照支付结果异步通知中的描述,对支付结果中的业务内容进行1\2\3\4二次校验,校验成功后在response中返回success,校验失败返回failurethis.check(params);// 另起线程处理业务EXECUTOR_SERVICE .execute(() -> {//接收响应参数的实体AliPayNotifyParam param = buildAlipayNotifyParam(params);String trade_status = param.getTradeStatus();// 支付成功if (AliPayTradeStatus.TRADE_SUCCESS.equals(trade_status)|| AliPayTradeStatus.TRADE_FINISHED.equals(trade_status)) {try {// 在这里处理支付成功逻辑} catch (Exception e) {logger.error("支付宝回调业务处理报错,params:" + paramsJson, e);}} else {logger.error("没有处理支付宝回调业务,支付宝交易状态:{},params:{}", trade_status, paramsJson);}});// 如果签名验证正确,立即返回success,后续业务另起线程单独处理// 业务处理失败,可查看日志进行补偿,跟支付宝已经没多大关系。outputStream = response.getOutputStream();outputStream.println("success");return;} else {logger.info("支付宝回调签名认证失败,signVerified=false, paramsJson:{}", paramsJson);outputStream.println("failure");}} catch (AlipayApiException e) {logger.error("支付宝回调签名认证失败,paramsJson:{},errorMsg:{}", paramsJson, e.getMessage());ExceptionCast.cast(ResultEnum.INVALID_PARAM);} catch (IOException e) {e.printStackTrace();} finally {try {outputStream.close();} catch (IOException e) {e.printStackTrace();}}}/*** pc网站支付异步通知接口** @return* @throws AlipayApiException*/@PostMapping("/aliPay_page_notify")public void aliPayPageNotify(HttpServletRequest request, HttpServletResponse response) {Map<String, String> params = AliPayServiceImpl.convertRequestParamsToMap(request);logger.info("notify params: {}", JSONObject.toJSON(params));// 验证签名boolean signVerified = false;try {signVerified = AlipaySignature.rsaCheckV1(params, Configs.getAlipayPublicKey(), "UTF-8", Configs.getSignType());logger.info("notify signVerified: {}", signVerified);if (signVerified) {Order order = this.check(params);// 另起线程处理业务EXECUTOR_SERVICE .execute(() -> {AliPayNotifyParam param = buildAlipayNotifyParam(params);String tradeStatus = param.getTradeStatus();// 支付成功if (AliPayTradeStatus.TRADE_SUCCESS.equals(tradeStatus)|| AliPayTradeStatus.TRADE_FINISHED.equals(tradeStatus)) {//在这里处理你的业务逻辑//判断该笔订单是否在商户网站中已经做过处理//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序//如果有做过处理,不执行商户的业务程序//注意://付款完成后,支付宝系统发送该交易状态通知} else {logger.error("没有处理支付宝回调业务,支付宝交易状态:{},params:{}", params.get("trade_status"), JSONObject.toJSON(params));}});// 如果签名验证正确,立即返回success,后续业务另起线程单独处理// 业务处理失败,可查看日志进行补偿,跟支付宝已经没多大关系。response.getOutputStream().println("success");} else {logger.info("验证失败,不去更新状态");response.getOutputStream().println("failure");}} catch (AlipayApiException e) {ExceptionCast.cast(ResultEnum.INVALID_PARAM);} catch (IOException e) {e.printStackTrace();}}private AliPayNotifyParam buildAlipayNotifyParam(Map<String, String> params) {String json = JSON.toJSONString(params);return JSON.parseObject(json, AliPayNotifyParam.class);}/*** 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,* 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),* 3、校验通知中的seller_id(或者seller_email)是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email),* 4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过,则表明本次通知是异常通知,务必忽略。* 在上述验证通过后商户必须根据支付宝不同类型的业务通知,正确的进行不同的业务处理,并且过滤重复的通知结果数据。* 在支付宝的业务通知中,只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时,支付宝才会认定为买家付款成功。** @param params* @throws AlipayApiException*/private Order check(Map<String, String> params) throws AlipayApiException {String outTradeNo = params.get("out_trade_no");// 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,Order order = orderService.findByOrderUUID(outTradeNo);if (order == null) {throw new AlipayApiException("out_trade_no错误");}// 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),BigDecimal bigDecimal = new BigDecimal(params.get("total_amount"));if (order.getPrice().compareTo(bigDecimal) != 0) {throw new AlipayApiException("error total_amount");}// 3、校验通知中的seller_id(或者seller_email)是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email),// 第三步可根据实际情况省略// 4、验证app_id是否为该商户本身。if (!params.get("app_id").equals(Configs.getAppid())) {throw new AlipayApiException("app_id不一致");}return order;}}
9.AliPayNotifyParam.java
/**
* 支付宝支付结果异步响应实体
*/
@Data
public class AliPayNotifyParam {private String appId;private String tradeNo; // 支付宝交易凭证号private String outTradeNo; // 原支付请求的商户订单号private String outBizNo; // 商户业务ID,主要是退款通知中返回退款申请的流水号private String buyerId; // 买家支付宝账号对应的支付宝唯一用户号。以2088开头的纯16位数字private String buyerLogonId; // 买家支付宝账号private String sellerId; // 卖家支付宝用户号private String sellerEmail; // 卖家支付宝账号private String tradeStatus; // 交易目前所处的状态,见交易状态说明private BigDecimal totalAmount; // 本次交易支付的订单金额private BigDecimal receiptAmount; // 商家在交易中实际收到的款项private BigDecimal buyerPayAmount; // 用户在交易中支付的金额private BigDecimal refundFee; // 退款通知中,返回总退款金额,单位为元,支持两位小数private String subject; // 商品的标题/交易标题/订单标题/订单关键字等private String body; // 该订单的备注、描述、明细等。对应请求时的body参数,原样通知回来private Date gmtCreate; // 该笔交易创建的时间。格式为yyyy-MM-dd HH:mm:ssprivate Date gmtPayment; // 该笔交易的买家付款时间。格式为yyyy-MM-dd HH:mm:ssprivate Date gmtRefund; // 该笔交易的退款时间。格式为yyyy-MM-dd HH:mm:ss.Sprivate Date gmtClose; // 该笔交易结束时间。格式为yyyy-MM-dd HH:mm:ssprivate String fundBillList; // 支付成功的各个渠道金额信息,arrayprivate String passbackParams; // 公共回传参数,如果请求时传递了该参数,则返回给商户时会在异步通知时将该参数原样返回。}
ps:蚂蚁金服开放平台SDK标准版现以官方的名义上传至 Maven 中央仓库,对接的商户有用到mvn项目的可以使用,maven地址:
<dependency><groupId>com.alipay.sdk</groupId><artifactId>alipay-sdk-java</artifactId><version>3.3.49.ALL</version></dependency>
这篇关于对接支付宝(当面付-扫码支付,电脑网站支付),已通过测试的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!