支付宝支付实战

-一、支付宝支付介绍和接入指引 

1 、准备工作

微信支付介绍和接入指引(在线实战!!!!微信直连!!)_微信支付接入-CSDN博客

微信支付APIv2介绍-CSDN博客

2 、支付宝开放能力介绍

支付宝开放平台

2.1 、产品地图

支付产品、资金产品等

主要将支付产品

2.2 、电脑⽹站⽀付产品介绍

可以用沙箱,不需要营业执照等,这方面和微信不一样

常规接入:创建应用、绑定应用、配置密钥、上线应用、签约功能

沙箱接入流程:直接使用沙箱提供的开发参数,无需进行应用的创建、绑定、上线签约

3、接入准备

3.1 、开放平台账号注册

3.2、常规接入

1、创建应用

创建好后获取appid

 还需要一些配置

接口加签方式配置:(有两种,一种是密钥方式,一种是证书方式)

然后点击确认【有两种方式,之前的微信支付,微信的v3版本所有的要使用公钥证书,但是微信支付的v2版本只有一些接口使用证书,比如红包等。】

但是支付宝的接口,我们可以使用密钥因为我们不需要商家给用户发红包、商家转账给用户等等。因为我们做的只是用户向商家付款的功能。所以选择密钥就可以了。

然后下一步:点击蓝字,密钥工具

 然后下载后点击安装如下:

因为RSA2是非对称加密,所以密钥有两把,公钥和私钥。(都是商户的私钥和公钥)

然后复制公钥到下面【私钥会复制到配置文件里面】

商户的私钥是用于签名,复制商户公钥给支付宝,是让支付宝做验签

然后输入即可 

支付宝公钥下载后就可以叉掉了

接下来看一下关于支付宝的网关地址

这个是我们向支付宝发起远程调用的主机地址,复制一下(正式的主机地址),复制到配置文件里面。【沙箱的主机地址和这个是不一样的】 

应用网关授权回调地址在当前界面是不需要配置的

应用网关是用于接受口碑或者生活号信息,授权回调地址是第三方授权或者用户信息授权使用的。目前不需要配置

下面这个也是可选,请求信息和响应信息要加密的话就要设置。【微信支付v3版本默认请求和响应就是加密的、对称加密】如果想要加密的话就需要有一个对账加密的密钥,我们接受到支付宝的响应信息或者支付宝接受到我们的请求信息,调用相同的对账加密的密钥进行解密操作。

然后查看,用这把钥匙进行加密和解密

2、绑定应用

 

点击商家服务平台

首页 - 商家平台

到商家中心后会获取到一个pid,也就是商家账号

在【账号中心】中的【收单账号】这个也放入到配置文件中

然后进行具体的绑定操作:绑定完成

3、配置密钥

实际上这个过程在创建应用过程中已经做过了 

注意:密钥和APPID是一一对应的。即开发者需要为名下的每一个应用分别设置密钥,且不同应用的密钥不能混用。

注意前面获取的支付宝公钥也要复制到配置文件中。 

# 支付宝支付相关参数

# 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
alipay.app-id=2021003124617201

# 商户PID,卖家支付宝账号ID
alipay.seller-id=2088102040215494

# 支付宝网关
alipay.gateway-url=https://openapi.alipay.com/gateway.do

# 商户私钥,您的PKCS8格式RSA2私钥
alipay.merchant-private-key=MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDmMcbk+ezXjrwG2kTIQugXLpMJnl8b112Bq+TV/yQgL2oMC2alYCXDzyHWyjXLHhpeb9MVrKKqqdifcv3+r0U2rclsmoBVIdmlpk9E3Hi0Ulb7qIoYwgrPUMpSQssCPnyCqoN7Xg+y7PqZhgHQpBOF34lGokc1hAVyHHIt+JybTvDaWr00Em9NwGslw3oV8mDXA1rpoPSEpMyxrGW6kFaOlX08zsYoLJUgL9VkQAJ/WUm5gMpQArSzO9MFL3VwnyEBg2KBNlxuDwj+PJ6D3RB6o4SWID8X7y/UCscNLwTmVr2qLf6zSf3GtX+/jnXSVLTtqqL8bnZNFXKNbfoWNH5ZAgMBAAECggEAVEROkg3npLVMoZmPalwLyEi1bOT73h5Fza1WVPxUhi+1O3mE9u8ug/K0aYOWk6eOcZmwBRQwbBdHBH+8+VnCFZUi0k3wwrlkil5KUGQBD8nAq9lzzEJkYKYrmld3J3gmblLrVOMHDjHwPvkueulFeFFvWFsZhD6zG6XMKoYDFlrq1k4yCfimMrPTSLRxhUtkdbPXt0V8vTgrX9D5z9wFiAebjzRhRRhbxWH4xkdy5WaPI7z+zakIjjrWECdXEVVF7BvahBv1dGtKCGjwRwPw15Bc336yZ2Gqfa9Il06PHY3XCB5YCtE31T1s+Wm620hbtJ1Dk3sDnspoYpV8ZnPHLQKBgQD0QOHErtE6OcFMezWLWjbtak1QWUuxdCYtmVCgWqpPpe/JuFSKUYy6AF6Bd5RBJOa4i+RtrZuMAjM0QzZ/6gCZyWzMo0aZB9KhtC3ftpAKWvlmFdvH08I0GKd/PLY/Wcz5YIgByhcV2VZ410NH2u1nVqKL+jIfpzJOzM3zsLFGdwKBgQDxQ87dsVPxdwqcvAkv4WwFY0Dv1932uscyb5ZjkasWtiQ6TPrfzzkT13Zu81vqXL9eaiTgaP8bYeVUQVVScFiO0r2Ylp6TJzp3nl3qEm8N4DmoA/Vwo5TXoR55HgDUMBv7DMzcBa/PU5J1ErZ1pt6k2uBD0U3sMxIRP+VBpsMFrwKBgQCENM4/GGS1gGdpT1NXHziV3zED6aF35qd3jQHAGfMPc4DMDdLsn2FtmB+PMjtz21Zq04WL/Ckyakpu4maQbAdxNj6GsWXYFQzka9NcwMNMZ5uQrwosKil261VWIHWA6slwvdhAJ7PBJseQVuva69wOUC1hWMZirawkTOS5H42E1wKBgQCuDt6CgDlwXhKQ6vOx0G6vIGEr58/h/fRSBcE4ylHlS7itOvZPW1/xWaO+/eFVHl6NzgQWxokthx39ADl/BUBOoelY2WlD/qwmumFEytHF7/uIpHqBLfLm8f1bIfM1IhQ9tYliPtQMvl1OCxcJoD7GLoZXRvxxqJKjUTaje5z9TwKBgQDul1tsUHKpuWyoXoHQ6j0zqzMPgPohvFGN1fV6MBpHHFDDCLajuCrpj/gQPWcwRpOYArZK7u0cyvn/sWijbdL1v6RtjYt7sASiDRbf2FFraLeRJOu/KKkFZ5UZbXWiR2fs02+sTkLBD2MI16HquJzsS3rK+8Hitgdd/U3PyHDSyQ==

# 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥
alipay.alipay-public-key=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAguAlv4mG5/uvo5VjyU2ZAJuZR3VbSbhUN11DKYuNbtaGh9lzysF0N7ZbjWLb8E3TLYsvzSYiDwPUJyVU1uPLld9mXiLQ/k8FOAma1rCG7OFMhWtBWglZq2LxLN68qz8aIsUPbuaqiuIfF+zqg9dQ4y9CrhC18U5cCzpYmqoSbxZPMLGRE28qgs7m9FxmGBjttpa+oHHg2Jf1i79DOtbUCHTrK9Mr6Cfd47dAMQf0OdIuvD+fxYD3fF8tVeJWH+GyibMCojYn66lFUhR1TqKoKZtUxFCcnGbodEhoWr2iTlPTMQzm24EiYODod0xn3GWwigZcJma2tLruW51de6U+dwIDAQAB

# 接口内容加密秘钥,对称秘钥
alipay.content-key=pdF//ropAEqHXxI360iwUg==


# 页面跳转同步通知页面路径
alipay.return-url=http://localhost:8080/#/success

# 服务器异步通知页面路径  需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
# 注意:每次重新启动ngrok,都需要根据实际情况修改这个配置
alipay.notify-url=https://a863-180-174-204-169.ngrok.io/api/ali-pay/trade/notify





4、上线应用

就是提交审核(其实当你创建应用的时候就可以提交审核,因为绑定应用已经自动绑定了)

上线后还需要进行开通产品:

5、开通产品(签约)

因为我们目前是自研,所以商家账号和开发者账号是同一个支付宝账号。

点击开通【需要上传营业执照等一些资料】【个人后面会有沙箱方式

3.3、沙箱接入流程

 

或者查看

会发现很多我们在开发过程中需要配置参数他已经提供给我们了

1、APPID(自动分配的用于测试的)

2、PID

3、接口加签方式,使用系统默认的密钥就行了,点击查看

4、支付宝网关地址(沙箱版)

5、接口内容加密密钥,对称密钥

 然后把沙箱版的参数复制到配置文件如下:【用自己的参数】

# 支付宝支付相关参数

# 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
alipay.app-id=9021000139611815

# 商户PID,卖家支付宝账号ID
alipay.seller-id=2088721039265025

# 支付宝网关
alipay.gateway-url=https://openapi-sandbox.dl.alipaydev.com/gateway.do

# 商户私钥,您的PKCS8格式RSA2私钥
alipay.merchant-private-key=MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCvazWT8HC4BOhWR320k5JI0AAMzhNZcJ3R2EHb7ir7ATGguhGn3cmK1xNizp5gShe3PRB/qHfJhRV2HVbGY5cuoMnA3ZVnA5jmN1S6iRhFCz2a5dfF1ozy4PGket6um65Vkk2jO+8o75969r5jajzeD+KyvHa2igtRiDgaDH1BfYdBAdGQTgZ9JybNhj4xgtvfmU8yZhY3lq213oVOeqvgk5a8IpyKWMbIRknGuWGA+QDslRkz0aWyE9OiImU2q1GR7Qf8PMq93BhIWeglF9ztvfQmbzAaua1R0XC35VvQnqC3FRoaPlNZ7AzRFfUPk3vhFR+YBEVSHwuZqO8Q8ikTAgMBAAECggEARu5O59cdfQte9yRJTC8vw4l3u7XIVS4YYrXTS57uejhtXNRgN0STLjTxfc6Wc9EB7fIfLb9tUGsj1nLel7HFe/FAI2B1/QWTWeOdux+cVK7K5ktyCGlM7RjQHPYh/PCueaODCTI+7oqWLh5/tatLmW9K94MFbaI8GWe/Z6kNLOFtX/rt1pEWsiciGMHKTe0+E8YVbUKJNzgKn1DGCnmfDvQHZz/l1io+9MYPCoGppWb7VAanBY4GnKZgUQwqgyzo94n34dPuqfL/aIWq3J2OH7tn+x+lJQTEQP7YQ61x3LCN6/uAoe2mLr0WwZsHS4ajHUgbgv/LHyQwkKWGyWvoIQKBgQDk7ES7rNs51TG7I8Fxw1yHlNCQ8Vojk1NZXcBeWAFKvJQyZmtP0t7GqXMkbObjBVLuhQZaxIo9c2OF5IwjIf+4JPt/qIxE/S8GadC56F4wDcsMEX8H3/uxUAd2Aik9MXEx51mSrE6HXcw3lNO+FptKnveEsWXy5X995IT/nD0fbwKBgQDEKtkpCkskG/FcXLf6iEaYSpbiUB4CFNBGofew18vJd5aI5YvlD2yA9e6pEwYx55GB7voZZRFE0srDhoSkUFagEUvN/+C+CP81Yq3vRKSM8clUwYPD3v0cwgoI59YcSF2ELM5hUKm9Gqh+V96+J41wZjbedHCC5BwFS4NO3zc+nQKBgQCk23F0JjuKHno95rX8k1AjY4v/lY/wZrxzcwyB5Kaph7zBvvgw+A7uykDdTn/HpaHfm3TEER3suxhE6B3zfxyFJDzp5ht940bmO3XRLAIIYSaEfmUY6rh7LMLyvQOtydlWsuDSu7uhyG606DzEe8tBpdFigc6FabiylMsiuQVbiQKBgBxpaFJ9Xyy3r1mFRFBQ9IiSJRBaMj5y7UeNpeZDGwzu+SqStJRUi46SECME0lzgAT8kcazD6qBhN2TiUsvJpwI//w6zBMYBX5VH2ZFSC9MuIc7YyFDSNy63G+T2TxiBkqnWI34MxMjS15yU9XntGJsy8rd9fd43U6egrD/8LHAxAoGAJ1B9a+L0NbDKlM5k+OKXv21nuVQH6wcD5BDLnt/OI/nD426p0Dj8HwcMrfZqUGtM+n73f8GsydntA804B/B56xubB9n0cSBLCp/fh8doLbK6pkjozGrrrxRv7cArORIAZkGrTblYETOCtKMxokKnw/942zRbkquJpLNLO0SZW88=

# 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥
alipay.alipay-public-key=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkKdrbP/kRGJRFS4WhAtDx/lplqYTYEPm6lO46xnO0UkX3BbpSMXIWVmtKFRiGukhayyKmc8yNO7BS/D7bOGmQwGbcSBsT9AJIwaVvw+QSnaYs8lq9UMuaJqPw7/YpK/Q+4XrYYYMt9rDOaglhlvvscgB2yxx4bRq2zrmdP4uDWXnz7b/5LHftJHogHSk6R95Ifb2M4fMtqzjsyPs8P/m9HnN0zcfjSqmzzBvS/dwa4RrFOYHxTjJpUuqi2SCODRvau0A8z+YzlJWS9iW0MW/oVbqdK0Z17vXdcPXItgQZEDpLIgDfYbhspcGYi2uv9rW5Bu2q/7DYgQjJ5ZtDMHOwwIDAQAB

# 接口内容加密秘钥,对称秘钥
alipay.content-key=QCD/GssT/+99wo119MvYqg==

# 页面跳转同步通知页面路径
alipay.return-url=http://localhost:8080/#/success

# 服务器异步通知页面路径  需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
# 注意:每次重新启动ngrok,都需要根据实际情况修改这个配置
alipay.notify-url=https://a863-180-174-204-169.ngrok.io/api/ali-pay/trade/notify





已经自动开通的能力(产品):

发现有商家信息和买家信息

我们支付的时候需要用买家信息

 还需要下载一个沙箱版的支付宝

注意:登录沙箱版支付宝的时候需要用买家信息的账号和密码登陆,不要用手机号登陆

二、运行和配置案例项目

 我这边直接启动,看之前的关于微信支付的案例。

这边还需要替换前端项目,因为最新的前端项目有关于支付宝

启动前端项目:

目前开发关于支付宝支付这块,关于微信V3和微信v2之前都开发过。 

1 、引⼊⽀付参数

1.1 、引⼊沙箱配置⽂件

将之前准备好的 alipay-sandbox.properties 复制到项⽬的 resources ⽬录中 并将其设置为 spring 配置⽂件

用自己的沙箱参数

然后设置为spring配置文件

 一个是沙箱的参数,一个是正式的参数

1.2 、创建配置⽂件

在 config 包中创建 AlipayClientConfig来读取上面配置文件的配置

可以参考之前微信配置文件的读取方式,这次用一个更加简单的方式:

补充,上面多了

@PropertySource("classpath:alipay-sandbox.properties")

理论上在第一个类 AlipayClientConfig 中读取了该配置文件后,后续的类中不需读取了。

1.3 、测试配置⽂件的引⼊

然后启动测试

启动会报这个错误

 说这个文件找不到

然后发现target目录下面确实没有这个文件:

所以需要编译打包可以用maven install

再次运行:成功打印

2 、引⼊服务端 SDK

2.1 、引⼊依赖

通用版 - 支付宝文档中心

参考⽂档:开放平台 => ⽂档 => 开发⼯具 => 服务端 SDK => Java => 通⽤版 => Maven 项⽬依赖

 <dependency>
       <groupId>com.alipay.sdk</groupId>
        <artifactId>alipay-sdk-java</artifactId>
        <version>4.22.57.ALL</version>
 </dependency>

 

2.2 、创建客⼾端连接对象

创建带数据签名的客⼾端对象

参考⽂档:开放平台 => ⽂档 => 开发⼯具 => 技术接⼊指南 => 数据签名 https://opendocs.alipay.com/common/02kf5q

使用密钥的方式

参考⽂档中 公钥方式 完善 AlipayClientConfig 类,添加 alipayClient() ⽅法 初始化 AlipayClient 对象 

三、⽀付功能开发

总七个接口、还有一个支付成功通知的接口在下面,所以八个接口

至少关于demo的下载:【没有必要,版本太古老了】

还有这个签名和微信的是大同小异的【远程过程调用签名的原理】

在开发过程中并不会亲自做这个签名和验签。感兴趣的可以自己在这里面做个测试。

1 、统⼀收单下单并⽀付⻚⾯(*)

1.1 、⽀付调⽤流程

https://opendocs.alipay.com/open/270/105899

 三个角色:用户、商户系统、支付宝支付平台

1、下单:用户向商户系统发送一个交易请求,这个交易请求就像下订单了,1.1、商户系统接到这个下订单的请求后,会向支付宝发起一个支付请求。此时支付宝就会展示支付二维码和展示支付登陆窗口的一个请求。此时用户就会有两种方式选择,一种就是用手机的支付宝扫描展示出来的二维码。然后5、确认支付。第二种方式是会展示一个支付宝的登陆页面,用户也可以2、选择输入支付宝的用户名和支付密码。然后3、点击登陆。点击登录后,支付宝会把这个页面切换为一个支付页面,在这个支付页面中4、用户可以选择支付渠道、输入密码支付。然后5、确认支付。

在这个支付流程完成后,支付宝会对我们的比如说支付指纹或者说支付密码,支付宝会对这笔支付进行一个支付校验。如果支付校验成功了,也就是支付成功了,6、下单的时候传returnUrl,然后页面跳转,那么支付宝会跳转到商户中一个支付成功的页面当中。只是看到了成功页面,但是我们商户系统也要接受到这个支付成功的通知,就是预先开发一个接口(和微信支付一样),7、回调接口,支付宝发起调用。然后修改订单状态,将未支付改为已支付。

也有可能因为网络的原因,我们这个回调接口不能被支付宝成功调用,我们的系统就不能处理订单了,此时8、我们的商户系统可以向支付宝主动发起一个查单请求,查询这笔订单是否支付成功了,如果支付成功了,支付宝会将查询订单的结果返回给我们商户系统,我们商户系统在后续的业务当中修改订单的状态。不去开发回调接口,直接查单也是可以的。

我们两个都开发,双重保障。

1.2 、接⼝说明

https://opendocs.alipay.com/apis/028r8t?scene=22

公共请求参数:所有接⼝都需要的参数【在之前的alipayClient()中绝大部分都设置完成了】

没有设置的:比如return_url等

还有一个sign字段参数,是签名,这个签名参数会在我们使用alipayClient中,向阿里的开放平台发送接口调用请求之前,这个签名的参数会自动生成,所以不需要我们手动设置。

还有一个biz_content就是业务参数具体内容,也就是下面的请求参数!!!

请求参数:当前接⼝需要的参数,在编程的过程中依此组装。

公共响应参数:所有接⼝的响应中都包含的数据,

响应参数:当前接⼝的响应中包含的数据,返回一个表单

返回给商户系统前端后,会直接执行表单提交,这个表单又会提交到我们支付宝开放平台当中【也就是action中地址】,这个支付宝开放平台就会给我们展示一个支付页面了。这个支付页面有两个选项,一个是支付二维码,一个是输入支付账号密码。

1.3 、发起⽀付请求

( 1 )创建 AliPayController

( 2 )创建 AliPayService

package com.atguigu.paymentdemo.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.request.AlipayTradePagePayRequest;

import com.alipay.api.response.AlipayTradePagePayResponse;
import com.atguigu.paymentdemo.entity.OrderInfo;
import com.atguigu.paymentdemo.service.AliPayService;
import com.atguigu.paymentdemo.service.OrderInfoService;
import lombok.extern.slf4j.Slf4j;

import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;

@Slf4j
@Service
public class AliPayServiceImpl implements AliPayService {
    @Resource
    private OrderInfoService orderInfoService;
    @Resource
    private AlipayClient alipayClient;
    @Resource
    private Environment config;

    @Transactional //比如生成订单后,发生异常,然后会发生回滚
    @Override
    public String tradeCreate(Long productId) throws Exception {
        try {
            //生成订单
            log.info("生成订单");
            OrderInfo orderInfo = orderInfoService.createOrderByProductId(productId);

            //调用支付宝接口,这个名字和alipay.trade.page.pay(统一收单下单并支付页面接口)一样
            AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
            //配置需要的公共请求参数
            //request.setNotifyUrl("");
            //支付完成后,我们想让页面跳转回的页面,配置returnUrl
            //request.setReturnUrl(config.getProperty("alipay.return-url"));

            //组装当前业务方法的请求参数,也是就是公共请求参数中的biz_content
            JSONObject bizContent = new JSONObject();

            bizContent.put("out_trade_no", orderInfo.getOrderNo());//设置订单号
            //微信的支付单位是分,支付宝的支付单位是元,所以需要除以100
            BigDecimal total = new BigDecimal(orderInfo.getTotalFee().toString()).divide(new BigDecimal("100"));
            bizContent.put("total_amount",total);        // 设置订单总金额
            bizContent.put("subject", orderInfo.getTitle());   // 设置订单标题
            bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");       // 设置产品码
            //后面都是可选的参数,可以不设置,有需要再设置。
            request.setBizContent(bizContent.toJSONString());
            //执行请求,调用支付宝接口
            AlipayTradePagePayResponse response=alipayClient.pageExecute(request);

            if(response.isSuccess()){
                log.info("调用成功,返回结果====》"+response.getBody());
                return response.getBody();
            }else{
                log.info("调用失败,返回码===》"+response.getCode()+",返回描述===》"+response.getMsg());
                throw new RuntimeException("创建支付交易失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("创建支付交易失败");
        }

    }
}

然后运行启动进行支付

1、可以用沙箱支付宝直接扫码支付

2、 进入沙箱账号 - 开放平台然后输入账号和密码进行支付

 支付完成后希望我们的页面跳转到商家页面【因为之前支付的时候会跳转到支付宝的页面】

先注入

然后重新运行启动

补充

在线调试

API - 开放平台

代码补充:使用model,功能都是一样的,不过更加方便。避免参数字符串写错的错误。

1.4 、前端⽀付按钮

( 1 ) index.vue

 //确认支付
    toPay() {
      //禁用按钮,防止重复提交
      this.payBtnDisabled = true

      //微信支付
      if (this.payOrder.payType === 'wxpay') {
        //调用统一下单接口
        wxPayApi.nativePay(this.payOrder.productId).then((response) => {
          this.codeUrl = response.data.codeUrl
          this.orderNo = response.data.orderNo

          //打开二维码弹窗
          this.codeDialogVisible = true

          //启动定时器
          this.timer = setInterval(() => {
            //查询订单是否支付成功
            this.queryOrderStatus()
          }, 3000)
        })

        //支付宝支付
      } else if (this.payOrder.payType === 'alipay') {

        //调用支付宝统一收单下单并支付页面接口
        aliPayApi.tradePagePay(this.payOrder.productId).then((response) => {
          //将支付宝返回的表单字符串写在浏览器中,表单会自动触发submit提交
          document.write(response.data.formStr)
        })
      }
    },

( 2 ) aliPay.js

2 、⽀付结果通知(*)

2.1 、设置异步通知地址

在 AliPayServiceImpl 的 tradeCreate ⽅法中设置异步通知地址 

2.2 、启动内⽹穿透

前面微信支付有讲过,或者

内网穿透cploar!!!-CSDN博客

2.3 、修改内⽹穿透配置

2.4 、开发异步通知接⼝

( 1 ) AliPayController

然后就可以先进行测试,看支付通知接口会不会被调用

 

参数:

{
  "gmt_create": "2025-01-13 11:43:24",
  "charset": "UTF-8",
  "gmt_payment": "2025-01-13 11:43:32",
  "notify_time": "2025-01-13 11:43:34",
  "subject": "大数据课程",
  "sign": "ZKDlqbdIJAk29HNmUXMdJB9FxL/qaLXZWwXV9rIh3MiP6ReRTKS1Jxdv865MAisTMXnWaW+FVZGz36SHY7n8nN79ePhmc2FugQjHkkwS8uO4E+ceJZNczYY+qP1WpINBdlVDK5fU/tUrnOmXC2ohTTIb6oD5G1DgLNZUL6qCNG0sMoxOEYuefD4AK75VVPgK8/7bghJXlgtwzKufs1N9Yt4DrRuCDDNabNkB4dzLmLxRXSq5Ip3Jx6sfD4p6c64kw1eRrMMi7jzhc9F7NvbNSNnc+ubyeQ2CABWzDWoxvApJxd2MYziYt7IQec7RYDHwg1sKfYtTytmWSzD0Fe0rQA==",
  "buyer_id": "2088722039265040",
  "invoice_amount": 0.01,
  "version": "1.0",
  "notify_id": "2025011301222114333165040505115823",
  "fund_bill_list": [
    {
      "amount": "0.01",
      "fundChannel": "ALIPAYACCOUNT"
    }
  ],
  "notify_type": "trade_status_sync",
  "out_trade_no": "ORDER_20250113114247986",
  "total_amount": 0.01,
  "trade_status": "TRADE_SUCCESS",
  //支付宝交易号,支付宝对每一笔交易都会生成交易,这是支付宝交易的唯一编号
  "trade_no": "2025011322001465040505059182",
  "auth_app_id": "9021000139611815",
  "receipt_amount": 0.01,
  "point_amount": 0.00,
  "buyer_pay_amount": 0.01,
  "app_id": "9021000139611815",
  "sign_type": "RSA2",
  "seller_id": "2088721039265025"
}

 公共参数:

【支付宝返回的参数是携带签名的(sign),等会会对其进行校验,如果校验成功了,我们才可以正确的去接收这个通知并且我们才能去做后面的业务处理。如果签名校验不成功,那我们认为这个通知是非法的,或者有安全风险的,甚至是黑客伪造支付宝发送的一个假通知,我们拒绝这个通知】

注意

  • 状态 TRADE_SUCCESS 的通知触发条件是商家开通的产品支持退款功能的前提下,买家付款成功。
  • 交易状态 TRADE_FINISHED 的通知触发条件是商家开通的产品不支持退款功能的前提下,买家付款成功;或者,商家开通的产品支持退款功能的前提下,交易已经成功并且已经超过可退款期限。
  • 但是这里只有交易成功,通知才会被触发:

 必须返回success【不过在沙箱的环境下,支付宝是不会重发通知的】

目前验签是可以直接复制(用工具类)

 重新启动测试

前面的异步通知验签其实对下面的前四个步骤进行的封装:第五个步骤就是我们说的数据的二次校验,要参考第五个步骤1,2,3,4对我们获取的业务数据进行二次校验。


    @ApiOperation("支付通知")
    @PostMapping("/trade/notify") //在支付流程图可以看到,支付宝开放平台会以post方式调用这个接口
    public String tradeNotify(@RequestParam Map<String,String> params){//这里处理和微信的支付通知不一样
        log.info("支付通知正在执行");
        log.info("通知参数===》{}",params);
        String result = "failure";
        try {
            //异步通知验签
            boolean signVerified = AlipaySignature.rsaCheckV1(
                    params,
                    config.getProperty("alipay.alipay-public-key"),
                    AlipayConstants.CHARSET_UTF8,
                    AlipayConstants.SIGN_TYPE_RSA2); //调用SDK验证签名
            if(!signVerified){
                //验签失败则记录异常日志,并在response中返回failure.
                log.error("支付成功异步通知验签失败!");
                return result;
            }

            log.info("支付成功异步通知验签成功!");
            // 验签成功后,按照支付结果异步通知中的描述,对支付结果中的业务内容进行二次校验,

            //1 商家需要验证该通知数据中的 out_trade_no 是否为商家系统中创建的订单号。
            String out_trade_no = params.get("out_trade_no");
            OrderInfo order = orderInfoService.getOrderByOrderNo(out_trade_no);
            if(order==null){
                log.error("订单不存在");
                return result;
            }

            //2. 判断 total_amount 是否确实为该订单的实际金额(即商家订单创建时的金额)。
            String total_amount = params.get("total_amount");
            int totalAmountInt = new BigDecimal(total_amount).multiply(new BigDecimal("100")).intValue();
            int totalFeeInt = order.getTotalFee().intValue();
            if(totalAmountInt!=totalFeeInt){
                log.error("订单金额不一致");
                return result;
            }

            //3. 校验通知中的 seller_id(或者 seller_email)是否为 out_trade_no 这笔单据的对应的操作方
            // (有的时候,一个商家可能有多个 seller_id/seller_email)。
            String seller_id = params.get("seller_id");
            if(!seller_id.equals(config.getProperty("alipay.seller-id"))){
                log.error("商家id不一致");
                return result;
            }
            //4. 验证 app_id 是否为该商家本身。
            String app_id = params.get("app_id");
            if(!app_id.equals(config.getProperty("alipay.app-id"))){
                log.error("app_id不一致");
                return result;
            }
            //在支付宝的业务通知中
            //只有交易通知状态为 TRADE_SUCCESS 或 TRADE_FINISHED 时,支付宝才会认定为买家付款成功。
            //这里我们只用判断TRADE_SUCCESS就可以了(参考文档)
            if(!params.get("trade_status").equals("TRADE_SUCCESS")){
               log.error("交易未成功");
               return result;
            }

            // 校验成功后在response中返回success并继续商户自身业务处理,校验失败返回failure
            //TODO 处理业务 修改订单状态 记录支付日志(和微信支付中一样)

            //向支付宝返回成功的结果
            result="success";
        } catch (AlipayApiException e) {

            e.printStackTrace();
        }
        return result;
    }

然后重新启动测试,没有打印任何error日志,说明判断成功。

接下来处理业务:

@ApiOperation("支付通知")
    @PostMapping("/trade/notify") //在支付流程图可以看到,支付宝开放平台会以post方式调用这个接口
    public String tradeNotify(@RequestParam Map<String,String> params){//这里处理和微信的支付通知不一样
        log.info("支付通知正在执行");
        log.info("通知参数===》{}",params);
        String result = "failure";
        try {
            //异步通知验签
            boolean signVerified = AlipaySignature.rsaCheckV1(
                    params,
                    config.getProperty("alipay.alipay-public-key"),
                    AlipayConstants.CHARSET_UTF8,
                    AlipayConstants.SIGN_TYPE_RSA2); //调用SDK验证签名
            if(!signVerified){
                //验签失败则记录异常日志,并在response中返回failure.
                log.error("支付成功异步通知验签失败!");
                return result;
            }

            log.info("支付成功异步通知验签成功!");
            // 验签成功后,按照支付结果异步通知中的描述,对支付结果中的业务内容进行二次校验,

            //1 商家需要验证该通知数据中的 out_trade_no 是否为商家系统中创建的订单号。
            String out_trade_no = params.get("out_trade_no");
            OrderInfo order = orderInfoService.getOrderByOrderNo(out_trade_no);
            if(order==null){
                log.error("订单不存在");
                return result;
            }

            //2. 判断 total_amount 是否确实为该订单的实际金额(即商家订单创建时的金额)。
            String total_amount = params.get("total_amount");
            int totalAmountInt = new BigDecimal(total_amount).multiply(new BigDecimal("100")).intValue();
            int totalFeeInt = order.getTotalFee().intValue();
            if(totalAmountInt!=totalFeeInt){
                log.error("订单金额不一致");
                return result;
            }

            //3. 校验通知中的 seller_id(或者 seller_email)是否为 out_trade_no 这笔单据的对应的操作方
            // (有的时候,一个商家可能有多个 seller_id/seller_email)。
            String seller_id = params.get("seller_id");
            if(!seller_id.equals(config.getProperty("alipay.seller-id"))){
                log.error("商家id不一致");
                return result;
            }
            //4. 验证 app_id 是否为该商家本身。
            String app_id = params.get("app_id");
            if(!app_id.equals(config.getProperty("alipay.app-id"))){
                log.error("app_id不一致");
                return result;
            }
            //在支付宝的业务通知中
            //只有交易通知状态为 TRADE_SUCCESS 或 TRADE_FINISHED 时,支付宝才会认定为买家付款成功。
            //这里我们只用判断TRADE_SUCCESS就可以了(参考文档)
            if(!params.get("trade_status").equals("TRADE_SUCCESS")){
               log.error("交易未成功");
               return result;
            }

            // 校验成功后在response中返回success并继续商户自身业务处理,校验失败返回failure
            //处理业务 修改订单状态 记录支付日志(和微信支付中一样)

            aliPayService.processOrder(params);


            //向支付宝返回成功的结果
            result="success";
        } catch (AlipayApiException e) {

            e.printStackTrace();
        }
        return result;
    }

( 2 ) AliPayService

 /**
     * 处理订单
     * @param params
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void processOrder(Map<String, String> params) {
        log.info("处理订单");
        //获取订单号
        String out_trade_no = params.get("out_trade_no");
        //更新订单状态
        orderInfoService.updateStatusByOrderNo(out_trade_no,OrderStatus.SUCCESS);

        //记录支付日志
        paymentInfoService.createPaymentInfoForAlipay(params);

    }

2.5 、记录⽀付⽇志

PaymentInfoService

/**
     * 记录支付日志:支付宝
     * @param params
     */
    @Override
    public void createPaymentInfoForAlipay(Map<String, String> params) {
        log.info("记录支付日志");

        PaymentInfo paymentInfo = new PaymentInfo();
        paymentInfo.setOrderNo(params.get("out_trade_no"));
        paymentInfo.setPaymentType(PayType.ALIPAY.getType());
        paymentInfo.setTransactionId(params.get("trade_no"));//业务编号
        paymentInfo.setTradeType("电脑网站支付");
        paymentInfo.setTradeState(params.get("trade_status"));//交易状态
        int totalAmountInt = new BigDecimal(params.get("total_amount")).multiply(new BigDecimal("100")).intValue();
        paymentInfo.setPayerTotal(totalAmountInt);//交易金额,元转分单位

        Gson gson = new Gson();
        String s = gson.toJson(params, HashMap.class);
        paymentInfo.setContent(s);

        baseMapper.insert(paymentInfo);

    }

 然后重新启动

然后订单状态修改成功 

 日志记录成功

2.6 、更新订单状态记录⽀付⽇志

假设商户端已经给支付宝端发送了”success“,但是某些情况网络原因,我们的业务服务器没有成功的将通知返回传递到支付宝端,或者支付宝端没有成功的接收到我们的通知返回,那么支付宝就会重复的发送通知。

但是我们的支付状态已经改变了,支付日志已经记录了(如果重新发送通知,然后支付状态会改变没有影响,因为都是支付成功,但是支付日志会有多条)。

使用需要解决重复通知的问题。 

在  rocessOrder ⽅法中,更新订单状态之前,添加如下代码

【只有未支付才继续执行后面操作,无论是支付成功、用户已取消等都返回】

2.7 、数据锁

在 AliPayServiceImpl 中定义 ReentrantLock 进⾏并发控制。注意,必须⼿动释放锁。

是有一个服务器集群的,有可能多台服务器同时发送通知,当有多台服务器通知同时到达的时候,

日志依然会有两条。

private final ReentrantLock lock = new ReentrantLock();

完整的 processOrder ⽅法 

 /**
     * 处理订单
     * @param params
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void processOrder(Map<String, String> params) {
        log.info("处理订单");
        //获取订单号
        String out_trade_no = params.get("out_trade_no");

         /*在对业务数据进行状态检查和处理之前,
        要采用数据锁进行并发控制,
        以避免函数重入造成的数据混乱*/
        //尝试获取锁:
        // 成功获取则立即返回true,获取失败则立即返回false。不必一直等待锁的释放
        if(lock.tryLock()) {
            try {
                //处理重复的通知
                //接口调用的幂等性:无论接口被调用多少次,以下业务执行一次
                String orderStatus=orderInfoService.getOrderStatus(out_trade_no);
                if (!OrderStatus.NOTPAY.getType().equals(orderStatus)){
                    return;
                }


                //更新订单状态
                orderInfoService.updateStatusByOrderNo(out_trade_no,OrderStatus.SUCCESS);

                //记录支付日志
                paymentInfoService.createPaymentInfoForAlipay(params);
            } finally {
                //要主动释放锁
                lock.unlock();
            }
        }

    }

3 、订单表优化

3.1 、表修改 t_order_info

有微信支付和支付宝支付,但是我们订单都存储在t_order_info中,但是我们没有办法区分哪个是微信支付、哪个是支付宝支付的订单。

表中添加 payment_type 字段

手动添加

然后我们应用程序中修改实体类:

 

3.2 、业务修改

( 1 )修改⽀付业务代码

修改 AliPayServiceImpl 、 WxPayServiceImpl 代 码 中 对 如 下 ⽅ 法的调⽤,添加参数

PayType.ALIPAY.getType()

 

( 2 )修改 OrderInfoService

接⼝的 createOrderByProductId ⽅法中添加参数 String paymentType OrderInfo

实现类的 createOrderByProductId ⽅法中添加参数 String paymentType

对 getNoPayOrderByProductId ⽅法的调⽤时添加参数 paymentType

⽣成订单的过程中添加 orderInfo.setPaymentType(paymentType)

然后

重新启动服务器。

还是进行支付宝支付

 微信的v2、v3,支付宝测试后数据库

与之前相比:

4 、统⼀收单交易关闭(*)

4.1 、定义⽤⼾取消订单接⼝

不支付

在 A liPayController 中添加⽅法

4.2 、关单并修改订单状态

4.3 、调⽤⽀付宝接⼝

AliPayServiceImpl 中添加辅助⽅法

  /**
     * 关单接口的调用
     * @param orderNo
     */
    private void closeOrder(String orderNo){
        try {
            log.info("调用支付宝API,关单====>{}", orderNo);
            AlipayTradeCloseRequest request=new AlipayTradeCloseRequest();

            JSONObject bizContent = new JSONObject();
            bizContent.put("out_trade_no", orderNo);
            request.setBizContent(bizContent.toJSONString());

            AlipayTradeCloseResponse response=alipayClient.execute(request);

            if(response.isSuccess()){
                log.info("调用成功,返回结果 ===> " + response.getBody());
            } else {
                log.info("调用失败,返回码 ===> " + response.getCode() + ", 返回描述 " +
                        "===> " + response.getMsg());
                throw new RuntimeException("关单接口的调用失败");
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
            throw new RuntimeException("关单接口的调用失败");
        }
    }

4.4 、测试

注意:

1、针对⼆维码⽀付,只有经过扫码的订单才在⽀付宝端有交易记录。

2、针对⽀付宝账号⽀付,只有经过登录的订单才在⽀付宝端有交易记录。

不然

所以之前是我没有进行扫码,订单不存在,关单也就没有必要

重新扫码或者登录一下,但是别支付,然后取消

或者

【因为之前是走到else后,然后抛出异常,导致后面更新用户订单状态无法实现。

但是我们走到else分支后可以不抛出异常,让应用程序不会异常退出,那么这个时候应用程序就会从closeOrder方法中正常退出。正常退出后就会正常去执行我们closeOrder后正常的一个关闭订单流程。我们单纯的关闭这笔订单就行了,因为远程的支付宝是没有这笔订单的。这样就解决了远程支付宝没有订单的情况下我们关闭本地订单的问题。】

5 、统⼀收单线下交易查询(*)

5.1 、查单接⼝的调⽤

商⼾后台未收到异步⽀付结果通知时,商⼾应该主动调⽤《统⼀收单线下交易查询接⼝》,同步订单状 态。

( 1 ) AliPayController

( 2 ) AliPayService

 /**
     * 查询订单
     * @param orderNo
     * @return 返回订单查询结果,如果返回null则表示支付宝端尚未创建订单
     */
    @Override
    public String queryOrder(String orderNo) {
        try {
            log.info("查单接口调用====》{}", orderNo);
            AlipayTradeQueryRequest request=new AlipayTradeQueryRequest();
            JSONObject bizContent = new JSONObject();
            bizContent.put("out_trade_no", orderNo);
            request.setBizContent(bizContent.toJSONString());
            AlipayTradeQueryResponse response=alipayClient.execute(request);
            if(response.isSuccess()){
                log.info("调用成功,返回结果====》"+response.getBody());
                return response.getBody();
            }else{
                log.info("调用失败,返回码===》"+response.getCode()+",返回描述===》"+response.getMsg());
                //throw new RuntimeException("查单接口的调用失败");
                return null;//订单不存在
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
            throw new RuntimeException("查单接口的调用失败");
        }

    }

启动测试

1、代表支付宝端没有这个订单号,所以我们返回空

 2、支付宝端有这个订单号

 

 result中参数

{
  "alipay_trade_query_response": {
    "code": "10000",
    "msg": "Success",
    "buyer_logon_id": "luv***@sandbox.com",
    "buyer_pay_amount": "0.00",
    "buyer_user_id": "2088722039265040",
    "buyer_user_type": "PRIVATE",
    "invoice_amount": "0.00",
    "out_trade_no": "ORDER_20250113162451852",
    "point_amount": "0.00",
    "receipt_amount": "0.00",
    "total_amount": "0.01",
    "trade_no": "2025011322001465040505059184",
    "trade_status": "TRADE_CLOSED"
  },
  "sign": "JtZVEYU2w8QqO/k7KVeuic+OcLtgRUm57kXZbT4TXxMjydwIzhVcPYyBKlrIK6oSWilM7CGG56hcPuuz1rtbTup9DueErAgKyXeADgJXpf0qPQyn1Plw4/hhafxjNmzgqMSerX+BCmkCcwTzZLmZnCXyzc2OgUalq8jDbqHJgOn5J3QvHiv35ovFBtT8MAte5Kh+XtykUaEj953l1W0015jqEmfWWraDd8+PFDSIPuHbzkDgboBV6VNKGLvtzulW3myFzAJVlGgD3ypkGZOIiK7mW1h+tZu53YPTIkKLJXMQTrbq5Wu8gKGD0kU7D+yYrJlnob171vEpJ4cQFArDJw=="
}

5.2 、定时查单

先屏蔽掉微信的查单,避免干扰到支付宝的查单【因为目前逻辑全部未支付的订单,无论是微信还是支付宝,所以微信的查单会查到支付宝的订单号,后续会进行完善

( 1 )创建 AliPayTask

然后重新启动应用程序 【目前有两个未支付(支付宝),一个未支付(微信)】

 但是我们日志查的时候支付宝的定时任务也能查到微信的订单,

所以我们要向之前一样扩充一个支付类型字段

( 2 )修改 OrderInfoService

( 3 )修改 WxPayTask

注意之前的微信定时任务也要添加【退款通知目前先不管

  

重新启动测试

这时候只查到了两条记录

5.3 、处理查询到的订单

( 1 ) AliPayTask 在定时任务的 for 循环最后添加以下代码

 

( 2 ) AliPayService 核实订单状态

/**
     * 根据订单号查询支付宝支付查单接口,核实订单状态
     *
     * 1.如果订单已支付,则更新商户端订单状态,记录支付日志
     *
     * 2.如果订单未支付,则调用关单接口,关闭订单并更新商户端订单状态
     *
     * 3.支付宝多了一种情况,如果订单未创建,则直接更新商户端的订单状态就行了。
     * @param orderNo
     */
    @Override
    public void checkOrderStatus(String orderNo) {
        log.info("核实订单状态====>{}",orderNo);
        String result = this.queryOrder(orderNo);
        if(result==null){
            //3.支付宝多了一种情况,如果订单未创建,则直接更新商户端的订单状态就行了。
            //支付宝端未创建订单,则更新商户端的订单状态
            log.info("订单未创建====>{}",orderNo);
            orderInfoService.updateStatusByOrderNo(orderNo,OrderStatus.CLOSED);
        }
        //解析查单响应结果
        Gson gson = new Gson();
        HashMap<String, LinkedTreeMap> resultMap = gson.fromJson(result, HashMap.class);
        LinkedTreeMap linkedTreeMap = resultMap.get("alipay_trade_query_response");
        String tradeStatus = (String) linkedTreeMap.get("trade_status");

        if(AlipayTradeState.NOTPAY.getType().equals(tradeStatus)){
            log.info("订单未支付===》{}", orderNo);
           // 2.如果订单未支付,则调用关单接口,关闭订单并更新商户端订单状态
            this.closeOrder(orderNo);
            //更新商户端的订单状态
            orderInfoService.updateStatusByOrderNo(orderNo,OrderStatus.CLOSED);
        }
        if(AlipayTradeState.SUCCESS.getType().equals(tradeStatus)){
            //1.如果订单已支付,则更新商户端订单状态,记录支付日志
            log.info("核实订单已支付 ===> {}", orderNo);
            //如果订单已支付,则更新商户端订单状态
            orderInfoService.updateStatusByOrderNo(orderNo, OrderStatus.SUCCESS);
            //并记录支付日志
            paymentInfoService.createPaymentInfoForAlipay(linkedTreeMap);
        }

    }

然后根据文档

修改

 

最后启动测试3种情况

然后结果如下:【说明了两种情况,一种是支付宝端没有订单号的,我们这边超时后直接关闭

一种是支付宝有订单号,我们查单后,关闭订单

还有一种测试需要停掉内网穿透【因为远程支付成功了,本地没有支付成功】 

首先进行一下支付发现通知没有了,我已经支付成功了,但是本地状态没有修改

 而且这里有小bug,就是我可以取消订单,还能取消成功,但是实际调用关单接口是不能的

然后定时任务查询的都是未支付的订单,所以这笔订单状态修改为已支付是不可能了。

重新下一笔订单

等待2分钟【定时任务3种情况测试完毕】

 

 

6 、统⼀收单交易退款(*)

6.1 、退款接⼝

( 1 ) AliPayController

( 2 ) AliPayService

 /**
     * 退款
     * @param orderNo
     * @param reason
     */
    @Override
    public void refund(String orderNo, String reason) {
        try {
            log.info("调用支付宝API,退款====>{}", orderNo);
            //根据订单编号创建退款单记录、
            RefundInfo refundInfo=refundInfoService.createRefundByOrderNoForAlipay(orderNo,reason);
            log.info("调用退款API");
            AlipayTradeRefundRequest request=new AlipayTradeRefundRequest();
            JSONObject bizContent = new JSONObject();
            bizContent.put("out_trade_no", orderNo);

            BigDecimal refund = new BigDecimal(refundInfo.getRefund().toString()).divide(new BigDecimal("100"));
            bizContent.put("refund_amount",refund);
            bizContent.put("refund_reason",reason);//可选
            request.setBizContent(bizContent.toJSONString());

            //执行请求,调用支付宝接口
            AlipayTradeRefundResponse response=alipayClient.execute(request);

            if(response.isSuccess()){
                log.info("调用成功,返回结果 ===> " + response.getBody());
                //这里和微信不同,默认情况常规退款在我们支付宝中是同步返回结果
                //微信是异步返回结果【所以我们要通知退款的异步通知获取退款状态】
                //而支付宝当中如果是非银行卡的退款的话,那么是可以直接得到退款是否成功的结果。
                //所以我们能通过response.isSuccess()来进行判断,为true代表退款成功。
                //更新订单状态
                orderInfoService.updateStatusByOrderNo(orderNo,OrderStatus.REFUND_SUCCESS);
                //更新退款单
                refundInfoService.updateRefundForAliPay(
                        refundInfo.getRefundNo(),
                        response.getBody(),
                        AlipayTradeState.REFUND_SUCCESS.getType());//退款成功

            } else {
                log.info("调用失败,返回码 ===> " + response.getCode() + ", 返回描述 " +
                        "===> " + response.getMsg());
                //更新订单状态
                orderInfoService.updateStatusByOrderNo(orderNo,
                        OrderStatus.REFUND_ABNORMAL);
                //更新退款单
                refundInfoService.updateRefundForAliPay(
                        refundInfo.getRefundNo(),
                        response.getBody(),
                        AlipayTradeState.REFUND_ERROR.getType()); //退款失败
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
            throw new RuntimeException("创建退款申请失败");
        }

    }

6.2 、创建退款记录

6.3 、更新退款记录

 

然后重新启动测试:

1、我们先测试退款失败【可以制作一个退款失败,原金额是1分钱,我们退款2分钱】

 当然数据库也会记录

2、退款成功测试【还是刚刚那笔订单,不过我用swagger测试

 

退款成功

 

7 、统⼀收单交易退款查询(*)

电脑网站支付快速接入 - 支付宝文档中心

退款调用流程:

用户先访问商户系统,通过商户系统点击退款按钮,上面所说的。通过退款按钮调用支付宝的接口会给我们同步返回一个退款结果【这是和微信不一样的地方】。根据退款结果修改订单状态以及退款记录。

如果退款接口由于网络等原因返回异常,商户可调用退款查询接口,查询指定交易的退款信息【和交易查询接口差不多】

退款查询

( 1 ) AliPayController

( 2 ) AliPayService

如果退款请求的时候传了退款单编号,那么查询的时候就要传退款单编号。如果之前没有传,执行的是全额退款的话,这里out_request_no用订单号就行了。除非有多笔订单退款。

 /**
     * 退款查询
     * @param orderNo
     * @return
     */
    @Override
    public String queryRefund(String orderNo) {
        try {
            log.info("查询退款接口调用 ===> {}", orderNo);
            AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest();
            JSONObject bizContent = new JSONObject();
            bizContent.put("out_trade_no", orderNo);
            bizContent.put("out_request_no", orderNo);
            request.setBizContent(bizContent.toJSONString());
            AlipayTradeFastpayRefundQueryResponse response = alipayClient.execute(request);
            if(response.isSuccess()){
                log.info("调用成功,返回结果 ===> " + response.getBody());
                return response.getBody();
            } else {
                log.info("调用失败,返回码 ===> " + response.getCode() + ", 返回描述 " +
                        "===> " + response.getMsg());
                //throw new RuntimeException("退款查询接口的调用失败");
                return null; //订单不存在
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
            throw new RuntimeException("查单接口的调用失败");
        }
    
    }

启动测试 

全额退款的话,订单状态会变为 TRADE_CLOSED订单关闭。

{
  "alipay_trade_query_response": {
    "code": "10000",
    "msg": "Success",
    "buyer_logon_id": "luv***@sandbox.com",
    "buyer_pay_amount": "0.00",
    "buyer_user_id": "2088722039265040",
    "buyer_user_type": "PRIVATE",
    "invoice_amount": "0.00",
    "out_trade_no": "ORDER_20250114101957897",
    "point_amount": "0.00",
    "receipt_amount": "0.00",
    "send_pay_date": "2025-01-14 10:20:23",
    "total_amount": "0.01",
    "trade_no": "2025011422001465040505073496",
    "trade_status": "TRADE_CLOSED"
  },
  "sign": "H2zAcaHUUHKicNUibR9OR4N2RBGPQhVp0XwbTm5UaOtrhqCx6Xa6DmcshHFIe5Xlc2w2oSecf3NnxJ8hfDC09wasc9jkgwrU5s2pFC6mw6kpux8ApG9j8d/Q7JBcsbs4RJILyjcw6ixAiK+jZbSnx3joBPTRvIlkyencnTTnC8F6uU8CwVA8UkRA80h5bMvyTPs2J5pC8A+ai8YzUfsyFF85Tjfp9eIUJc6UKrUg5tzJcF6eEfj9zhXc8AWd7wBGFZeA++N67Lc7GPXm7jx6udCk+jpM+Iy3ckg3FtgR0i1oQCquprPkfjZ1s+dFIWW8t0rwlcSyHqsP0Ex+k85cZw=="
}

 

8 、收单退款冲退完成通知

退款存在退到银⾏卡场景下时,收单会根据银⾏回执消息发送退款完成信息。 开发流程类似⽀付结果通知。

参考支付结果通知进行开放

9 、对账(*)

查询对账单下载地址接⼝

交易账单、资金账单

 

( 1 ) AliPayController

( 2 ) AliPayService

 @Override
    public String queryBill(String billDate, String type) {
        try {
            log.info("调用支付宝API,查询账单====>{},{}", billDate, type);
            AlipayDataDataserviceBillDownloadurlQueryRequest request = new AlipayDataDataserviceBillDownloadurlQueryRequest();
            JSONObject bizContent = new JSONObject();
            bizContent.put("bill_type", type);
            bizContent.put("bill_date", billDate);
            request.setBizContent(bizContent.toJSONString());
            AlipayDataDataserviceBillDownloadurlQueryResponse response = alipayClient.execute(request);
            if (response.isSuccess()) {
                log.info("调用成功,返回结果 ===> " + response.getBody());
                //获取账单下载地址
               /* Gson gson=new Gson();
                HashMap<String, LinkedTreeMap> resultMap = gson.fromJson(response.getBody(), HashMap.class);
                LinkedTreeMap billDownloadUrlResponse = resultMap.get("alipay_data_dataservice_bill_downloadurl_query_response");
                String downloadUrl = (String) billDownloadUrlResponse.get("bill_download_url");*/
                //现在不用解析也行
                String downloadUrl = response.getBillDownloadUrl();

                return downloadUrl;
            } else {
                log.info("调用失败,返回码 ===> " + response.getCode() + ", 返回描述 " +
                        "===> " + response.getMsg());
                throw new RuntimeException("申请账单失败");
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
            throw new RuntimeException("申请账单失败");
        }
    }

然后启动测试

 

 

 

一共8个接口,除了收单退款冲退完成通知(与银行卡有关)完成了七个,目录中标注(*),

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值