支付宝springboot快速开发(实用干货一步一步教你在SpringBoot中集成支付宝的当面付)
支付宝springboot快速开发(实用干货一步一步教你在SpringBoot中集成支付宝的当面付)注意:将jar打到本地maven仓库时命令行最好是一行,不好有回车换行 这里使用2017版本的,不是2016版本的,Demo中的还是2016的版本。集成前需要先创建应用、配置密钥、回调地址等,具体操作请查看Spring Boot入门教程(三十五):支付宝集成-准备工作集成当面付即可以参考TradePayDemo中的Main.java也可以参考WebRoot下的jsp,这里参考JSP中的代码来集成。 这里注意一点:jsp中的最上面的注释,所有的jsp文件都是一样的,作者粘贴了也不知道改一下,吐槽一下:这Demo写的真不细心条码支付流程图:扫码支付流程图:
一:简介当面付分为三种,不同的方式有着不同的使用场景,当面付的字面含义是面对面的付也就是买家当着卖家的面来付款。其中条码支付和扫码支付使用的最多,本文主要介绍这两种。
- 当面付条码支付(商家使用扫码设备扫描买家支付宝的付款码):条码支付是支付宝给到线下传统行业的一种收款方式。商户使用扫码枪等条码识别设备扫描用户支付宝钱包上的条码/二维码,完成收款。一般在超市、便利店、店铺等使用。扫码支付(买家使用支付宝扫一扫扫描卖家的二维码):指用户打开支付宝钱包中的“扫一扫”功能,扫描商户针对每个订单实时生成的订单二维码,并在手机端确认支付。一般在类似于无人售货机上使用,像地铁中的无人售货饮料机、医院中的自助挂号收费机等声波支付
在接入之前首先要熟悉一下当面付产品的业务文档:
- 当面付文档
- 沙箱当面付接入引导
- 快速接入
当面付SDK&Demo 拿到demo我们熟悉一下demo,就可以依葫芦画瓢了,集成到自己工程中来。
通过下载Demo可以看到有两个工程,一个是TradePayDemo,一个是TradePaySDK,集成SDK必须要使用alipay-sdk-java20161213173952.jar,而TradePaySDK是对alipay-sdk-java.jar的进一步封装,在TradePayDemo中可以看到已经对TradePaySDK打成了jar包依赖进来了,当然也可以将整个TradePaySDK的src目录拖入到自己工程中 当然也可以不使用alipay-trade-sdk.jar直接使用alipay-sdk-java.jar来开发。
集成当面付即可以参考TradePayDemo中的Main.java也可以参考WebRoot下的jsp,这里参考JSP中的代码来集成。 这里注意一点:jsp中的最上面的注释,所有的jsp文件都是一样的,作者粘贴了也不知道改一下,吐槽一下:这Demo写的真不细心
- 条码付参考trade_pay.jsp
- 扫码付参考trade_precreate.jsp
- 退款参考trade_refund.jsp
- 交易查询参考trade_query.jsp
条码支付流程图:
扫码支付流程图:
二:集成步骤0. 创建应用、配置密钥
集成前需要先创建应用、配置密钥、回调地址等,具体操作请查看Spring Boot入门教程(三十五):支付宝集成-准备工作
1. 将Demo中的alipay-sdk-java和alipay-trade-sdk两个jar安装到本地maven仓库中
注意:将jar打到本地maven仓库时命令行最好是一行,不好有回车换行 这里使用2017版本的,不是2016版本的,Demo中的还是2016的版本。
mvn install:install-file -DgroupId=com.alipay -DartifactId=alipay-sdk-java -Dversion= 20170725114550 -Dpackaging=jar -Dfile=alipay-sdk-java-20170725114550.jar
mvn install:install-file -DgroupId=com.alipay -DartifactId=alipay-trade-sdk -Dversion=20161215 -Dpackaging=jar -Dfile=alipay-trade-sdk-20161215.jar
2. 引入依赖
<!-- alipay begin -->
<dependency>
<groupId>com.alipay</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>20170725114550</version>
</dependency>
<dependency>
<groupId>com.alipay</groupId>
<artifactId>alipay-trade-sdk</artifactId>
<version>20161215</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>1.10</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.2.1</version>
</dependency>
<!-- alipay end -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
3. 配置application.yml
注意:配置appPrivateKey和alipayPublicKey时值不要换行 returnUrl在当面付中不是必须的 注意:这里的returnUrl和notifyUrl在测试的时候需要外网能访问,可以通过natapp来实现
# 沙箱配置
pay:
alipay:
gatewayUrl: https://openapi.alipaydev.com/gateway.do
appid: 2016091400508498
appPrivateKey: MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCtXKWs trRSuCxEUvlsEeSAuLWs3B/Dh74R8223BnfzoA29aGeoycAqfKlBMcbzU2G1KayESvZKGpwBLeejSbecRYjgZsQDyEaDimQQJtGFvZVV6u4XNnvIJ72eQzEaEIQfuiorjBTLm6DQuds4R0GxftqON6QFoIZkWB9ZrZKd02cuy16dW UqtLVGGAHcCIAkB63zUiKSNfweMddneZ7MVs3lvu3xhMnD 5us/ n2Vp4qhfmpYLcdqIW6InU4GypeoOpyUTrfUGpgdR0l924vHy/GQJZEKFaRcK3cYK ECyKpSIoqaJJFLHbkqsliuPpMUG rM3jiqeIAH4psAznAgMBAAECggEBAJ5jyEbbxrJzrAh7GhHX1fwUQPYSadTbrPYAfHX2cHlnrQMJtsk nTLhEv0r VJwZ8WpYkfMong8kcqYtL7ajcmsHqMAFhE9EWxBxj2ymWsXLabZe93sj30IG9Rq0nxcGQgDO0RqKWLGSFgK93Al2KRInKT3InkY53K vR61ihVLmGf7 GwPtIZteBy tuAyvcj2RlkYvjiFi3ySyZ1wA3sJIlgrGiixd6fj20XFGNE3CnAwu0BJgXXbP/S9J4C0RRa3ZXj8fX7oONhVxz2xKxn7AT4e8OWjt7J41H2LRct8Fgl9pqgz2FJYv/WfbkG8x9fGiKYYvPXoxjjrk/tkewkCgYEA8f9Lcu5JPrE9rpw9zlwhm5cOO81xLxdwL5R5/1bRP48BZGIYuqlCbVvjJVqtO8eTnLhUwH7fG8B7cmoeO9bGr9GQrtfyCqz6FtVymTBieJlfgZDVhtzyv2qKOBMIFE8jsbSBK/NHHMvykJ XdQ1riwCeQDdXICRuYTTFwGk2OsUCgYEAt2SoN95tVmVrvKG6ATLNEtge/ozeVywA4GjltrSw/G9vqp DkkT2pY19uROuzMazoTzKWpPho2q/qzNlv/ANbOFM2GEmKamQ7CO88JgRxMsPTvc/HxCLU/ClMJU8LcOf9LfP2KYZpPwuheKJoF4vDGj8NsbFmccJyYSdpkNEk7sCgYBJlL2FMaz1sgC2Ue19DIhvfaunRV1P20mSPgwmNmijccETm7w3LXX0OIdFeV/JGHLqqSWj7i 6iXk/ncKZoUGCfi8G6sQ uL/GJ5qTt6GJV ExTS PtSjeSO/EAw1m13Vb C16hpstx1l23f 4aJ81gbecgPct38XsKpaiXZtOnQKBgQCMsN7QRYYxwoq9YoDUzIlAzKYyeBVWYL6najHYUZR5hG/xQIBqZRem9/4cTvpJxKInrwA6LrrqaEl0aHDFp75U6h7O3PCvA5PXZK9dD/yJsZIj7U/yX/nTQokn1UUegrYiwiTkusBvrrtuINWePsLvTVc4GpObHnPmsiNTWsWwYwKBgENaeTNOCHV2km/ysXQSEIhKbtlAMQPsgWHCt/bzHlF9m18izb1LrJyjzcSsd Zy78R pv4G50Q27c3e/DFPz/wYxN/yHWRbyLBA8ipJbCtMtPEdS9krpmN6cChIdLGbz4CVUqOPSRzNb9lhhgPCcCNRq6DG3HBceb1Se9VnO3zk
alipayPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApFQKccMq wPoWh93DcX3XYrnT7FJ3gntJA/jEwgk6Jd3iEVS2CyUCCgFVcQn8xjXT81YbZHYvoC50IBuu A Ey J8VIgsBm5g9uwbOLRf66GrZjuKOlalHm5gHXjcL2gZRMBJEStOxstcO2YQriqhQzdL3EKp HQc9u14IOVFpZdR8qq1l7CzKHn1vQo/1fUPfUrTLQqSuQTU9Ospv/QHFqOJA7DPetUQ jnaZ082f3clr4ITw4EE8A6IWqTXcETTx5j/udCGP84g2Y4j 8i9DqYGyD5ePVgt4G0ICBX1bi1qNlylfxRg8Y3c1DFrRGyr0NpKQxSVXkYaVNvrCoudQIDAQAB
returnUrl: http://fuzsmc.natappfree.cc/alipay/return
notifyUrl: http://fuzsmc.natappfree.cc/alipay/notify
4. AliPayProperties
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import javax.annotation.PostConstruct;
/**
* @author mengday zhang
*/
@Data
@Slf4j
@ConfigurationProperties(prefix = "pay.alipay")
public class AlipayProperties {
/** 支付宝gatewayUrl */
private String gatewayUrl;
/** 商户应用id */
private String appid;
/** RSA私钥,用于对商户请求报文加签 */
private String appPrivateKey;
/** 支付宝RSA公钥,用于验签支付宝应答 */
private String alipayPublicKey;
/** 签名类型 */
private String signType = "RSA2";
/** 格式 */
private String formate = "json";
/** 编码 */
private String charset = "UTF-8";
/** 同步地址 */
private String returnUrl;
/** 异步地址 */
private String notifyUrl;
/** 最大查询次数 */
private static int maxQueryRetry = 5;
/** 查询间隔(毫秒) */
private static long queryDuration = 5000;
/** 最大撤销次数 */
private static int maxCancelRetry = 3;
/** 撤销间隔(毫秒) */
private static long cancelDuration = 3000;
private AlipayProperties() {}
/**
* PostContruct是spring框架的注解,在方法上加该注解会在项目启动的时候执行该方法,也可以理解为在spring容器初始化的时候执行该方法。
*/
@PostConstruct
public void init() {
log.info(description());
}
public String description() {
StringBuilder sb = new StringBuilder("\nConfigs{");
sb.append("支付宝网关: ").append(gatewayUrl).append("\n");
sb.append(" appid: ").append(appid).append("\n");
sb.append(" 商户RSA私钥: ").append(getKeyDescription(appPrivateKey)).append("\n");
sb.append(" 支付宝RSA公钥: ").append(getKeyDescription(alipayPublicKey)).append("\n");
sb.append(" 签名类型: ").append(signType).append("\n");
sb.append(" 查询重试次数: ").append(maxQueryRetry).append("\n");
sb.append(" 查询间隔(毫秒): ").append(queryDuration).append("\n");
sb.append(" 撤销尝试次数: ").append(maxCancelRetry).append("\n");
sb.append(" 撤销重试间隔(毫秒): ").append(cancelDuration).append("\n");
sb.append("}");
return sb.toString();
}
private String getKeyDescription(String key) {
int showLength = 6;
if (StringUtils.isNotEmpty(key) && key.length() > showLength) {
return new StringBuilder(key.substring(0 showLength)).append("******")
.append(key.substring(key.length() - showLength)).toString();
}
return null;
}
}
5. AliPayConfiguration
@Configuration
@EnableConfigurationProperties(AlipayProperties.class)
public class AlipayConfiguration {
private AlipayProperties properties;
public AlipayConfiguration(AlipayProperties properties) {
this.properties = properties;
}
@Bean
public AlipayTradeService alipayTradeService() {
return new AlipayTradeServiceImpl.ClientBuilder()
.setGatewayUrl(properties.getGatewayUrl())
.setAppid(properties.getAppid())
.setPrivateKey(properties.getAppPrivateKey())
.setAlipayPublicKey(properties.getAlipayPublicKey())
.setSignType(properties.getSignType())
.build();
}
}
6. PayUtil
public class PayUtil {
/**
* 根据url生成二位图片对象
*
* @param codeUrl
* @return
* @throws WriterException
*/
public static BufferedImage getQRCodeImge(String codeUrl) throws WriterException {
Map<EncodeHintType Object> hints = new Hashtable();
hints.put(EncodeHintType.ERROR_CORRECTION ErrorCorrectionLevel.M);
hints.put(EncodeHintType.CHARACTER_SET "UTF8");
int width = 256;
BitMatrix bitMatrix = (new MultiFormatWriter()).encode(codeUrl BarcodeFormat.QR_CODE width width hints);
BufferedImage image = new BufferedImage(width width 1);
for(int x = 0; x < width; x) {
for(int y = 0; y < width; y) {
image.setRGB(x y bitMatrix.get(x y) ? -16777216 : -1);
}
}
return image;
}
}
7. AlipayF2FController
package com.example.paydemo.controller;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.demo.trade.model.GoodsDetail;
import com.alipay.demo.trade.model.builder.AlipayTradePayRequestBuilder;
import com.alipay.demo.trade.model.builder.AlipayTradePrecreateRequestBuilder;
import com.alipay.demo.trade.model.builder.AlipayTradeQueryRequestBuilder;
import com.alipay.demo.trade.model.builder.AlipayTradeRefundRequestBuilder;
import com.alipay.demo.trade.model.result.AlipayF2FPayResult;
import com.alipay.demo.trade.model.result.AlipayF2FPrecreateResult;
import com.alipay.demo.trade.model.result.AlipayF2FQueryResult;
import com.alipay.demo.trade.model.result.AlipayF2FRefundResult;
import com.alipay.demo.trade.service.AlipayTradeService;
import com.alipay.demo.trade.utils.ZxingUtils;
import com.example.paydemo.configuration.AlipayProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.util.*;
/**
* 支付宝-当面付 控制器.
* <p>
* https://openclub.alipay.com/read.php?tid=1720&fid=40
*
* https://docs.open.alipay.com/203/105910
*
* @author Mengday Zhang
* @version 1.0
* @since 2018/6/4
*/
@Slf4j
@RestController
@RequestMapping("/alipay")
public class AlipayF2FController {
@Autowired
private AlipayTradeService alipayTradeService;
@Autowired
private AlipayProperties aliPayProperties;
/**
* 当面付-条码付
*
* 商家使用扫码工具(扫码枪等)扫描用户支付宝的付款码
*
* @param authCode
*/
@PostMapping("/f2fpay/barCodePay")
public String barCodePay(String authCode){
// 实际使用时需要根据商品id查询商品的基本信息并计算价格(可能还有什么优惠),这里只是写死值来测试
// (必填) 商户网站订单系统中唯一订单号,64个字符以内,只能包含字母、数字、下划线,
String outTradeNo = UUID.randomUUID().toString();
// (必填) 订单标题,粗略描述用户的支付目的。如“喜士多(浦东店)消费”
String subject = "测试订单";
// 订单描述,可以对交易或商品进行一个详细地描述,比如填写"购买商品2件共15.00元"
String body = "购买商品2件共20.05元";
// (必填) 订单总金额,单位为元,不能超过1亿元
// 如果同时传入了【打折金额】 【不可打折金额】 【订单总金额】三者 则必须满足如下条件:【订单总金额】=【打折金额】 【不可打折金额】
String totalAmount = "0.01";
// (可选) 订单不可打折金额,可以配合商家平台配置折扣活动,如果酒水不参与打折,则将对应金额填写至此字段
// 如果该值未传入 但传入了【订单总金额】 【打折金额】 则该值默认为【订单总金额】-【打折金额】
String undiscountableAmount = "";
// (必填) 商户门店编号,通过门店号和商家后台可以配置精准到门店的折扣信息,详询支付宝技术支持
String storeId = "test_store_id";
// 商户操作员编号,添加此参数可以为商户操作员做销售统计
String operatorId = "test_operator_id";
// 商品明细列表,需填写购买商品详细信息,
List<GoodsDetail> goodsDetailList = new ArrayList<>();
GoodsDetail goods1 = GoodsDetail.newInstance("goods_id001" "全麦小面包" 1 1);
goodsDetailList.add(goods1);
GoodsDetail goods2 = GoodsDetail.newInstance("goods_id002" "黑人牙刷" 1 2);
goodsDetailList.add(goods2);
// 支付超时,线下扫码交易定义为5分钟
String timeoutExpress = "5m";
AlipayTradePayRequestBuilder builder = new AlipayTradePayRequestBuilder()
.setOutTradeNo(outTradeNo)
.setSubject(subject)
.setBody(body)
.setTotalAmount(totalAmount)
.setAuthCode(authCode)
.setTotalAmount(totalAmount)
.setStoreId(storeId)
.setOperatorId(operatorId)
.setGoodsDetailList(goodsDetailList)
.setTimeoutExpress(timeoutExpress);
// 当面付,面对面付,face to face pay -> face 2 face pay -> f2f pay
// 同步返回支付结果
AlipayF2FPayResult f2FPayResult = alipayTradeService.tradePay(builder);
// 注意:一定要处理支付的结果,因为不是每次支付都一定会成功,可能会失败
switch (f2FPayResult.getTradeStatus()) {
case SUCCESS:
log.info("支付宝支付成功: )");
break;
case FAILED:
log.error("支付宝支付失败!!!");
break;
case UNKNOWN:
log.error("系统异常,订单状态未知!!!");
break;
default:
log.error("不支持的交易状态,交易返回异常!!!");
break;
}
/**
* {
* "alipay_trade_pay_response": {
* "code": "10000"
* "msg": "Success"
* "buyer_logon_id": "ekf***@sandbox.com"
* "buyer_pay_amount": "0.01"
* "buyer_user_id": "2088102176027680"
* "buyer_user_type": "PRIVATE"
* "fund_bill_list": [
* {
* "amount": "0.01"
* "fund_channel": "ALIPAYACCOUNT"
* }
* ]
* "gmt_payment": "2018-06-10 14:54:16"
* "invoice_amount": "0.01"
* "out_trade_no": "91fbd3fa-8558-443a-82c2-bd8e941d7e71"
* "point_amount": "0.00"
* "receipt_amount": "0.01"
* "total_amount": "0.01"
* "trade_no": "2018061021001004680200326495"
* }
* "sign": "BNgMmA2t8fwFZNSa39kyEKgL6hV45DVOKOsdyyzTzsQnX8HEkKOzVevQEDyK083dNYewip1KK/K92BTDY2KMAsrOEqcCNxsk9NLAvK9ZQVxQzFbAFKqs5EBAEzncSWnChJcb7VMhDakUxHZFmclHg38dLJiHE2bEcF8ar9R1zj0p4V0Jr BXO10kLtaSTc9NeaCwJZ89sPHKitNnUWRroU7t0xPHc1hWpstObwixKmAWnsFyb9eyGwPQnqNBsUVNSNWCJ7Pg3rb03Tx6J3zNsqH5f0YhWilMi09npPe33URFc6zG1HJSfhEm4Gq1zwQrPoA/anW8BbdmEUUmNo1dEw=="
* }
*/
String result = f2FPayResult.getResponse().getBody();
return result;
}
/**
* 当面付-扫码付
*
* 扫码支付,指用户打开支付宝钱包中的“扫一扫”功能,扫描商户针对每个订单实时生成的订单二维码,并在手机端确认支付。
*
* 发起预下单请求,同步返回订单二维码
*
* 适用场景:商家获取二维码展示在屏幕上,然后用户去扫描屏幕上的二维码
* @return
* @throws AlipayApiException
*/
@PostMapping("/precreate")
public void precreate(HttpServletRequest request HttpServletResponse response) throws Exception {
// 实际使用时需要根据商品id查询商品的基本信息并计算价格(可能还有什么优惠),这里只是写死值来测试
// (必填) 商户网站订单系统中唯一订单号,64个字符以内,只能包含字母、数字、下划线,
String outTradeNo = UUID.randomUUID().toString();
// (必填) 订单标题,粗略描述用户的支付目的。如“喜士多(浦东店)消费”
String subject = "测试订单";
// 订单描述,可以对交易或商品进行一个详细地描述,比如填写"购买商品2件共15.00元"
String body = "购买商品2件共20.05元";
// (必填) 订单总金额,单位为元,不能超过1亿元
// 如果同时传入了【打折金额】 【不可打折金额】 【订单总金额】三者 则必须满足如下条件:【订单总金额】=【打折金额】 【不可打折金额】
String totalAmount = "0.01";
// (可选) 订单不可打折金额,可以配合商家平台配置折扣活动,如果酒水不参与打折,则将对应金额填写至此字段
// 如果该值未传入 但传入了【订单总金额】 【打折金额】 则该值默认为【订单总金额】-【打折金额】
String undiscountableAmount = "";
// 卖家支付宝账号ID,用于支持一个签约账号下支持打款到不同的收款账号,(打款到sellerId对应的支付宝账号)
// 如果该字段为空,则默认为与支付宝签约的商户的PID,也就是appid对应的PID
String sellerId = "";
// (必填) 商户门店编号,通过门店号和商家后台可以配置精准到门店的折扣信息,详询支付宝技术支持
String storeId = "test_store_id";
// 商户操作员编号,添加此参数可以为商户操作员做销售统计
String operatorId = "test_operator_id";
// 商品明细列表,需填写购买商品详细信息,
List<GoodsDetail> goodsDetailList = new ArrayList<>();
GoodsDetail goods1 = GoodsDetail.newInstance("goods_id001" "全麦小面包" 1 1);
goodsDetailList.add(goods1);
GoodsDetail goods2 = GoodsDetail.newInstance("goods_id002" "黑人牙刷" 1 2);
goodsDetailList.add(goods2);
// 支付超时,线下扫码交易定义为5分钟
String timeoutExpress = "5m";
AlipayTradePrecreateRequestBuilder builder =new AlipayTradePrecreateRequestBuilder()
.setSubject(subject)
.setTotalAmount(totalAmount)
.setOutTradeNo(outTradeNo)
.setUndiscountableAmount(undiscountableAmount)
.setSellerId(sellerId)
.setBody(body)
.setOperatorId(operatorId)
.setStoreId(storeId)
.setTimeoutExpress(timeoutExpress)
//支付宝服务器主动通知商户服务器里指定的页面http路径 根据需要设置
.setNotifyUrl(aliPayProperties.getNotifyUrl())
.setGoodsDetailList(goodsDetailList);
AlipayF2FPrecreateResult result = alipayTradeService.tradePrecreate(builder);
String qrCodeUrl = null;
switch (result.getTradeStatus()) {
case SUCCESS:
log.info("支付宝预下单成功: )");
AlipayTradePrecreateResponse res = result.getResponse();
BufferedImage image = PayUtil.getQRCodeImge(res.getQrCode());
response.setContentType("image/jpeg");
response.setHeader("Pragma" "no-cache");
response.setHeader("Cache-Control" "no-cache");
response.setIntHeader("Expires" -1);
ImageIO.write(image "JPEG" response.getOutputStream());
break;
case FAILED:
log.error("支付宝预下单失败!!!");
break;
case UNKNOWN:
log.error("系统异常,预下单状态未知!!!");
break;
default:
log.error("不支持的交易状态,交易返回异常!!!");
break;
}
}
/**
* 订单查询(最主要用于查询订单的支付状态)
* @param orderNo 商户订单号
* @return
*/
@GetMapping("/query")
public String query(String orderNo){
AlipayTradeQueryRequestBuilder builder = new AlipayTradeQueryRequestBuilder()
.setOutTradeNo(orderNo);
AlipayF2FQueryResult result = alipayTradeService.queryTradeResult(builder);
switch (result.getTradeStatus()) {
case SUCCESS:
log.info("查询返回该订单支付成功: )");
AlipayTradeQueryResponse resp = result.getResponse();
log.info(resp.getTradeStatus());
// log.info(resp.getFundBillList());
break;
case FAILED:
log.error("查询返回该订单支付失败!!!");
break;
case UNKNOWN:
log.error("系统异常,订单支付状态未知!!!");
break;
default:
log.error("不支持的交易状态,交易返回异常!!!");
break;
}
return result.getResponse().getBody();
}
/**
* 退款
* @param orderNo 商户订单号
* @return
*/
@PostMapping("/refund")
public String refund(String orderNo){
AlipayTradeRefundRequestBuilder builder = new AlipayTradeRefundRequestBuilder()
.setOutTradeNo(orderNo)
.setRefundAmount("0.01")
.setRefundReason("当面付退款测试")
.setOutRequestNo(String.valueOf(System.nanoTime()))
.setStoreId("A1");
AlipayF2FRefundResult result = alipayTradeService.tradeRefund(builder);
switch (result.getTradeStatus()) {
case SUCCESS:
log.info("支付宝退款成功: )");
break;
case FAILED:
log.error("支付宝退款失败!!!");
break;
case UNKNOWN:
log.error("系统异常,订单退款状态未知!!!");
break;
default:
log.error("不支持的交易状态,交易返回异常!!!");
break;
}
return result.getResponse().getBody();
}
/**
* 扫码付异步结果通知
* https://docs.open.alipay.com/194/103296
* @param request
*/
@RequestMapping("/notify")
public String notify(HttpServletRequest request) throws AlipayApiException {
// 一定要验签,防止黑客篡改参数
Map<String String[]> parameterMap = request.getParameterMap();
parameterMap.forEach((key value) -> System.out.println(key "=" value[0]));
// https://docs.open.alipay.com/54/106370
// 获取支付宝POST过来反馈信息
Map<String String> params = new HashMap<>();
Map requestParams = request.getParameterMap();
for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
String name = (String) iter.next();
String[] values = (String[]) requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i ) {
valueStr = (i == values.length - 1) ? valueStr values[i]
: valueStr values[i] " ";
}
params.put(name valueStr);
}
boolean flag = AlipaySignature.rsaCheckV1(params
aliPayProperties.getAlipayPublicKey()
aliPayProperties.getCharset()
aliPayProperties.getSignType());
if (flag) {
/**
* TODO 需要严格按照如下描述校验通知数据的正确性
*
* 商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
* 并判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
* 同时需要校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email),
*
* 上述有任何一个验证不通过,则表明本次通知是异常通知,务必忽略。
* 在上述验证通过后商户必须根据支付宝不同类型的业务通知,正确的进行不同的业务处理,并且过滤重复的通知结果数据。
* 在支付宝的业务通知中,只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时,支付宝才会认定为买家付款成功。
*/
return "success";
}
return null;
}
}
总结
以上代码都实际测试过,保证所有代码都是可用的,Spring Boot集成支付宝只需把代码复制过去即可,关注并私信“当面付”获取支付宝集成源码!