支付宝支付架构
# 支付宝支付架构
## 相关知识点
### 对称加密
#### 对称加密是什么?
> 首先, 服务器会生成一个密钥, 然后将该密钥的副本传输给用户, 数据传输之前, 会用该密钥对数据进行加密, 数据到达后, 通过该密钥进行解密
#### 对称加密为什么不安全?
> **因为传输密钥的时候可能会被别人截取, 一旦被别人截取, 数据就很可能被恶意网关解密, 数据泄漏的同时, 也会造成传输过程中数据被恶意篡改**

### 非对称加密
#### 非对称加密是什么?
> 首先, 服务器会生成一个密钥对, 一个是公钥, 一个是私钥, 公钥用于传输, 私钥是需要做到绝对保密的, 将公钥传输给用户, 下次数据传输的时候(用户), 会使用公钥加密, 最后由服务器端的私钥进行解密, 公钥加密, 私钥解密, 私钥加密, 公钥解密, 公钥加密, 公钥不能解密, 私钥同理
#### 非对称加密为什么不安全?
> **因为有可能在公钥传输的时候, 恶意网关拦截真公钥, 将一个假公钥给了用户, 用户用假公钥加密, 恶意网关用假密钥解密, 然后用真公钥加密, 服务器用真密钥解密, 这样就被第三方恶意篡改数据了, 因此非对称加密也不安全**

> **公钥是给别人的, 私钥是给自己的**
### 支付宝的架构

> 简单来说, 用户创建一个密钥对, 将用户公钥传输给支付宝服务端, 而支付宝服务端给我们一个支付宝公钥, 即我们获取了支付宝公钥和用户私钥, 支付宝获取了支付宝私钥(这个几乎不可能被偷取)和用户公钥
> **数据传输的时候, 均用公钥加密, 私钥解密的形式, 但是, 单纯加密和解密一定会有问题, 如果用户的密钥对泄漏了, 支付宝服务端响应的数据可能会被恶意篡改**
> 最简单那的场景, 第三方通过使用泄漏的密钥对修改响应结果, 让用户一直支付, 造成用户财产损失
### 加签和验签
> 为了解决上述情况, 无论是哪一方, 发送数据的时候都要对原数据进行特殊的数据摘要(加签), 数据到达后, 通过密钥解密, 在进行数据摘要判断与之前的数据摘要结果比较, 如果没有改变则验签成功, 这样解决了数据篡改问题
### 为什么需要内网穿透?
> **因为当我们支付成功后, 无论是同步回调(支付成功, 需要跳转到某个页面), 还是异步回调(支付成功, 进行异步通知), 都是支付宝端发的请求, 即, 需要我的的应用能被外网访问, 因此, 我们需要内网穿透**
### 内网穿透原理


## 支付宝沙箱申请流程
> [密钥对文档](https://opendocs.alipay.com/common/02kipk?pathHash=0d20b438)
> **根据文档, 完成对服务器的密钥设置, 获取服务器的公钥, 最终, 我们需要获取到支付宝公钥和应用私钥**

> [下载Demo场景](https://opendocs.alipay.com/open/270/106291)
> 这个项目是一个eclipse项目, 我们需要把他转换成Idea项目, [参考博客](https://blog.csdn.net/qq_41799219/article/details/103931450#:~:text=%E6%95%99%E4%BD%A0%E4%BB%A5%E6%9C%80%E5%B9%B2%E5%87%80%E7%9A%84%E6%96%B9%E5%BC%8F%E7%94%A8IDEA%E6%89%93%E5%BC%80eclipse%E9%A1%B9%E7%9B%AE%201%E3%80%81%E5%8E%BB%E9%99%A4%E4%B8%8D%E5%BF%85%E8%A6%81%E9%A1%B9%E7%9B%AE%E6%96%87%E4%BB%B6,%E5%85%B6%E5%AE%9E%E5%88%B0%E8%BF%99%E9%87%8C%EF%BC%8C%E5%B0%B1%E6%98%AF%E6%8A%8Aeclipse%E7%9A%84%E6%89%80%E6%9C%89%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E5%8F%8A%E7%94%9F%E6%88%90%E7%9A%84%E6%96%87%E4%BB%B6%E9%83%BD%E5%88%A0%E9%99%A4%EF%BC%8C%E5%8F%AA%E7%95%99%E4%B8%8B%E9%A1%B9%E7%9B%AE%E7%9A%84%E4%BB%A3%E7%A0%81%E3%80%82%202%E3%80%81%E7%94%A8IDEA%E5%AF%BC%E5%85%A5%E9%A1%B9%E7%9B%AE)
> **配置成web项目**

> **配置项目Tomcat**

> **注意: 引入的是web**
> **修改配置并测试**
### Bug修复
#### 错误页面

>**这是因为支付宝网关出现了错误, 需要把两个网关都设置成沙箱环境**
#### 访问不了页面
>**这是因为没有配置web环境**
#### 启动问题
> 如果启动发现报无法解析编码等相关问题, 可以尝试更改页面编码,然后该回去, 再重启即可
## 内网穿透流程
1. [下载花生壳](https://hsk.oray.com/download)
2. **设置Demo环境的内网穿透**

3. 测试(成功)

## 导入项目流程
> **确保项目的编码是UTF-8(所有)**
```xml
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.34.0.ALL</version>
<exclusions>
<exclusion>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
```
```yaml
alipay:
app_id: 沙箱应用ID
merchant_private_key: 应用私钥
alipay_public_key: 阿里云公钥
notify_url: http://工程公网访问地址/alipay.trade.page.pay-JAVA-UTF-8/notify_url.jsp
return_url: http://工程公网访问地址/alipay.trade.page.pay-JAVA-UTF-8/notify_url.jsp
sign_type: RSA2
charset: utf-8
gatewayUrl: https://openapi-sandbox.dl.alipaydev.com/gateway.do
log_path: https://openapi-sandbox.dl.alipaydev.com/gateway.do
```
```java
@Configuration
@EnableConfigurationProperties(AlipayProperties.class)
public class AlipayTemplate {
@Autowired
private AlipayProperties alipayProperties;
public String pay(PayVO payVO) throws AlipayApiException {
//获得初始化的AlipayClient
AlipayClient alipayClient = new DefaultAlipayClient(alipayProperties.getGatewayUrl(),
alipayProperties.getApp_id(), alipayProperties.getMerchant_private_key(),
"json",
alipayProperties.getCharset(),
alipayProperties.getAlipay_public_key(),
alipayProperties.getSign_type());
//设置请求参数
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
alipayRequest.setReturnUrl(alipayProperties.getReturn_url());
alipayRequest.setNotifyUrl(alipayProperties.getNotify_url());
//商户订单号,商户网站订单系统中唯一订单号,必填
String out_trade_no = payVO.getOut_trade_no();
//付款金额,必填
String total_amount = payVO.getTotal_amount().toString();
//订单名称,必填
String subject = payVO.getSubject();
//商品描述,可空
String body = payVO.getBody();
alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
+ "\"total_amount\":\""+ total_amount +"\","
+ "\"subject\":\""+ subject +"\","
+ "\"body\":\""+ body +"\","
+ "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
//若想给BizContent增加其他可选请求参数,以增加自定义超时时间参数timeout_express来举例说明
//alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
// + "\"total_amount\":\""+ total_amount +"\","
// + "\"subject\":\""+ subject +"\","
// + "\"body\":\""+ body +"\","
// + "\"timeout_express\":\"10m\","
// + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
//请求参数可查阅【电脑网站支付的API文档-alipay.trade.page.pay-请求参数】章节
//请求
return alipayClient.pageExecute(alipayRequest).getBody();
}
}
```
```java
@ConfigurationProperties(prefix = "spring.alipay")
@Data
public class AlipayProperties {
// 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
private String app_id;
// 商户私钥,您的PKCS8格式RSA2私钥
private String merchant_private_key;
// 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
private String alipay_public_key;
// 服务器异步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
private String notify_url;
// 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
private String return_url;
// 签名方式
private String sign_type;
// 字符编码格式
private String charset;
// 支付宝网关
private String gatewayUrl;
// 支付宝网关
private String log_path;
}
```
```java
@Data
public class PayVO {
/**
* 订单号
*/
private String out_trade_no;
/**
* 应付总额: 必须保留两位小数, 否则会参数异常
*/
private BigDecimal total_amount;
/**
* 标题
*/
private String subject;
/**
* 备注
*/
private String body;
}
```
```java
@RestController
@RequestMapping("/alipay")
public class AlipayController {
@Autowired
private AlipayTemplate alipayTemplate;
/**
* 跳转支付页面
* @return
*/
@PostMapping("/toPay")
public String toPay(PayVO payVO) throws AlipayApiException {
payVO.setTotal_amount(payVO.getTotal_amount().setScale(2));
return alipayTemplate.pay(payVO);
}
}
```
> **最终引入依赖**
# Bug修复
## 工程里面的模块突然间和工程同级
> 解决方案很简单, 右键打开maven, 重新加载工程, 直到全部消失即可
## 又出现无法反序列化的问题
> **这个原因本质上是因为引入的支付依赖中存在fastJson的依赖, 这个依赖导致我们之前的底层逻辑改变了, 所以又出现了反序列化问题, 因此, 我们需要排除依赖**
```xml
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.34.0.ALL</version>
<exclusions>
<exclusion>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</exclusion>
</exclusions>
</dependency>
```
## 参数问题

> 这是因为应付总额的小数大于2, 所以, 参数异常, 把应付总额的小数变成2即可
# 其他知识
## 为什么跳转支付页面调用POST请求?
>** 因为如果按照老师发送GET请求, 仅仅会有订单号, 我们还需要调用其他微服务查询数据, 效率极低, 如果发送POST请求, 不仅对请求参数做出了保护, 而且数据可在请求域中获取, 效率极大的提高**