实用干货 | 一步一步教你在SpringBoot中集成支付宝的当面付

实用干货 | 一步一步教你在SpringBoot中集成支付宝的当面付

首页战争策略ProjectCL2更新时间:2024-04-30
一:简介

当面付分为三种,不同的方式有着不同的使用场景,当面付的字面含义是面对面的付也就是买家当着卖家的面来付款。其中条码支付和扫码支付使用的最多,本文主要介绍这两种。

在接入之前首先要熟悉一下当面付产品的业务文档:

当面付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写的真不细心

条码支付流程图:

扫码支付流程图:

二:集成步骤

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集成支付宝只需把代码复制过去即可,关注并私信“当面付”获取支付宝集成源码!

查看全文
大家还看了
也许喜欢
更多游戏

Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved