提交 7b3683d7 编写于 作者: 沉默王二's avatar 沉默王二 💬

支付对账

上级 3aa05a44
---
title: 如何解决头疼的支付掉单问题?
shortTitle: 抄答案就是了,两套详细的设计方案,解决头疼的支付掉单问题
author: 楼下小黑哥
category:
- 博客园
---
之前提到,支付过程会出现**掉单、卡单**的情况,这种情况对于用户来讲,体验非常差,明明自己付了钱,扣了款,但是订单却未成功。
前面我们简单说了下解决方案,这次就结合生产实际碰到的情况,给出两种详细设计的方案:
* 定时轮询补偿方案
* 延迟消息补偿方案
大家可以根据自己系统的实际情况,选择性参考。
## 定时轮询补偿方案
### 整体流程
这个方案主要采用定时任务,批量查询掉单记录,从而驱动查询具体支付支付结果,然后更新内部订单。
整体方案流程图如下:
![定时任务补偿](https://img2020.cnblogs.com/other/1419561/202101/1419561-20210107083620714-1094767745.jpg)
前三步流程没什么好说的,正常的支付流程,咱们针对后面几步具体详细说下。
第三步调用支付通道之后,如果支付通道端返回**支付受理成功或者支付处理中**,我们就需要调用第四步,将这类订单插入掉单表。
如果支付直接成功了,那就正常流程返回即可。
> 复习一下,网关类支付,比如支付宝、微信支付、网银支付,这种支付模式,支付通道仅仅返回支付受理成功,具体支付结果需要接收支付通道端的支付通知,这类支付我们将其称为异步支付。
>
> 相应的还有同步支付,比如银行卡支付,微信、支付宝代扣类支付,这类支付,同步就能返回支付结果。
第五步,补单应用将会定时查询数据库,批量查询掉单记录。
第六步,补单应用使用线程池,多线程异步的方式发起掉单查询。
第七步,调用支付通道支付查询接口。
重点来了,如果第七步支付结果查询为以下状态:
* **支付结果为扣款成功**
* **支付结果为明确失败**
* **掉单记录查询达到最大次数**
**第八步就会删除掉单记录。**
最后,如果掉单查询依旧还是处理中,那么经过一定的延时之后,重复第五步,再次重新掉单补偿,直到成功或者查询到达最大次数。
### 相关问题
**为什么需要新建一张掉单表?不能直接使用支付订单表,查询未成功的订单吗?**
这个问题,实际上确实可以直接使用的支付订单表,然后批量查询当天未成功的订单,补单程序发起支付查询。
*那为什么需要新建一张掉单表?*
主要是因为数据库查询效率问题,因为支付订单表每天都会大量记录新增,随着时间,这张表记录将会越来越多,越来越大。
**支付记录越多,批量范围查询效率就会变低,查询速度将会变慢。**
所以为了查询效率,新建一张掉单表。
这张表里仅记录支付未成功的订单,所以数据量就会很小,那么查询效率就会很高。
另外,掉单表里的记录,不会被永久保存,只是临时性。当支付结果查询成功,或者支付结果明确失败,再或者查询次数到达规定最大次数,就会删除掉单记录。
**这就是第八步为什么需要删除掉单表的原因。**
如果需要保存每次掉单表查询详情,那么这里建议再新增一张掉单查询记录表,保存每一次的查询记录。
针对这个方案,如果还有其他问题,欢迎留言。
### 方案优缺点
定时轮询补偿方案,最大的优点可能就是系统架构方案比较简单,比较容易实施。
那么这个方案的缺点主要在于**定时任务**上。
定时任务轮询方案天然会存在以下不足:
1. **轮询效率稍低**
2. 每次查询数据库,已经被执行过记录,仍然会被扫描(补单程序将会根据一定策略决定是否发起支付通道查询),有**重复计算**的嫌疑
3. **时效性不够好**,如果每小时轮询一次,最差的情况下,时间误差会达到1小时
4. 如果为了解决时效性问题,增加定时任务查询效率,那么 1 中查询效率跟 2 的重复计算问题将会更加明显。
## 延迟消息补偿方案
下面介绍另外一种掉单补偿方案,延迟消息补偿方案,这个方案整体流程与定时任务方案类似,最大区别可能在于,从一种**拉模式**变成一种**推模式**
整体方案流程图如下:
![](https://img2020.cnblogs.com/other/1419561/202101/1419561-20210107083620916-1904350166.jpg)
这个方案主要流程跟定时方案类似,主要区别在于第四步,第五步,第八步。
第四步的流程从插入掉单表变更为往**延迟队列发送掉单消息**
第五步,补单程序接收掉单消息,然后触发支付掉单查询。
第八步,如果第七步支付结果查询为以下状态:
* 支付结果为扣款成功
* 支付结果为明确失败
* 掉单记录查询达到最大次数
补单程序将会告知延迟队列消费成功,延迟队列将会删除这条掉单消息。
其他状态将会告知消费失效,延迟队列将会在一定延时之后,再次发送掉单消息,然后继续重复第五步。
### 延迟队列
这里的延迟队列需要自己实现,复杂度还是比较高的,这里给大家推荐几种实现方案:
第一种,基于 **Redis SortedSet** 实现延迟队列。可以参考一下有赞的实现方案[https://tech.youzan.com/queuing\_delay/](https://tech.youzan.com/queuing_delay/)
第二种,基于时间轮算法(**TimingWheel**)实现延迟队列,具体可以参考 Kafka 延时队列。
第三种,基于 **RocketMQ** 延迟消息。
前两种方案说起来还需要再开发,所以还是比较复杂的。
这里重点说下第三种方案,该方案是 **RocketMQ** 已经支持的特性,开箱即用,使用起来还是比较简单的。
RocketMQ 延迟消息支持 18 个等级,分别如下:
```shell
1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
```
消息发送方可以通过以下方式指定延迟等级,对应上方的延迟时间。
```java
Message#setDelayTimeLevel
```
消息消费方,如果消费失败,默认将会在消息发送方的的延迟等级基础上加 1。如果消息消费方需要指定其他的延迟等级,可以使用如下方式:
```java
ConsumeConcurrentlyContext#setDelayLevelWhenNextConsume
```
RocketMQ 延迟消息,支持的特性还是比较基础、简单,不支持自定义延迟时间。不过对于掉单补偿的这个场景刚好够用,但是如果需要自定义延迟的,那还是得采用其他的方案。
### 方案优缺点
延迟消息的方案相对于定时轮询方案来讲:
* 无需再查询全部订单,效率高
* 时效性较好
不过延迟消息这种方案,需要基于**延迟队列**,实现起来比较复杂,目前开源实现也比较少。
## 小结
支付掉单、卡单是支付过程中经常会碰到的事,我们可以采用异步补偿的方案,解决该问题。
异步补偿方案可以采用如下两种:
* 定时轮询补偿方案
* 延迟消息补偿方案
定时轮询补偿方案实现起来比较简单,但是时效性稍差。
而延迟消息补偿方案总体来说比较优秀,但是实现起来比较复杂。如果没有自定义的延迟时间的需求,可以直接采用 RocketMQ 延迟消息,简单快捷。
另外**延迟队列**使用场景还是比较多,不仅仅能用在掉单补偿上,还可以用于支付关单等场景。所以有能力开发的团队,可以开发一个通用的延迟队列、
>参考链接:[https://www.cnblogs.com/goodAndyxublog/p/14244458.html](https://www.cnblogs.com/goodAndyxublog/p/14244458.html),整理:沉默王二
---
title: Java NIO 学习笔记(五)
shortTitle: Java NIO 学习笔记(五)
author: 路径、文件和管道 Path/Files/Pipe
category:
- 博客园
---
**目录:**
[Java NIO 学习笔记(一)----概述,Channel/Buffer](https://www.cnblogs.com/czwbig/p/10035631.html)
[Java NIO 学习笔记(二)----聚集和分散,通道到通道](https://www.cnblogs.com/czwbig/p/10040349.html)
[Java NIO 学习笔记(三)----Selector](https://www.cnblogs.com/czwbig/p/10043421.html)
[Java NIO 学习笔记(四)----文件通道和网络通道](https://www.cnblogs.com/czwbig/p/10046987.html)
[Java NIO 学习笔记(五)----路径、文件和管道 Path/Files/Pipe](https://www.cnblogs.com/czwbig/p/10056126.html)
[Java NIO 学习笔记(六)----异步文件通道 AsynchronousFileChannel](https://www.cnblogs.com/czwbig/p/10056131.html)
[Java NIO 学习笔记(七)----NIO/IO 的对比和总结](https://www.cnblogs.com/czwbig/p/10056804.html)
# Path 接口和 Paths 类
Path 接口是 NIO2(AIO) 的一部分,是对 NIO 的更新,Path 接口已添加到 Java 7 中,完全限定类名是 java.nio.file.Path 。
Path 实例表示文件系统中的路径。 路径可以指向文件或目录,也可以是绝对的或相对的。在某些操作系统中,不要将文件系统路径与环境变量中的 path 路径相混淆。 java.nio.file.Path 接口与路径环境 path 变量无关。
在许多方面,java.nio.file.Path 接口类似于 java.io.File 类,但存在一些细微差别。 但在许多情况下,可以使用 Path 接口替换 File 类的使用。
### 创建 Path 对象
可以使用名为 Paths.get() 的 Paths 类(java.nio.file.Paths)中的静态方法创建 Path 实例,get()方法是 Path 实例的工厂方法,一个示例如下:
```java
public class PathExample {
public static void main(String[] args) {
// 使用绝对路径创建
Path absolutePath = Paths.get("D:\\test\\1.txt");
// 使用相对路径创建
Path relativePath = Paths.get("D:\\test", "1.txt");
System.out.println(absolutePath.equals(relativePath)); // ture
}
}
```
注意路径分隔符在 Windows 上是“\\”,在 Linux 上是 “/”。
Paths 类只有2个方法:
方法|描述|
---|---|
static Path get(String first, String... more)|将路径字符串或在连接时形成路径字符串的字符串序列转换为路径。|
static Path (URI uri)|将给定URI转换为路径对象。|
Path 接口部分方法:
方法|描述|
---|---|
boolean endsWith(Path other)|测试此路径是否以给定路径结束。|
boolean equals(Object other)|取决于文件系统的实现。一般不区分大小写,有时区分。 不访问文件系统。|
Path normalize()|返回一个路径,该路径消除了冗余的名称元素,比如'.', '..'|
Path toAbsolutePath()|返回表示该路径的绝对路径的路径对象。|
File toFile()|返回表示此路径的 File 对象。|
String toString()|返回的路径字符串使用默认名称分隔符分隔路径中的名称。|
# Files
NIO 文件类(java.nio.file.Files)为操作文件系统中的文件提供了几种方法,File 类与 java.nio.file.Path 类一起工作,需要了解 Path 类,然后才能使用 Files 类。
### 判断文件是否存在
```java
static boolean exists(Path path, LinkOption... options)
```
options 参数用于指示,在文件是符号链接的情况下,如何处理该符号链接,默认是处理符号链接的。其中 LinkOption 对象是一个枚举类,定义如何处理符号链接的选项。整个类只有一个 `NOFOLLOW_LINKS;` 常亮,代表不跟随符号链接。
### createDirectory(Path path) 创建目录
```java
Path output = Paths.get("D:\\test\\output");
Path newDir = Files.createDirectory(output);
// Files.createDirectories(output); // 这个方法可以一并创建不存在的父目录
System.out.println(output == newDir); // true
System.out.println(Files.exists(output)); // true
```
如果创建目录成功,则返回指向新创建的路径的 Path 实例,此实例和参数是同一个实例。
如果该目录已存在,则抛出 FileAlreadyExistsException 。 如果出现其他问题,可能会抛出IOException ,例如,如果所需的新目录的父目录不存在。
### 复制文件
一共有 3 个复制方法:
```java
static long copy(Path source, OutputStream out)
static Path copy(Path source, Path target, CopyOption... options)
static long copy(InputStream in, Path target, CopyOption... options)
```
其中 CopyOption 选项可以选择指定复制模式,一般是其子枚举类 StandardCopyOption 提供选项,有 3 种模式,第二个参数是可变形参,可以多个组合一起使用:
1. `ATOMIC_MOVE` :原子复制,不会被线程调度机制打断的操作;一旦开始,就一直运行到结束;
2. `COPY_ATTRIBUTES` :同时复制属性,默认是不复制属性的;
3. `REPLACE_EXISTING` :重写模式,会覆盖已存在的目的文件;
一个例子如下:
```java
Path sourcePath = Paths.get("D:\\test\\source.txt"); // 源文件必须先存在
Path desPath = Paths.get("D:\\test\\des.txt"); // 目的文件可以不存在
Files.copy(sourcePath, desPath); // 默认情况,如果目的文件已存在则抛出异常
Files.copy(sourcePath, desPath, StandardCopyOption.REPLACE_EXISTING); // 覆盖模式
```
注意:复制文件夹的时候,只能复制空文件夹,如果文件夹非空,需要递归复制,否则只能得到一个空文件夹,而文件夹里面的文件不会被复制。
# 移动文件/文件夹
只有 1 个移动文件或文件夹的方法:
```java
static Path move(Path source, Path target, CopyOption... options);
```
如果文件是符号链接,则移动符号链接本身,而不是符号链接指向的实际文件。
和移动文件一样,也存在第三个可选参数 CopyOption ,参考上述。如果移动文件失败,可能会抛出 IOException,例如,如果文件已存在于目标路径中,并且遗漏了覆盖选项,或者要移动的源文件不存在等。
和复制文件夹不一样,如果文件夹里面有内容,复制只会复制空文件夹,而移动会把文件夹里面的所有东西一起移动过去,以下是一个移动文件夹的示例:
```java
// 移动 s 目录到一个不存在的新目录
Path s = Paths.get("D:\\s");
Path d = Paths.get("D:\\test\\test");
Files.createDirectories(d.getParent());
Files.move(s, d);
```
和 Linux mv 命令一样,重命名文件与移动文件方式相同,移动文件还可以将文件移动到不同的目录并可以同时更改其名称。 另外 java.io.File 类也可以使用它的 renameTo() 方法来实现移动文件,但现在 java.nio.file.Files 类中也有文件移动功能。
### 删除文件/文件夹
```java
static void delete(Path path);
static boolean deleteIfExists(Path path); // 如果文件被此方法删除则返回 true
```
如果文件是目录,则该目录必须为空才能删除。
### Files.walkFileTree() 静态方法
删除和复制文件夹的时候,如果文件夹为空,那么会删除失败或者只能复制空文件夹,此时可以使用 walkFileTree() 方法进行遍历文件树,然后在 FileVisitor 对象的 visitFile() 方法中执行删除或复制文件操作。
Files 类有 2 个重载的 walkFileTree() 方法,如下:
```java
static Path walkFileTree(Path start,
FileVisitor<? super Path> visitor)
static Path walkFileTree(Path start,
Set<FileVisitOption> options,
int maxDepth,
FileVisitor<? super Path> visitor)
```
将 Path 实例和 FileVisitor 作为参数,walkfiletree() 方法可以递归遍历目录树。Path 实例指向要遍历的目录。在遍历期间调用 FileVisitor ,首先介绍 FileVisitor 接口:
```java
public interface FileVisitor<T> {
FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs) throws IOException;
FileVisitResult visitFile(T file, BasicFileAttributes attrs) throws IOException;
FileVisitResult visitFileFailed(T file, IOException exc) throws IOException;
FileVisitResult postVisitDirectory(T dir, IOException exc) throws IOException;
}
```
必须自己实现 FileVisitor 接口,并将其实现的实例传递给 walkFileTree() 方法。在目录遍历期间,将在不同的时间调用 FileVisitor 实现的 4 个方法,代表对遍历到的文件或目录进行什么操作。如果不需要使用到所有方法,可以扩展 SimpleFileVisitor 类,该类包含 FileVisitor 接口中所有方法的默认实现。
```java
Files.walkFileTree(inputPath, new FileVisitor<Path>() {
// 访问文件夹之前调用此方法
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
System.out.println("pre visit dir:" + dir);
return FileVisitResult.CONTINUE;
}
// 访问的每个文件都会调用此方法,只针对文件,不会对目录执行
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
return FileVisitResult.CONTINUE;
}
// 访问文件失败会调用此方法,只针对文件,不会对目录执行
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
return FileVisitResult.CONTINUE;
}
// 访问文件夹之后会调用此方法
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
return FileVisitResult.CONTINUE;
}
});
```
这四个方法都返回一个 FileVisitResult 枚举实例。FileVisitResult 枚举包含以下四个选项:
* CONTINUE : 继续
* TERMINATE : 终止
* SKIP\_SIBLINGS : 跳过兄弟节点,然后继续
* SKIP\_SUBTREE : 跳过子树(不访问此目录的条目),然后继续,仅在 preVisitDirectory 方法返回时才有意义,除此以外和 CONTINUE 相同。
通过返回其中一个值,被调用的方法可以决定文件遍历时接下来应该做什么。
### 搜索文件
walkFileTree() 方法还可以用于搜索文件,下面这个例子扩展了 SimpleFileVisitor 来查找一个名为 input.txt 的文件:
```java
Path rootPath = Paths.get("D:\\test");
String fileToFind = File.separator + "input.txt";
Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
String fileString = file.toAbsolutePath().toString();
System.out.println("pathString: " + fileString);
if (fileString.endsWith(fileToFind)) {
System.out.println("file found at path: " + fileString);
return FileVisitResult.TERMINATE;
}
return FileVisitResult.CONTINUE;
}
});
```
同理,删除有内容的目录时,可以重写 visitFile() 方法,并在里面执行删除文件操作,重写 postVisitDirectory() 方法,并在里面执行删除目录操作即可。
### Files 类中的其他方法
Files 类包含许多其他有用的函数,例如用于创建符号链接,确定文件大小,设置文件权限等的函数。有关java.nio.file.Files 类的详细信息,请查看 [JavaDoc](https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html)
# 管道 Pipe
Pipe 是两个线程之间的单向数据连接。管道有 source 通道和一个 sink 通道,将数据写入 sink 通道,就可以从 source 通道读取该数据。
以下是管道原理的说明:
![image](//upload-images.jianshu.io/upload_images/14923529-de26928e835ab0aa.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
### 使用管道进行读取数据
先看一个完整的例子:
```java
public class PipeExample {
public static void main(String[] args) throws IOException {
Pipe pipe = Pipe.open();
Pipe.SinkChannel sinkChannel = pipe.sink(); // sink 通道写入数据
String data = "some string";
ByteBuffer buffer = ByteBuffer.allocate(32);
buffer.clear();
buffer.put(data.getBytes());
buffer.flip(); // 反转缓冲区,准备被读取
while (buffer.hasRemaining()) {
sinkChannel.write(buffer); // 将 Buffer 的数据写入 sink 通道
}
Pipe.SourceChannel sourceChannel = pipe.source(); // 源通道读取数据
ByteBuffer readBuffer = ByteBuffer.allocate(32);
int bytesRead = sourceChannel.read(readBuffer); // 返回值代表读取了多少数据
System.out.println("Read: " + bytesRead); // Read: 11
System.out.println(new String(readBuffer.array())); // some string
}
}
```
如上代码,首先要创建管道,打开管道之后是使用同一个管道对象获取对应的 sink 通道和 source 通道的,这会自动地将两个通道连接起来,作为对比,在标准 IO 管道中是分别创建读管道和写管道,然后在构造器中或者使用`pipe1.connect(pipe2)` 方法来连接起来,如下:
```java
PipedOutputStream output = new PipedOutputStream();
PipedInputStream input = new PipedInputStream();
input.connect(output);
// 或者使用如下1行代码,可以代替上面2行代码来连接2个管道
//PipedInputStream input = new PipedInputStream(output);
```
>参考链接:[https://www.cnblogs.com/czwbig/p/10056126.html](https://www.cnblogs.com/czwbig/p/10056126.html),整理:沉默王二
---
title: 解读银行卡支付背后的原理
shortTitle: 解读银行卡支付背后的原理
author: 楼下小黑哥
category:
- 博客园
---
虽然现在我们主流的支付方式是使用支付宝/微信支付,但是当我们余额不足,或者选择从银行卡扣款时,将就会使用到银行卡支付。
银行卡支付可以将其分为线上支付与线下支付。其中线下支付分类就比较简单,就是我们平常在商城购物时,POS 机刷卡支付。
而线上支付分类就比较多了,根据银行卡类别,可以分为信用卡支付与借记卡支付。按照支付行为,我们又可以将其分为快捷支付,网银支付,Token 支付。
![](https://img2020.cnblogs.com/other/1419561/202005/1419561-20200519071024554-2009321892.jpg)
## 网银支付
首先我们来聊聊网银支付,这种方式在 10 年前,应该是最主流线上支付方式。
我们以电商购物为例,我们在网站上下单之后,选择银行卡支付通常会跳转到一个收银台页面。然后在收银台页面我们选择相关银行,点击到银行支付最后将会跳转到相应的银行页面。
> 这个收银台页面可能是商户的页面,也可能是支付机构的页面,这个跟网银支付对接模式有关。
跳转到银行页面之后,我们首先需要下载按照银行安全控件,这样我们才能输入银行卡的相关信息。其次我们还需要使用银行给的安全设备,比如 USB 盾,令牌器,令牌码等。
在银行网站支付成功之后,就可以点击返回同步跳回到电商的网站,整个流程如下图所示:
![网银支付流程](https://img2020.cnblogs.com/other/1419561/202005/1419561-20200519071024773-1530158438.jpg)
后台支付流程如下:
![](https://img2020.cnblogs.com/other/1419561/202005/1419561-20200519071025365-816049808.png)
可以看到网银支付整个链路非常长,任何一步都可能发生失败,所以支付成功率不会很高。另外有部分银行网银页面只能在 IE 中打开,而且还有可能是很老版本的 IE。再加上网银支付为了保证安全性,还需要使用 U 盾,安装安全插件。
这个过程说实话还是很复杂,还记得当年使用某行网银充值购买黄钻的时候,搞了一下午都没成功的,各种证书安装失败啥的。第一次在线充值,就这么失败告终。
感谢某行为我省下 10 元零花钱~
![](https://img2020.cnblogs.com/other/1419561/202005/1419561-20200519071025607-1336400658.jpg)
## 快捷支付
快捷支付,指的用户提供卡信息给电商等商户,商户会在后台将卡信息传递给支付机构,然后进行协议绑定。一旦绑定成功,下次支付,无需再让用户提供卡号等信息。
还是以电商购物支付为例,首次支付,需要经历绑卡过程。
![](https://img2020.cnblogs.com/other/1419561/202005/1419561-20200519071025890-1003361164.png)
扣款成功之后,前往银行 APP 可以查到该卡与支付机构绑定记录。
![](https://img2020.cnblogs.com/other/1419561/202005/1419561-20200519071026203-1746966302.jpg)
历次在电商网站下单支付时,由于电商网站已保存记录,所以无需再输入卡信息。历次支付流程如下:
![](https://img2020.cnblogs.com/other/1419561/202005/1419561-20200519071026374-1493881344.jpg)
上图展示历次支付过程还需要输入验证码的情况,这一步其实还可以做到一定额度的免密支付。
快捷支付接口一般可以归为两类:
* 签约/支付
* 代扣支付
### 签约/支付
签约/支付需要分为两个步骤:
* 签约申请/签约验证
* 支付
签约过程需要传入银行卡信息,银行卡号,户名,身份证号,手机号,信用卡的话可能还需要传入 cvv2 以及有效期。这个过程主要是为了鉴权,校验银行卡信息的正确性。
一旦支付机构/银行端信息校验成功,将会下发短信。用户回填短信,就代表同意开通快捷支付,建立绑定关系。绑定成功之后,支付机构将会返回给商户**协议号**
支付过程,商户就可以拿着协议号进行扣款。
整个后台流程如下所示:
![](https://img2020.cnblogs.com/other/1419561/202005/1419561-20200519071026578-964724064.jpg)
### 代扣支付
代扣支付的过程相比签约/支付就比较简单,每次直接上送银行卡信息,就可以直接扣款。代扣支付原则上可以做到整个过程无密支付,即不需输入验证码,完成扣款。
流程较为简单,详情可以参考快捷支付支付过程。
相比于签约/支付过程,代扣支付看起来更快捷,但是这种方式安全风险就会比签约支付大,可能就会出现盗刷现象。原本代扣接口本应适用于水电煤等扣费场景,但是发展过程一度被用于金融支付等场景。
现在这类接口正在慢慢下线,正在被新的商业委托接口(类似于签约/支付)所代替。
虽然快捷支付支付体验好,整个流程无需跳转到银行页面,支付过程不会被打断,支付成功率高。
但是易用跟安全性,永远都是矛盾。由于这个过程用户向商户提供银行卡相关信息,这些数据如果一旦被窃取,资金就可能会被盗取。另外,快捷支付,手机验证码可能是最后一道防线,手机如果丢失,那么银行卡资金也可能被盗取。
## 银行支付相关问题
总得来说,对接银行卡支付渠道,整个过程不是很难的,无非就是按照接口文档,拼接参数,然后做一些相应的调试。但是这个过程有些点需要特别注意。
### 加签/验签
银行卡支付一般通过互联网传输,这个过程为了防止报文被串改,通常会采用 RSA2 ,国密等加密算法加密报文,得到签名串,然后一起上送给支付机构。
支付机构方会进行相应的验签,验签失败,就会驳回支付请求,这样可以有效保证支付请求是从合法商户发起。所以对于商户来说,一定要保存好相应公私钥,不要随意泄漏。
另外,对于支付请求的响应信息/网银结果异步通知,支付机构端也会进行加签。商户端一定要进行**验签**,只有验签通过才能进行下一步。
> ps:发送请求由于不加签,交易无法进行,所以这一步肯定会做的。
>
> 但是返回信息你不进行验签,也能处理报文,这个可能就会被忽略。
>
> 我第一次对接相关支付渠道的时候,嫌麻烦,就没进行验签。现在想想,真的是心大。。。
### 终态判定
对于快捷支付这类同步接口,对于支付接口请求响应消息,我们需要判定请求是否成功,需要根据接口返回的响应码。有些接口也可能返回响应码与支付状态,那么我们就需要根据两者结合起来一起判断。
这个过程,不是说除了成功的响应码之外,其他都算失败。我们需要根据相关的接口文档进行相应的分类,有些如余额不足,卡要素不正确等错误码,当然可以明确归类为失败。
但是比如一些处理中,或者系统异常等返回码,这种无法明确到底是成功还是失败的,我们不能置为失败,需要结合支付查询或者异步通知结果,然后在做处理。
对于网银支付这类同步接口,这类只能等待渠道端的异步通知。一般来说,渠道端只会通知的成功的支付订单。
> 这个具体根据渠道端接口文档。
一般来说渠道异步通知接口,若没有给渠道端异步通知返回成功响应,该通知将会重复通知,直到到达一定次数或者得到成功的相应。
所以接受到异步通知之后,**一定要内部逻辑处理成功之后**,才能返回成功响应码给渠道端。这样即使内部逻辑处理错误,还能再次通过异步通知处理内部逻辑。
另外还需要注意内部处理逻辑的**幂等性**
### 请求参数相关
**支付金额**
请求过程一定要注意接口文档中支付**金额的单位**,是分,还是元。如果不注意单位,很有可能造成少收,多收的情况。
对于成功响应的信息,我们还需要注意校验上送金额与扣款金额(如果有返回的话)一致性。如果不一致,**一定不要将订单更新为成功,**及时人工介入查单。
最后支付渠道上线之后,还需要做一些真实扣款,比如小额 0.1,渠道最大额度测试。扣款成功之后,还要及时查看银行卡真实扣款金额是否与上送金额一致。
> 原因见下文。
**请求流水号(订单号)**
除了支付金额,我们还需要注意请求流水号/订单号唯一性,需要使用**唯一 id** 当做请求流水号,切勿使用时间戳等方式。
对于重复流水号,如果未成功,是允许重复支付的。如果成功,不允许再次支付的。但是也不乏有些机构接口没做好这部分校验。
举一个自己趟过的坑,一个几万的教训。之前对对接过某银行的系统,测试的时候为了方便,直接采用时间戳当流水号。
上线时未及时发现这个问题,某天恰好同一秒产生两笔流水号一样的单子,上送给银行。然后对方返回两笔都收款成功,但是第二天对账时发现仅收到一笔单子的资金。所幸最后通过人工追回这笔资金,不然当时卖了我,也赔不起啊。。。
![](https://img2020.cnblogs.com/other/1419561/202005/1419561-20200519071026753-560207240.jpg)
虽然这个例子银行端肯定也是存在问题的,未做防重处理,但是只要我们做好唯一流水号的逻辑,也能避免该问题。
### 真实惨痛例子
上面注意的问题聊了这么多,其实想引起对接渠道技术同学注意。不要片面认为支付机构或银行等系统很稳,不会有问题。
程序毕竟是人写的,一次升级改动,就有可能引起血崩。
![](https://img2020.cnblogs.com/other/1419561/202005/1419561-20200519071026886-1044429272.jpg)
所以不要过分相信对方系统的稳定性,我们能做的就是做好我们自己系统的稳定性,加入各种参数校验,尽量降低风险的发生。
给大家举几个惨痛的例子:
曾经对接过某银行,小额测试,完全没问题。但是我们在测试限额的时候,比如说限额 1000 元,我们测试 1000.01 的时候,讲道理这笔支付应该会失败。
但是这笔扣款成功了,并且查看银行扣款记录,仅仅只扣了 0.01 。
看到这个,你是否有很多问号???这 TM 竟然发生限额溢出。。。
![](https://img2020.cnblogs.com/other/1419561/202005/1419561-20200519071027195-1776107807.jpg)
哎,这种问题,只能紧急下线该渠道,然后等待银行端修复。
最后再举几个来自网上的例子,关于支付的漏洞。
地址:[https://wooyun.js.org/drops/](https://wooyun.js.org/drops/)
![来源:https://wooyun.js.org/drops/在线支付逻辑漏洞总结.html](https://img2020.cnblogs.com/other/1419561/202005/1419561-20200519071027768-1101605862.png)
## 总结
今天我们主要聊了下银行卡支线上支付的两种主流模式,快捷支付与网银支付。
快捷支付目前是现在最主流银行卡支付方式,因为使用体验最好,支付流程不易被打断。但是该模式相对来说安全性较低。不过现在支付机构端与银行端会有相应的风控手段,大家不用过分担心。
另外一点快捷支付,一般额度较小,通常最高额度可能只有几万。
所以对于支付金额较大的场景,只能采用网银支付这种方案。
最后聊了下银行卡支付对接过程中一些问题,有些例子,可以集成到测试案例中。每当对接一个渠道时,就可以按照案例执行。
>参考链接:[https://www.cnblogs.com/goodAndyxublog/p/12914676.html](https://www.cnblogs.com/goodAndyxublog/p/12914676.html),整理:沉默王二
---
title: 钱被扣走了,但是订单却未成功!支付掉单该怎么办?
shortTitle: 钱被扣走了,但是订单却未成功!支付掉单异常最全解决方案
author: 楼下小黑哥
category:
- 博客园
---
## 掉单异常
一个最常见的支付平台架构关系如下所示:
![](https://img2020.cnblogs.com/other/1419561/202010/1419561-20201012083859085-1227418818.jpg)
> 上图我们是站在第三方支付公司支付角度,如果是自己公司的内部支付系统,那么外部商户这一块其实就是公司内部一些系统,比如说订单系统,而外部支付渠道其实就是第三方支付公司
我们以携程为例,在其上面发起一笔订单支付,将会经过三个系统:
1. 携程创建订单,向第三方支付公司发起支付请求
2. 第三方支付公司创建订单,并向工行发起支付请求
3. 工行完成扣款操作,返回第三方支付公司
4. 第三方支付完成订单更新并返回携程
5. 携程变更订单状态
上面的流程,简单如下图所示:
![](https://img2020.cnblogs.com/other/1419561/202010/1419561-20201012083859313-868801941.jpg)
在这个过程就可能会碰到,用户工行卡已经扣款,但是携程订单却还是待支付,我们通常将这种情况称为**掉单**
上述掉单的场景,多数是因为**③、⑤**环节信息丢失导致,这种掉单我们将其称为**外部掉单**
还有一种极少数的情况,收到 **③、⑤**环节返回信息,但是在**④、⑥**环节内部系统更新订单状态失败,从而导致丢失支付成功的信息,这类掉单由于是内部问题,我们通常将其称之为**内部掉单**
## 外部掉单
外部掉单是因为没有收到对端返回信息,这种情况极有可能是网络问题,也有可能对端处理逻辑太慢,导致我方请求超时,直接断开了网络请求。
### 增加超时时间
对于这种情况,第一个最简单的解决办法,**适当的增加超时时间**
不过这里需要注意了,在我们增加网络超时时间之后,我们可能还需要调整整个链路的超时时间,不然有可能导致整个链路内部差事从而引起内部掉单。
> 画外音:对接外部渠道,一定要**设置网络连接超时时间与读取超时时间**。
### 接收异步通知
第二个办法,接收渠道异步回执通知信息。
一般来说,现在支付渠道接口我们都可以上送一个异步回调地址,当渠道端处理成功,将会把成功信息通知到这个回调地址上。
这种情况下,我们只需要接收通知信息,然后解析,再更新内部订单状态。
![支付系统异常处理-支付异步通知](https://img2020.cnblogs.com/other/1419561/202010/1419561-20201012083859492-1898161292.jpg)
这种情况下,我们需要注意几点:
1. 对于异步请求信息,一定需要对通知内容进行签名验证,并校验返回的订单金额是否与商户侧的订单金额一致,防止数据泄漏导致出现“假通知”,造成资金损失。
2. 异步通知将会发送多次,所以异步通知处理需要幂等。
### 掉单查询
有的渠道可能没有提供异步通知的功能,只提供了订单查询的接口,这种情况下,我们只能使用第三种解决办法,定时掉单查询。
我们可以将这类超时未知的订单的单独保存到掉单表,然后定时向渠道端查询订单的状态。
若查询成功或者明确失败(比如订单不存在等),可以更新订单状态,并且删除掉单表记录。
若查询依旧未知,这时我们需要等待下次查询的结果。
![支付系统异常处理-定时查询](https://img2020.cnblogs.com/other/1419561/202010/1419561-20201012083859615-1762843903.jpg)
这里我们需要注意了,有些情况下,有可能无法查询返回订单的状态,所以我们需要设置订单查询的最大次数,防止无限查询浪费性能。
### 对账
最后,极少数的情况下,订单查询与异步通知都无法获取的支付结果,这就还剩下最后一种兜底的解决办法,对账。
如果第二天渠道端给的对账文件有这一笔支付结果,那么我们可以根据这个记录更新直接更新我们内部支付记录。
> 画外音:稳妥一点,可以先发起查询,然后根据查询结果更新订单记录。
>
> 不过有些极端情况,查询无法获取结果,那么直接更新内部记录即可。
那如果第二天也没有这笔记录的结果,这种情况下,我们可以认为这笔是失败的。如果用户被扣款,渠道端内部将会发起退款,将支付金额返回给用户。所以这种情况可以无需处理。
## 内部掉单异常
### 支付公司内部订单关系
接下来我们讲下内部掉单异常,首先我们来看下为什么会发生内部掉单的异常,这其实跟我们系统架构有关。
![](https://img2020.cnblogs.com/other/1419561/202010/1419561-20201012083859756-1011257296.jpg)
如上图随所示,第三方支付公司内部表通常为支付订单与渠道订单这样一种 1 比 N 的关系。
支付订单保存着外部商户系统的订单号,代表第三方支付公司内部订单与外部商户的订单的关系。
而渠道订单代表着第三方支付公司与外部渠道的关系,其实对于外部渠道系统来讲,第三方支付公司就是一个外部商户。
为什么需要设计这种关系那?而不是使用下面这种 1 对 1 关系的那?
![](https://img2020.cnblogs.com/other/1419561/202010/1419561-20201012083859966-155602254.jpg)
如果我们使用上图 1 对1 的订单关系,如果第一次支付支付失败,外部商户可能会再次使用相同订单号对第三方支付公司发起支付。
这时如果第三方支付公司也拿相同的内部订单去请求外部渠道系统,有可能外部渠道系统并不支持同一订单号再次请求。
那其实我们也有其他办法,生成一个新的内部单号,更新原有支付订单上内部记录,然后去请求外部渠道系统。但是这样的话就会丢失上次支付失败记录,这就不利于我们做一些事后统计了。
那其实第三方支付公司也可以不支持相同的订单号再次发起请求,但是这样的话,就需要外部商户重新生成的新的订单号。
这样的话,第三方支付公司是系统是简单了,全部复杂度都交给了外部商户。
但是现实的情况,很多外部商户并不是那么容易更换生成新的订单号,所以一般第三方支付公司都需要支持同一外部商户订单号在未成功的情况下,支持重复支付。
在这种情况下,就需要我们上面的 1:N 的订单关系图了。
### 内部掉单异常的原因
当我们收到外部渠道系统的成功的返回信息,成功更新了渠道订单表的记录。但是由于渠道订单表与支付订单表可能不是同一个数据库,也有可能两者并不在同一个应用中,这就有可能导致更新支付订单表的更新失败。
![](https://img2020.cnblogs.com/other/1419561/202010/1419561-20201012083900191-2087810427.jpg)
由于支付订单是表保存着外部商户订单与内部订单关系,支付订单未成功,所以外部商户也无法查询得到成功的支付结果。
此时渠道订单表已经成功,所以上面外部掉单的方法并不适用内部掉单。
### 内部掉单异常解决办法
**第一种解决办法,分布式事务。**
内部掉单异常,说白就是因为支付订单表与渠道订单表无法使用数据库事务保证两者同时更新成功或失败。
那么这种情况下,我们其实就需要使用分布式事务了。
不过我们没有采用这种分布式事务,一是因为之前开发的时候市面上并没有开源成熟分布式事务框架,第二自己自己开发难度又很大。
所以对于分布式事务这一块,并没有什么使用经验。如果有使用分布式事务解决这类的问题同学,留言去可以评论一下。
**第二种解决办法,异步补偿更新。**
当发生内部掉单的情况,即更新支付订单失败等情况,可以将这里支付订单保存到一张内部掉单表。
但是这里可能会有一个问题,我们无法保证保存到内部掉单表这一步骤也一定成功。
所以说,我们还需要定时查询,查询一段时间内支付订单未成功,而渠道订单表已成功的支付订单记录,然后也将其插入到内部掉单表。
另一个系统应用,只需要定时扫描内部掉单表,将支付订单成功,然后再删除内部掉单记录即可。
这里需要注意了,当支付订单表数据量很大之后,定时查询可能会慢,为了防止影响主库,所以这类查询可以在备库进行。
## 总结
今天主要介绍了支付系统中的掉单异常,这类异常往往会导致用户实际已经被扣钱,但是商户订单还是等待支付的情况。
这个异常如果没有很好处理,将会导致客户用户体验很不好,还有可能收到客户的投诉。
掉单的异常,通常可以外部系统与内部系统。而大部分的掉单都是因为外部系统导致,我们可以增加超时时间,掉单查询,以及接受异步通知解决 99% 的问题,剩下 1% 的掉单只能通过次日的对账来兜底。
内部系统导致掉单异常是典型的分布式环境数据一致性的问题,这类问题我们可以不需要追求强一致性,只要保证最终一致性即可。我们可以使用分布式事务解决这类问题,也可以定时扫描状态不一致的订单,然后在做批量更新。
>参考链接:[https://www.cnblogs.com/goodAndyxublog/p/13800796.html](https://www.cnblogs.com/goodAndyxublog/p/13800796.html),整理:沉默王二
---
title: 收款神器!解读聚合收款码背后的原理
shortTitle: 收款神器!解读聚合收款码背后的原理
author: 楼下小黑哥
category:
- 博客园
---
Hello,我是二哥呀!
继续跟大家分享一下支付相关的话题:**聚合收款码的支付原理**,这也是我这大半年来一直在做的项目。
微信/支付宝收款码大家应该不会陌生,线下小微商户收款大多使用这个,就比如下图。
![](https://img2020.cnblogs.com/other/1419561/202009/1419561-20200928084835878-1900620469.jpg)
这种收款方式很方便,微信、支付宝后台申请开通,然后还可以免费申请相关物料。
![](https://img2020.cnblogs.com/other/1419561/202009/1419561-20200928084836141-145624609.jpg)
不过这种方式用户体验其实不是很好,之前有好几次拿出支付宝,却扫了微信支付码。
另外,这种个人的收款码通常还有单日收款的上限,比如支付宝单日上限 500元。
有了需求,自然会有聪明人人想到解决方案,于是有了聚合收款码产品解决方案,如下图。
![](https://img2020.cnblogs.com/other/1419561/202009/1419561-20200928084836293-1865437191.png)
一个收款码,支持多种客户端,主流是微信、支付宝,现在常见还会支持银联,QQ 等。
用户选择任一支持的客户端扫码,都能完成支付,再也不用纠结扫错码的尴尬。
有没有很神奇?其实底层原理很简单,看完你就明白了,下面就让二哥带你解密聚合收款码的底层原理。
## 微信相关支付方式
聚合收款码底层支付其实还是离不开微信、支付宝支持的支付方式,所以我们先从微信支付宝渠道出发,简单介绍这个过程将会使用的支付方式。
打开[微信支付官网](https://pay.weixin.qq.com/wiki/doc/api/index.html),可以看到很多支付方式。
![](https://img2020.cnblogs.com/other/1419561/202009/1419561-20200928084836565-788134085.png)
其中付款码支付在前两篇文章完整介绍过,这里不再介绍,感兴趣的小伙伴可以回头去看看这两篇文章。
![](https://files.mdnice.com/user/3903/44920c8c-bcdc-472e-b26d-7d35ef98ac73.png)
首先我们介绍一下**微信Native支付**,引用微信官网的解释:
> Native支付是商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式。该模式适用于PC网站支付、实体店单品或订单支付、媒体广告支付等场景。
简单来讲就是商户后台调用微信支付接口,微信返回预支付交易的链接,格式如下:
```
weixin://wxpay/bizpayurl?sr=123456
```
然后商户将其转为二维码,提供给客户使用微信扫码支付。
![](https://img2020.cnblogs.com/other/1419561/202009/1419561-20200928084836698-1766666089.jpg)
这种支付方式可以应用在 PC 网站购物场景,比如说英雄联盟官网购买相关游戏道具:
![](https://img2020.cnblogs.com/other/1419561/202009/1419561-20200928084836847-1798138956.jpg)
既然**微信Native支付**最后可以变成二维码完成支付,那么聚合收款码是不是可以采用**微信Native支付**这种支付方式呢?
答案是可以,但是不适合,产品体验不太好。
最好使用微信支付另外一种支付产品**JSAPI 支付**
至于原因,不要急,接下去看就会明白。
**JSAPI 支付**,又被称为公众号支付,名词解释引用一下官网介绍:
> JSAPI 支付是用户在微信中打开商户的 H5 页面,商户在 H5 页面通过调用微信支付提供的 JSAPI 接口调起微信支付模块完成支付。
具体业务流程如下:
![](https://img2020.cnblogs.com/other/1419561/202009/1419561-20200928084837003-1217243690.jpg)
日常生活中,很多应用场景使用这种支付方式,比如说:极客时间公众号上购买课程
![](https://img2020.cnblogs.com/other/1419561/202009/1419561-20200928084837175-1741244885.jpg)
这种支付方式相对于**微信Native支付**,比较麻烦,还需要使用微信公众号登录授权功能,以此获取用户的 openid。
![](https://img2020.cnblogs.com/other/1419561/202009/1419561-20200928084837323-1269436908.jpg)
另外当我们调用**微信 JSAPI** 后台接口,拿到微信返回的相关参数之后,我们还需要使用**微信的 JSSDK**,这样才能唤起微信支付。
## 聚合收款码核心原理
了解完聚合支付的所需要的底层支付方式,下面我们来了解一下聚合收款码的核心原理。
聚合收款码业务流程如下:
![](https://img2020.cnblogs.com/other/1419561/202009/1419561-20200928084837592-1240017064.jpg)
第一步,用户使用微信/支付宝 APP 扫码之后,将会打开一个收银台页面。
这个收银台页面可以自适应,不同 APP 显示不同的样式,比如支付宝打开收银台显示支付宝的 logo,微信打开就会显示微信的 logo。
第二步,用户在收银台输入金额,点击支付之后将会唤起 APP 的支付弹窗。
好了,观察这个流程,我们可以发现扫码之后,后台应用需要识别出当前 APP 到底是微信还是支付宝。
那如何判断当前使用的 APP 呢?
其实这个原理很简单,在支付宝/微信打开一个链接,实际将会使用内置的浏览器发起了 HTTP 请求,而 HTTP 的请求头将会携带 **User-Agent(UA)**,用来标识用户代理软件的应用类型、操作系统、软件开发商以及版本号。
微信/支付宝中浏览器发起 HTTP 请求,携带的 **User-Agent** 分别为:
```
支付宝
UCBrowser/11.5.0.939 UCBS/2.10.1.6 Mobile Safari/537.36 AliApp(AP/10.0.15.051805) AlipayClient/10.0.15.051805 Language/zh-Hans
微信
MQQBrowser/6.2 TBS 043220 Safari/537.36 MicroMessenger/6.5.8.1060 NetType/4G Language/zh_CN
```
这里需要注意了,不同型号的手机,不同的版本 APP,**User-Agent** 不一定会一样,其实我们只需要判断是否包含某些关键字即可,比如说只要 **User-Agent** 包含 **MicroMessenger** 就是微信,包含 **AlipayClient** 就是支付宝。
下面使用 Java 代码为例:
```java
String userAgent = request.getHeader("user-agent");
if (Objects.equals(userAgent, "AlipayClient")) {
// 支付宝
} else if (Objects.equals(userAgent, "MicroMessenger")) {
// 微信
}
```
这个问题解决之后,后面的流程就很简单了,只要调用微信/支付宝的 **JSAPI 支付**接口,拿到相关参数之后,唤起支付。
> 准确来讲,支付宝那边 JSAPI 支付官方名称为支付宝生活号支付。
这里解释一下上面的问题,为什么聚合收款码不能使用**微信Native支付**呢?
主要是因为**微信Native支付**接口返回是一个微信自定义 schema 协议,只能通过微信扫码打开,唤起支付。
如果聚合收款码使用**微信Native支付**,收银台提交金额之后,需要将微信返回交易链接转成二维显示在页面,然后用户使用微信内置识别二维码功能唤起支付。
这样一来比较影响产品体验,降低支付的成功率。
支付宝也有类似**微信Native支付**支付接口-**当面付扫码支付**,成功调用之后也会返回支付链接。
那这里可以给大家提个小问题,聚合收款码是否可以使用**支付宝当面付扫码支付**接口那?
答案是可以的,而且体验比**微信Native支付**好。
这是因为支付宝返回链接是一个标准 HTTP 连接,如下:
```
https://qr.alipay.com/xxxx
```
这个链接只要在支付宝内中打开,就可以唤起支付。
所以如果聚合收款码使用**支付宝当面付扫码支付**接口,收银台金额提交之后,当拿到支付宝返回的支付链接,应用程序内只要使用 HTTP 302 跳转到支付链接,就可以唤起支付宝支付。
> 画外音:之前我也一直以为支付宝跟微信一样,不能使用。
那这样实际上聚合收款码底层使用支付方式就有了两种方案:
* 微信 JSAPI 支付/支付宝生活支付
* 微信 JSAPI 支付/支付宝面付扫码支付
那如何选择那?
个人建议使用第一种方案,微信、支付宝都采用 JSAPI 支付。
主要是因为只要 302 跳转唤起支付宝支付,就会关闭我们收银台页面,这样一来整个微信支付与支付宝支付流程就不太一样了
其次,当用户支付成功之后,JSAPI 支付还可以跳转到一个成功页面,这个页面我们可以支付结果展示,或者骚一点,还可以挂些广告,或者引流其他公号上。
但是如果使用付宝面付扫码支付,支付完成之后,页面就被关闭了,就没办法完成支付页面跳转。
## 聚合收款码核心流程
介绍完原理,下面主要介绍一下市面上主流聚合收款码业务流程,其实聚合收款码可以分为三类:
* 静态聚合收款码
* 动态聚合收款码
* 银联静态二维码
静态聚合收款码就类似如下这种,需要用户主动输入金额,可以无限次使用。
![](https://img2020.cnblogs.com/other/1419561/202009/1419561-20200928084837726-948904977.jpg)
而动态聚合收款码是只能使用一次,并且由商家指定金额,用户只要扫码就可以支付指定金额。
这种应用场景比如 B 站购买大会员:
![](https://img2020.cnblogs.com/other/1419561/202009/1419561-20200928084837895-1482620524.jpg)
银联静态二维码其实功能上与静态聚合收款码差不多,但是它多了支持银联支付的功能。
除了这个以外,最主要的区别是银联静态二维码是银联发码,背后对应的地址是银联的地址,类似如下:
```http
https://qr.95516.com/00010000/xxx
```
### 静态聚合收款码流程
静态聚合收款码主要支付流程主要可以分为二步,第一步为登录授权。
![](https://img2020.cnblogs.com/other/1419561/202009/1419561-20200928084838095-1989649474.jpg)
这里的登陆授权一般使用微信、支付宝匿名登录授权功能,这样这个过程普通用户其实是无感知的。
> 画外音:如果是程序员的话,可能会感受到这个过程经过了多次跳转。
第二步,用户在收银台输入金额之后,应用内部将会创建相应的订单,然后再调用微信/支付宝的 JSAPI 支付。
![聚合收款码-JSAPI支付](https://img2020.cnblogs.com/other/1419561/202009/1419561-20200928084838224-844322765.jpg)
另外,如果支付宝采用面付扫码支付这种支付方式的话,那么其实不需要第一步登录授权了,可以直接跳到收银台发起支付。
![聚合收款码-支付宝 native 支付](https://img2020.cnblogs.com/other/1419561/202009/1419561-20200928084838382-1257375540.jpg)
### 动态聚合收款码流程
动态聚合收款码其实与静态收款码总体比较类似,只不过创建动态码内部已经创建了相应的订单,后续流程与静态聚合收款码差不多。
![聚合收款码-动态码内部创单](https://img2020.cnblogs.com/other/1419561/202009/1419561-20200928084838535-1889083978.jpg)
### 银联静态二维码流程
如果你使用微信、支付宝扫码打开银联二维码,将会打开我们自己收银台页面,后续流程其实跟静态聚合收款码一模一样的。
但是如果你使用支付银联支付的 APP 扫码,比如说各大银行的手机 APP,美团,京东等,就会在这些 APP 内各自支付页面,然后完成支付。
我们银联二维码的功能,将会在银联后台报备一个跳转地址,比如说
```
https://www.heihei.com
```
当用户使用微信/支付宝访问银联二维码,银联后台自己识别访问请求 **User-Agent** ,然后后台根据规则拼接重定向地址。
拼接规则如下:
```http
https://www.heihei.com?qrCode=URLENCODE(https://qr.95516.com/00010000/xxx)
```
![聚合收款码-银联二维码扫码流程](https://img2020.cnblogs.com/other/1419561/202009/1419561-20200928084838683-1907722064.jpg)
## 总结
聚合收款码统一了用户支付流程,提高商家的收款效率。
另外聚合收款码其实还可以跟商家后台一些 ERP 等软件打通,这样还提高的商家生产效率。
*不得不说,第一个设计出聚合收款码的的产品,真是个鬼才~*
聚合收款码,背后原理一点也不难,根据用访问请求的 **User-Agent** ,以此判断用户当前扫码使用的客户端类型。
然后调用微信/支付宝匿名登录获取用户 id,最后用户输入金额之后,调用微信/支付宝完成支付。
>参考链接:[https://www.cnblogs.com/goodAndyxublog/p/13743027.html](https://www.cnblogs.com/goodAndyxublog/p/13743027.html),整理:沉默王二
---
title: 一笔订单,但是误付了两笔钱!这种重复付款异常到底该如何解决?
shortTitle: 一笔订单,但是误付了两笔钱!这种重复付款异常到底该如何解决?
author: 楼下小黑哥
category:
- 博客园
---
之前,我们聊了聊支付过程中掉单的场景,用户明明付款成功,银行卡都扣款了,但是订单却还显示待付款。
今天,我们将聊到重复付款的异常,即同一笔订单,扣了用户两笔钱。
另外我们还将会提到另外一种异常,用户扣款成功,但是订单却支付失败的场景。
以上两种异常对于被扣款的用户来讲,使用体验极差,自己多付了钱,订单却还不成功。所以如果不及时处理这两类异常,那就真的等着被投诉吧。
## 重复付款异常
### 异常场景
重复付款异常一般常见于网银支付,微信支付,支付宝等这类需要跳转到一个支付网关页(网银支付),或者跳转到钱包 APP(支付宝、微信),从而异步完成扣款的支付场景。
![网银支付流程](https://img2020.cnblogs.com/other/1419561/202010/1419561-20201027082649240-968119571.jpg)
这种支付场景下,只能通过接受异步通知才能知道支付结果,我们一般将其称为异步支付。
> PS:有了异步支付,那么同步支付是什么?
>
> 其实同步支付指的就是调用支付接口之后,就可以立刻返回支付结果的,比如银行卡类快捷/代扣等支付就是同步支付。
>
> 当然也有一些奇葩的银行卡支付渠道,同步支付结果为受理成功,只能接受异步通知或者查询返回支付结果。
>
> 由于银行卡支付需要返回明确支付结果,对于这类渠道只能内部设计将异步转为同步。
后台支付流程如下:
![网关支付](https://img2020.cnblogs.com/other/1419561/202010/1419561-20201027082649633-999170298.jpg)
### 为什么会发生重复付款?
主要原因其实跟上次内部掉单异常一样,跟业务表设计有关。
上次我们提到,支付系统主要表结构如下:
![](https://img2020.cnblogs.com/other/1419561/202010/1419561-20201027082649843-787372831.jpg)
在这个表结构下,只要支付订单未成功,商户就可以重复使用其内部同一订单号调用支付接口。
假设这样一个场景,用户在收银台支付时选择招行进行网银支付,当他点击支付之后,商户系统将会调用支付公司的网银接口。
这时支付系统内部将会创建一笔支付单以及关联的渠道订单,并且调用招行系统的接口。
然后用户的浏览器页面将会打开一个新页面,然后跳转到招行网站。
这时如果此时用户再次在收银台点击支付,将会再次调用支付系统接口。这时候由于支付单已存在,所以仅仅会再创建一条渠道订单记录,并且调用招行系统的接口。这时用户的浏览器将会再次打开一个招行的网站。
*如果用户在两个招行网银页都完成支付,这时就发生了重复付款。*
上面这种场景看起来有点傻,但是真实用户操作真的会发生。除了这种,还有下面这种情况:
![](https://img2020.cnblogs.com/other/1419561/202010/1419561-20201027082650091-99479569.jpg)
### 解决办法
重复付款异常的主要的解决办法有两种,分为事前与事后。
事前主要的目是尽可能防止用户重复付款,主要解决办法为优化付款页面,尽可能做好提示。
**第一种优化方式,付款页面直接跳转到第三方/银行的网银页面,不要打开新的页面去跳转。**
![网银同步跳转](https://img2020.cnblogs.com/other/1419561/202010/1419561-20201027082650264-1951946928.gif)
这种方式可以防止用户误打开两个网银付款的页面,从而导致重复付款。
但是这里会有一个问题,银行网银页面付款成功之后,用户如何知道其在商户侧订单状态也成功了?
其实很简单,现在网银支付接口,一般都会有一个参数 **return\_url:同步跳转地址**
![来自支付宝开发文档](https://img2020.cnblogs.com/other/1419561/202010/1419561-20201027082650491-364220649.jpg)
只要在接口传入这个地址,当支付成功之后,页面最终就会跳转到这个传入的地址,商户侧就可以在地址显示订单是否支付成功。
![支付系统异常处理-同步跳转](https://img2020.cnblogs.com/other/1419561/202010/1419561-20201027082650641-2048104145.jpg)
上面我们提到,用户有可能会使用浏览器回退功能,跳转到支付页,从而导致重复付款。
对于这种情况,我们可以在其回退支付页时,首先向后台查询这笔订单支付结果,如果已支付成功,那就直接显示成功页面。
**第二种优化,对于这种重新打开一个页面跳转到银行网站,我们可以在页面加入弹窗提示,询问用户是否已支付完成。**
![](https://img2020.cnblogs.com/other/1419561/202010/1419561-20201027082650798-1142876603.png)
比如上面这种处理方式,当用户点击确认完成充值,可以马上向后台发起查询订单状态。
下面来聊聊事后的解决办法,**其实解决办法很简单,发起内部退款,将多余支付的一笔反向退款回去**
支付系统内部可以有个定时任务,定时扫描支付单下有多条成功渠道订单的记录,然后选择将重复支付渠道订单发起退款。
这种方式是支付公司系统内部的操作,不需要商户侧发起指令。
## 订单失效异常
### 异常场景
这种场景一般常见于电商购物,秒杀等购物场景。当用户下单之后,页面将会开始倒计时,用户需要在有效期内支付成功。
![](https://img2020.cnblogs.com/other/1419561/202010/1419561-20201027082651009-1400620650.jpg)
假设用户点击跳转到支付宝,但是其没有立刻支付,而是停留了很久,在订单最后一秒时间内完成了支付,但是这个时候订单早已因为时间到期而被自动取消。
这样就发生用户扣款已经成功,但是订单却是失败或关闭的场景的。
另外还有一种情况,用户在有效期内支付成功,但是因为网络、内部应用等问题,支付结果的异步通知过了很久才收到,这时内部订单的早因为时间到期而被取消。
### 解决办法
**第一种解决办法,上送有效期给支付渠道。**
一般支付接口都会有一个支付有效期的字段,表明这笔支付最晚可以支付的时间。如果超时未支付,这笔支付将会被关闭。
![来自支付宝开发文档](https://img2020.cnblogs.com/other/1419561/202010/1419561-20201027082651175-1888380115.jpg)
当然一般情况下,如果未上送,这个字段内部一般会有个默认的有效期,比如 3 天,这个时间就比较长了。
所以当调用支付接口时,可以将订单剩余有效期传入支付接口。这样用户如果在超时时间内未完成支付,支付将会失败。
**第二种解决办法,内部发起退款。**
这个解决办法依然事后托底的解决办法,对于支付订单已关闭,但是支付却成功的情况,发起内部退款,将钱退给用户。
内部可以有个定时任务,定时扫描支付订单已关闭但是支付却成功的情况,然后发起退款指令。
## 最后
最后用思维导图方式帮大家总结一下支付系统可能会碰到的异常。
![](https://img2020.cnblogs.com/other/1419561/202010/1419561-20201027082651334-28675286.jpg)
>参考链接:[https://www.cnblogs.com/goodAndyxublog/p/13882587.html](https://www.cnblogs.com/goodAndyxublog/p/13882587.html),整理:沉默王二
---
title: 一文带你了解微信/支付宝支付的相关概念
shortTitle: 一文带你了解微信/支付宝支付的相关概念
author: 楼下小黑哥
category:
- 博客园
---
## 微信
名词解释:
* **appid**:公众号、小程序、移动应用唯一标识
* **mchid**:商户申请微信支付后,由微信支付分配的商户收款账号。
* **openid**,微信用户在公众号 **appid** 下的唯一用户标识,可用于永久标记一个用户
* **sub\_appid**:子 **appid**,服务商模式/银行服务商模式起作用
* **sub\_mch\_id**:子商户号, 服务商模式/银行服务商模式起作用
* **sub\_openid**:子 **appid** 下的唯一标识
* **channel\_id**:服务商模式下,微信支付分配给收单服务商的ID
微信支付接入可以分为
* 普通商户
* 服务商模式
* 普通服务商
* 银行类服务商
银行类服务商与普通类服务商的最大区别在于,普通类服务商不能清算资金,微信将会直接把资金清算给商户账户。
![](https://img2020.cnblogs.com/other/1419561/202003/1419561-20200307182820919-1430456766.jpg)
而银行类服务商是可以为下属特约商户清算资金的。
![](https://img2020.cnblogs.com/other/1419561/202003/1419561-20200307182821261-1288199620.jpg)
> 现有银行类服务商实际上是通过银联/网联转接微信
银行服务商省申请资质要求较高,一般只有银行、支付机构可以申请成为银行类服务商。两者申请要求如下:
![](https://img2020.cnblogs.com/other/1419561/202003/1419561-20200307182821423-1778222315.jpg)
> 官网地址:[https://pay.weixin.qq.com/partner/public/home](https://pay.weixin.qq.com/partner/public/home)
### 普通商户
普通商户模式下,商户需要向微信申请 **appid** 以及 **mchid**。接入微信普通商户版本的支付接口。
文档地址:[https://pay.weixin.qq.com/wiki/doc/api/index.html](https://pay.weixin.qq.com/wiki/doc/api/index.html)
JSAPI 支付模式,需要提前登陆授权,需要获取用户的 **openid**。三者关系如下图:
![](https://img2020.cnblogs.com/other/1419561/202003/1419561-20200307182821561-1776141502.jpg)
同一个 **mchid** 可以绑定多个 **appid**,两者可以是同一个公司主体,也可以不同公司主体(**限定资格开放**)。**mchid** 最多可以绑定 50 个 **appid**
同一个 **appid** 又可以被多个 **mch** id 绑定,不过需要注意的是,**mchid** 费率必须不相同。
所以 **appid****mchid** 原则上是多对多的关系。
![](https://img2020.cnblogs.com/other/1419561/202003/1419561-20200307182821742-1850849746.jpg)
这里需要注意,微信清算资金实际上是基于 **mchid**。即同一个 mchid,使用多个 **appid** 做交易,第二天资金是清算到 **mchid** 绑定的商户的账户。
### 服务商模式
第三方服务商向微信申请自己的 **appid**,并通过 **appid** 申请服务商**mch\_id**,以此获得微信支付服务商能力。再通过服务商 **mch\_id** 为所服务的特约商户申请创建微信支付 **sub\_mch\_id**,创建好的 **sub\_mch\_id** 默认和服务商的**mch\_id** 建立父子授权关系。银行服务商模式可以通过接入入驻特约商户,而普通服务商只能通过页面入驻。
服务商模式下,可以配置特约商户使用自己申请 **appid**,传入 **sub\_appid** 字段。
![](https://img2020.cnblogs.com/other/1419561/202003/1419561-20200307182821945-514823327.jpg)
> 官方解释:
>
> **sub\_appid** 可为公众号,**APP **或小程序的 **appid**,服务商在服务商商户平台可手动为每个特约商户绑定与渠道商主体或特约商户主体一致的公众号,APP或小程序的appid;针对小程序,还支持绑定服务商主体的小程序 **appid**。目前最多配置5个. 手工配置路径:”服务商商户平台-服务商功能-子商户管理-开发配置-特约商户**appid** 配置
服务商模式下使用 JSAPI,若特约商户想基于自己 **appid** 做交易,可以基于特约商户 **appid** 登陆授权获取 **openid**,然后传入 **sub\_appid****sub\_openid**
若没传入,只能使用服务商 **appid** 登陆授权,获取 **openid**
### 总结
普通商户模式,需要商户自己开发团队对接微信。而服务商模式,一般有服务商开发自己的收款功能,特约商户入驻即可使用,无需二次开发,适合个人/小微企业。另外服务商也可以提供包装之后的接口,有开发的能力也可以接入。
### 微信帮助文档
[https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa\_api.php?chapter=7\_10&index=1#](https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_10&index=1#)
[https://pay.weixin.qq.com/static/pay\_setting/appid\_protocol.shtml](https://pay.weixin.qq.com/static/pay_setting/appid_protocol.shtml)
## 支付宝
名词解释:
* **app\_id**:支付宝分配给开发者的应用 ID
* 商户 **uid/pid**:支付宝商户号
* **buyer\_id**:买家的支付宝唯一用户号(2088开头的16位纯数字)
* **org\_pid**:银行服务商模式下,收单机构(例如银行)的标识,填写该机构在支付宝的 **pid**
* **merchant\_id**,银行服务商模式下,特约商户入驻成功之后,分配商户号
支付宝接入分为:
* 普通商户
* 服务商
* ISV:独立软件开发商
* 银行类服务商
支付宝银行类服务商与微信类似,服务商可以自己清算资金。
![](https://img2020.cnblogs.com/other/1419561/202003/1419561-20200307182822080-1840781475.jpg)
不过支付宝的这个模式比较简单,服务商只需要通过后台接口入驻特约商户成功,得到支付宝分配的商户编号。交易时上送商户编号与机构编码即成功。
### 普通商户
普通商户需要向支付宝申请入驻,将会得到商务号。接着需要创建应用得到 **appid**,上架成功后,需要进行签约。签约成功之后,这个 **appid** 与商户号建立唯一的绑定的关系。
同一个商户号可以绑定多个 **appid**,但是同一个 **appid** 只能绑定唯一个商户号。
> 其实从支付宝的接口也可以看出,支付宝只要求传入 **appid**,后台肯定是跟库 **appid** 查找对应的商户号。
>
> 这点虽然没有微信灵活,但是支付宝理解起来简单。微信 **appid** 与 **mchid** 多对多的关系,有点复杂。
![](https://img2020.cnblogs.com/other/1419561/202003/1419561-20200307182822284-1436718277.jpg)
### 银行类服务商
这里服务商直接触过银行类服务商,其他类型服务商不太清楚。
银行类服务商交易接口与普通商户模式相比,仅仅多出了 \*\*merchant\_id \*\***org\_pid**,其他信息与普通商户的接口一致。
### 总结
同微信。
> ps:虽然支付宝官网上文档倒是挺多的,但是就是没有找到说的很明白的文档。。。这一点微信还是挺舒服的,示例图画的很清晰,赞一个 ღ( ´・ᴗ・\` )比心。
### 帮助资料
[https://docs.open.alipay.com/200/105314/](https://docs.open.alipay.com/200/105314/)
[https://opendocs.alipay.com/isv/10272/ru478a](https://opendocs.alipay.com/isv/10272/ru478a)
>参考链接:[https://www.cnblogs.com/goodAndyxublog/p/12436075.html](https://www.cnblogs.com/goodAndyxublog/p/12436075.html),整理:沉默王二
---
title: 技术总监亲自上阵,手撸了个电商可视化面板,产品经理惊呆了。。。
shortTitle: 技术总监亲自上阵,手撸了个电商可视化面板,产品经理惊呆了。。。
description: 今天我们以电商项目为例,来聊聊电商项目的可视化面板需要包含哪些数据指标!
author: 老三
category:
- 微信公众号
head:
---
在我们平时做项目的时候,经常会遇到做可视化面板的需求,今天我们就以电商项目为例子,来聊聊电商项目的可视化面板需要包含哪些数据指标!
![](https://mmbiz.qpic.cn/mmbiz_png/CKvMdchsUwlqIArsbJQpdicibHTX0MZuBlpGybTx5OJbw2mwNBCxbZvlI0kgIMvCkL5jMnxAibG5A08nngVNJbhjA/640?wx_fmt=png)
不知道你的日常是否经历过这样的场景——
* 产品:我们这期要上线巴拉巴拉……
* 开发:为什么要做这个需求呢?
* 产品:因为这个需求,能给我们带来\*\*收益……总之,好处大大地有!
* 开发:……
![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWcMFJCnMvGcZJpr9suYfGYiceNFR4GkLHnj3OsM6U9g4pDicELm4YazGVOibql4YzODWytlViaIiakApwg/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1)
放心,有你好果子吃
对于我们开发而言,很多时候想的的是写很吊的代码,收益也似乎很好衡量:
* 接口平均响应时间降低了30%
* QPS提升了10%
* 节约了15%的机器
* 界面渲染时间降低20%
* 系统可用性提升一个9
……
但是更多时候,写的都是业务需求,一阵CRUD之后,只余深深的空虚……
![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWcMFJCnMvGcZJpr9suYfGYicPWDYyLNaFPUd1NEiaafXtBEfJXW8tuTEYKXkeh0EPy1FRwwhBIIkcow/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1)
秃然懵比
这节我们就来看看,电商的数据指标,了解我们肝的业务,收益在哪。
## 电商数据概览
> 事情是这样的,你是一个平平无奇的靓仔,某天窝在沙发上,刷着某音,突然一个漂亮的小姐姐映入眼帘,你忍不住点了进去,小姐姐的一顿操作让你头晕目眩,突然,视频的最后:“想要同款XX吗?点击评论区置顶链接!”
>
> 恰饭啊,你还是忍不住点开了链接,点击进去,原来是一家新的电商网站,模特还不错,刷刷看。
>
> 你浏览了首页的商品列表,没有找到想要的,你又在检索框,搜索“\*\*”,嘿嘿,这个好,你又忍不住点进详情页看了一下,刷了一下评论,你又发现有新人优惠券,买了!哦,还没注册,先注册,再购买,下单,付款,一气呵成,等着宝贝到了。
>
> 没什么其它事情,你又刷了一会,发现有些东西有点想买,但又不是特别想买,嗯,先加购物车吧。
>
> 过两天,你想起来,看看你的宝贝到哪了,哎,快到了,物流挺给力。看看购物车,你发现有些东西挺想要,选中购物车里的商品,下单、付款、躺平,你又接着刷小姐姐去了,嗯,这个小姐姐也有个链接,点进去看看……
看到上面这个,大家应该都知道对于电商而言,一个新用户购物的完整流程了。
![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWcMFJCnMvGcZJpr9suYfGYic4zXhjPohXWPxMuSeeeHq9Hf8kVMRkPb3T06t4I1bFENzdAV8uGJXfQ/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1)
电商新用户购物
那么从数据指标的角度来看这一套业务的流程,它又是什么样子呢?
![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWcMFJCnMvGcZJpr9suYfGYicFUfM3c6HVib1AJSY3ib6sgiaRavytBsjeuiaHZgHMquI0Wqwx9tia5icw6uQ/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1)
电商业务流程数据看点
1. **拉新**
拉新,最主要的方式就是广告,通过在各个渠道投放广告,像搜索引擎、抖音红人、公众号、微博等等。
通常需要关注每个渠道的拉新数量,和获客成本,比如CPA(单个注册成本)。
当然,拉新数据,更多的是市场部门在关注,产品关注更多的是人拉到网站后的数据。
2. **流量**
用户来了之后,就会浏览各个页面,比如从渠道进来的落地页,商城的首页,商品分类的列表页,搜索的结果页,商品的详情页。
这里需要关注,每个页面的UV、PV、浏览时长、点击率等等。
3. **转化**
用户光看看没啥用,挣不到钱,得看看用户是不是掏钱买,掏钱买的情况。
购买的几个关键点是:加购、下单、支付。
需要关注每个环节的人数,和从上一级下来的转化率,加购人数、加购率、下单人数、下单金额、支付单量、支付金额、客单价等等。
对于流量和转化,其实是呈一个漏斗形的,也就是所谓的`流量漏斗转化模型`
![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWcMFJCnMvGcZJpr9suYfGYicGPwhUALcB0TiahCNRiciaB9qtnls3rkDibR2fQxKBgPIic49kfMz6nZLG3g/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1)
流量漏斗转化模型
可以看到,每一级往下,都会过滤掉一些流量,所以也可以注意看一下,各个电商平台都在想法设法地缩减用户的购买流程,甚至抖音电商可以做到“一键购买”。
4. **复购**
好不容易,拉来一个新用户,当然是希望用户多复购,不能做一锤子买卖。这时候,就要关注用户活跃度、复购率这些指标。
除了这个主线上的流程,我们还得关注一些其它的指标,例如营销活动的触达率,风险订单的拦截率,用户的满意度等等,接下来我们来详细看看电商的数据指标。
## 电商数据详览
### 用户指标
用户是电商的核心,有人来了,把人留住,电商才能生存和发展。
我们来看看,有哪些需要关注的用户指标:
![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWcMFJCnMvGcZJpr9suYfGYicBagZJeuZl1tBHqeG9oiaSRko8WXQae9Fcibo3p8Xtu3cicMJm4G8Tfsgg/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1)
主要用户指标
* **注册用户数**:注册电商平台用户数
* **日增新用户**:每天注册用户的数量
* **活跃用户数**:登录了电商平台的用户,可以分为`日活跃用户`(日活:DAU)、`周活跃用户数`(周活:WAU)、月活跃用户(月活:MAU)
* **活跃率**:活跃用户/总用户数。
* **新用户数**:历史成交订单数为0的用户数
* **老用户数**:历史成交订单数大于0的用户数
* **复购用户数**:历史成交订单数大于1的用户数
* **沉默用户数**:距离上次登录平台大于30天,小于90天的用户数
* **流失用户数**:距离上次登录平台大于等于90天的用户数
* **留存率**:(第1天新增的用户中,第N天还在登录浏览的用户数)/第1天新增用户数
根据时间,留存率又分为次日留存率、第7日留存率、第30日留存率。
根据不同用户的区分,可以对用户进行分层精细化运营,比如新用户可以通过新人优惠券和push,促使其尽快完成首单,沉默用户可以通过邮件、优惠等等尝试召回。
留存率可以评估电商产品功能对用户的黏性,如果留存率过低,那就说明用户对电商产品的粘性低,就得想办法提高留存了。
### 流量指标
流量规模指标,就是看看用户浏览了什么,多少用户浏览了。
关于流量,首先要知道两个指标的定义:
* **PV**:访问次数,Page View,页面浏览次数,用户每打开一个网页可以看作一个PV,用户看了十个网页,那么PV为10。
* **UV**:独立访客数,Unique Visitor,值得的是不重复访问电商平台的人数,一个用户一天之内看了十个界面,也之算一个UV。
电商平台分为多级页面,包括首页、活动页、列表页、搜索页、商详页等等,需要关注这些页面的流量,来观测各个页面对用户的吸引度、流量漏斗中哪一环流失比较多等等。
* 首页
![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWcMFJCnMvGcZJpr9suYfGYicsx9Qr3ocvZPuiaKlP5J0DDx9kPLK2D6ibXeyNjFTPTBJ6XrFHiaqdXvLQ/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1)
首页
* 活动页
![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWcMFJCnMvGcZJpr9suYfGYicpBVRh8JndCYVgPC7A8LYHyCSrTibZld9d5qsHANw0IE9QQMXcjgxezQ/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1)
活动页
* 搜索页
![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWcMFJCnMvGcZJpr9suYfGYicicbiagoI8EbIorN7rNRFmNUAZq50pkpXHWxIKT8wEbHMcpPpJKkka9Jw/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1)
搜索页
* 商详页
![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWcMFJCnMvGcZJpr9suYfGYicuibL5E4wTgP6JU3gtVwoCow6NyC0kOsdZicB4dgqicdBH6b3r071xcYVg/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1)
商品详情页
* 下单页
![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWcMFJCnMvGcZJpr9suYfGYicNbQWqCG8J9rNLkjW9lBqmoyZSuUQ3Xia8MwgBLMe047odT3ZMQk7nDA/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1)
下单页
* 支付页
![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWcMFJCnMvGcZJpr9suYfGYicOicP8SAAu0dQbp9G9iaZ3sQwG0TxFHPnEAsagFOYsDlmLgKKGSe5Upqw/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1)
支付页
通常需要关注各个界面的曝光UV、点击UV、页面点击PV、页面UV点击率、页面PV点击率。
所以大家可以看到,电商的产品经理在提需求的时候,除了UI、业务等等之外,通常都会附加一些埋点的需求,通过埋点上报,来分析流量数据。
### 转化/销售指标
用户来了,还得看用户买不买,这时候,就需要关注电商主要业务历程:`浏览`\>`加车`\>`下单`\>`支付` 各个节点的转化率,也就是所谓的`加车率``下单转化率``支付转化率`
#### 购物车类指标
包括一定周期(日、周、月)`加入购物车次数``加入购物车用户数``加入购物车商品数`,也得关注流失的情况,`放弃购物车用户数`
转化率包括`加购下单率``加购支付率`等等。
#### 订单类指标
订单是电商的核心模块。
包括基础指标的统计:`下单数量``下单用户数``下单金额`
下单之后,还得关注最终的成交情况:`成交数量``成交用户数``成交金额``成交件单价=成交金额/成交数量``成交客单价=成交金额/成交人数``成交人数转化率=成交人数/下单人数``成交订单转化率=成交数量/下单数量`
也得关注下单过程中的流失情况:`关单数量``关单人数``关单金额``自动关单数量``手动关单数量`
手动关单通常会做一些问卷调查的功能,需要关注用户去掉订单的原因,商品质量、无法支付、价格过高……相当于用户调研,来根据用户的反馈不断迭代。
自动关单,通常会去做一些挽回,短信、邮件提醒用户支付,这时候也要关注弃单的挽回率。
#### 支付类指标
支付是电商业务的最后一环,做好这一环,整个交易才能平稳落地。
* **支付方式覆盖度**
国内电商基本不用考虑太多的支付方式,支付宝、微信基本可以涵盖大部分用户的支付需求。跨境电商就不太一样了,支付方式复杂很多,包括信用卡支付、钱包支付、线下转账、本地支付等等,需要定期和竞品对比主要支付方式的覆盖度,来确定还有哪些支付方式要接入。
![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWcMFJCnMvGcZJpr9suYfGYicxCDHZrRuaDYWbNHxMcQwPdVlWaaAeEOxJhD4GMH5zS6icP9ynVHV3icA/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1)
Lazada部分支付方式
* **支付成功率**
支付是强依赖第三方的业务,不同支付公司提供的支付产品质量不同,需要关注不同渠道支付成功率,然后刨除正常的业务异常,观察系统级的异常,来判断不同渠道的支付产品质量。
支付的也涉及到和第三方的交互,例如钱包支付,需要拉起对应的钱包,所以也需要分客户端(APP、PC、WAP)去观测成功率,分析交互是否还有优化的空间。
![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWcMFJCnMvGcZJpr9suYfGYicqqaSvfEK4M55lxibPNeKnIztGJWOll3fMCLKYATF99zJt9ZPtD91ibMw/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1)
支付成功率
* **收款成本**
还需要关注各个支付渠道的收款成本:包括收款费率和现金成本。现金成本包括账扣成本和平台服务费,账扣成本即信用卡等支付方式的拒付损失,账扣损失的原因可能包括欺诈、3DS(无卡支付)、产品问题、未收货、未退款等,可以通过统计账扣额度、原因占比和趋势,尽量减少损失。通过对比各渠道的收款成本优先选择性价比最高的支付渠道。另外可以对欺诈、拒付等风险指标进行监控,制定金额、占比、笔数等数据标准,如果出现超标则进行告警。
#### 总体指标
整个链路下来,我们还得看看整体的数据指标。
* **总订单数**:用户完成下单的订单数之和。
* **访问到下单转化率**:下单次数/访问次数
* **总成交额(GMV)**:总的成交金额,也可以说“流水”,用户下单,就可以算在GMV里,包括下单未支付的金额。
* **销售金额**:销售金额就是商品出售的金额综合,销售金额一般只指实际成交金额,所以GMV数字一般比销售金额大。
* **客单价**:订单金额/订单数量。
### 复购指标
![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWcMFJCnMvGcZJpr9suYfGYicI26TPekBMXU4ibicDicprzicv3tHrsKIhXsv1l7CzwxwvX7uMGD0a1twGQ/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1)
有调查数据显示,一个满意的用户会带来8笔潜在生意,不满意的用户可能会影响25个人的购买意愿,可见回头客多么重要。
复购率可以分为“用户复购率”和“订单复购率”,此外,“用户回购率”意义与复购率相似,也在此范围内。
* **用户复购率**:单位时间内,购买两次及以上的用户数/有购买行为的总用户数
* **订单复购率**:单位时间内,第二次及以上购买的订单个数/总订单数
* **用户回购率**:单位时间内,有购买行为的老用户数/有购买行为的总用户数
分析复购是非常重要的:
* 可以分析用户黏性,辅助发现复购率问题,制定运营策略。
* 横向维度(商品、用户、渠道)对比分析,细化复购率,辅助问题定位。
## 电商指标总结
电商平台,主线业务的指标,差不多就是这些了,其实还有很多其它维度的指标,比如商品、物流、营销、风控、会员、满意度等等,以后有机会再讨论吧。
数据很重要,但不能迷信数据,比如海外的一些电商平台,数据很好看,市场占有率很高,但是用过之后,真心觉得不好用。这里就小声比比一下,做跨境电商的产品和运营,你们能不能不要盯着竞品抄?做的都不怎么样,去抄淘宝、京东啊!
我的读者应该基本都是开发,为什么我还会写这一篇数据的文章呢?因为说真的,我们的KPI、月报、季报、年报,没有数据支撑,只写一个工作列表,真的不好看,一定要想办法写点收益。
> 完成xx国际化功能,带来显著流量和转化率提升,xx国家首页PV提升60%,首页UV提升45%…… 访问下单转化率提升50%,累计提升订单量13000单,按客单价50计算,提升GMV$65000……
当然,老三不是专业的产品经理,或者数据分析工程师,文中难免有一些错漏,欢迎指出。
最最……后,再比比一句👇
![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWcMFJCnMvGcZJpr9suYfGYicUhRiaWYkcyo0uuSVH8CGs8ichggib3lCfcbd76YAicTueZctTNoxqmCVKw/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1)
不开心
## 参考
* [【长文干货】一文详解电商数据指标体系](https://mp.weixin.qq.com/s?__biz=Mzg5NzczMzg3OA==&mid=2247483773&idx=1&sn=d40f7185258ed09e35c31a80faa58bfc&scene=21#wechat_redirect)
* 数据分析逻辑:流量转化漏斗模型详解:https://www.woshipm.com/data-analysis/571256.html
* 电商数据分析的基本指标体系:https://www.cnblogs.com/HondaHsu/p/14356320.html
* 电商产品经理需要关注哪些数据指标?:https://new.qq.com/omn/20201107/20201107A0EFDG00.html
* 【数据分析】电商数据分析基础指标体系:https://cloud.tencent.com/developer/article/1045377
* 电商业务常用指标:https://zhuanlan.zhihu.com/p/82816765
* * *
**微信8.0将好友放开到了一万,小伙伴可以加我大号了,先到先得,再满就真没了**
**扫描下方二维码即可加我微信啦,`2022,抱团取暖,一起牛逼。`**
![](https://mmbiz.qpic.cn/mmbiz_jpg/CKvMdchsUwlqIArsbJQpdicibHTX0MZuBl4qIrpW8lzePf1A8QVbL5rzp7lly3ffSUsvn1A9Mia5iaHIyVGyk8JgTw/640?wx_fmt=jpeg)
## 推荐阅读
* [换掉Typora!这款支持云端同步的开源笔记应用,太炫酷了!](https://mp.weixin.qq.com/s?__biz=MzU1Nzg4NjgyMw==&mid=2247502723&idx=1&sn=82a1ee739178f5abe69deed34e758951&scene=21#wechat_redirect)
* [盘点12个yyds的低代码开源项目,一天开发一个系统不是梦!](https://mp.weixin.qq.com/s?__biz=MzU1Nzg4NjgyMw==&mid=2247502722&idx=1&sn=f3f457da3b0e07fb21627b2f2a67bf5a&scene=21#wechat_redirect)
* [新同事把工作流引擎运用的炉火纯青,直接干掉几千行if else!](https://mp.weixin.qq.com/s?__biz=MzU1Nzg4NjgyMw==&mid=2247502595&idx=1&sn=104533503b704a1cbeb02a4b3080b58f&scene=21#wechat_redirect)
* [还在手写SQL实现?试试MyBatis-Plus同款IDEA插件吧!](https://mp.weixin.qq.com/s?__biz=MzU1Nzg4NjgyMw==&mid=2247502551&idx=1&sn=5017e6bf5b9aaabebcad8fb9f3fc7d89&scene=21#wechat_redirect)
* [推荐几款开源的数据库管理工具,界面炫酷,功能也很强!](https://mp.weixin.qq.com/s?__biz=MzU1Nzg4NjgyMw==&mid=2247502443&idx=1&sn=cc881653e105e20622faaa67e16d36a7&scene=21#wechat_redirect)
* [开箱即用的后台管理系统模版,用来撸项目正合适!](https://mp.weixin.qq.com/s?__biz=MzU1Nzg4NjgyMw==&mid=2247502434&idx=1&sn=5d98d82d3772d1a49547b1abaa2ab918&scene=21#wechat_redirect)
* [重磅更新!Mall实战教程全面升级,瞬间高大上了!](https://mp.weixin.qq.com/s?__biz=MzU1Nzg4NjgyMw==&mid=2247499376&idx=1&sn=3ed28795cdd35fbaa3506e74a56703b0&scene=21#wechat_redirect)
* [40K+Star!Mall电商实战项目开源回忆录!](https://mp.weixin.qq.com/s?__biz=MzU1Nzg4NjgyMw==&mid=2247486684&idx=1&sn=807fd808adac8019eb2095ba088efe54&scene=21#wechat_redirect)
![](https://mmbiz.qpic.cn/mmbiz_gif/CKvMdchsUwlkU1ysoMgG69dVYbCQcI6Byneb8ibzZWPfUCr3T8CuBicCSGyFE6SpAtxpxtDCp6VlZ4F1hEL1BNyg/640?wx_fmt=gif)
>参考链接:[https://mp.weixin.qq.com/s/1nrbGJ5fxNSZ9WqRODcB-A](https://mp.weixin.qq.com/s/1nrbGJ5fxNSZ9WqRODcB-A),出处:macrozheng,整理:沉默王二
---
title: 聊聊对账系统的设计方案
shortTitle: 聊聊对账系统的设计方案
description: 本文主要结合实际的项目经验,聊聊对账系统的设计方案。
author: 楼下小黑哥
category:
- 微信公众号
head:
---
## 前言
对账系统作为支付系统中的基石系统,处于整个支付环节中的最后一层,主要用来保证我方支付数据与第三方支付渠道或银行的数据一致性。
在没有对账系统之前,财务在第二日手工核对前一日的应收与实收。倘若不一致,这就需要一一核对数据,找出不一致的数据。对账系统出现之后,就可减少以这种繁琐手工操作,财务只需要每天关注系统的对账记录,释放了生产力。
## 系统整体设计
对账系统设计主要分为以下四个模块:
* 渠道数据处理模块
* 数据处理模块
* 数据核对模块
* 差异数据处理模块
模块调用顺序层次图如下。
![](https://mmbiz.qpic.cn/mmbiz_png/LEFcpfxrbq4ZMX8c2ErJTaTJbJkiaHS48HxKRIkVOqQ2XdqW5TJ2hJAHJXCJ1qLjVGn4lVhhwjY7pcI7qlkqWww/640?wx_fmt=png)
## 渠道数据处理模块
这个模块主要负责渠道对账文件的下载,解析,以及数据落库。
目前市面上第三方支付渠道对账文件下载方式主要分为以下几类:
* 第三方渠道定时推送到 SFTP/FTP。这种模式比较简单,我们定时从 SFTP/FTP 取对账文件。
* 调用第三方渠道对账文件下载接口。这种模式需要对接渠道下载对账文件接口,定时调用下载。支付宝与微信为该模式。
* 手动在支付渠道网站下载对账文件。这种模式最不友好,需要我们花费人力下载文件。
除了下载方式,对账文件的格式也会存在一些区别。比如支付宝对账文件格式为 csv,而微信的对账文件格式为 txt,另外有些渠道为 xml,xls。
第三方渠道对账文件里面字段数量以及字段名称也存在不同。
![](https://mmbiz.qpic.cn/mmbiz_png/LEFcpfxrbq4ZMX8c2ErJTaTJbJkiaHS48zqq0ObI17NGwuJtE63ybDggZZ534GoYFjpInuhiaItqfE19oIbWBWSQ/640?wx_fmt=png)
一般这一层每接入一个渠道需要专门根据这个渠道特性开发。这一层可以抽象化接口,对外暴露下载与解析接口。每次接入渠道,实现该接口相应方法即可。
这一层开发难度不大,只要根据对账文件格式相应解析文件即可。一般需要提取对账文件里面信息如下:
```
商户号
商户订单号
渠道流水号
交易日期
交易金额
手续费
退款原订单号
```
下面说一下开发这一层需要注意的一些细节。
1、同一渠道若申请了多个商户号。这种情况下,每个商户号若前一日都存在交易,第三方渠道会为每个商户号都会产生一份对账文件。所以这里系统设计时候需要考虑到多份对账文件处理的情况。
2、对账文件需要考虑重复下载的情况。一般情况下,渠道的对账文件一旦生成,就不会改变。但是第三方渠道也可能发生异常,导致我方收到对账文件数据不完整。这种情况下,需要有机制重新下载解析入库。
3、每个第三方渠道下载文件时间都不一样。
## 数据处理模块
讲完对账文件处理模块,我们来看数据处理模块。
这个模块主要用来提取我方前一日所有支付成功的流水数据以及上一模块入库的前一日对账单的流水数据。为了减少数据库的压力,提取的数据只需要包括必要字段即可,无需将整行数数据信息都提取出来。一般来说只要需要提取交易时间,金额,交易订单号,渠道返回流水号。
这一层主要就是数据库的查询行为。最好使用备库进行数据查询。因为这里我们需要提取前一日全量的支付成功的数据,数据量大的情况下,可能会拖慢主库,影响在线的支付交易。
## 核对模块
这一个模块我们使用上一模块提取出来的数据,核对订单号与金额是否完全一致。核对模块伪代码如下。
![](https://mmbiz.qpic.cn/mmbiz_png/LEFcpfxrbq4ZMX8c2ErJTaTJbJkiaHS48sue6xL9NXAFvJU62Z0rW53avic7BvKAWFGjcZTfFs33dXTicnZp4BxhQ/640?wx_fmt=png)
这个过程可能产生三类差异数据。
第一种情况为本端数据存在,对端数据不存在,我们称为本端多账。
第二种情况为对端数据存在,本端数据不存在,我们称为对端多账。
第三种情况为金额不一致。
三者如图所示。
![](https://mmbiz.qpic.cn/mmbiz_png/LEFcpfxrbq4ZMX8c2ErJTaTJbJkiaHS48U7BSzTJV2VhdobFJpoLH3ZRokAMaC0ZOBic1TzkSxOuxZBEmGCNkM2A/640?wx_fmt=png)
这里产生的差异数据存入一张差异表中,以便下个模块使用。
## 差异数据处理模块
这个模块主要用来处理上个模块产生的差异数据。
上面三类差异数据中,金额不一致相当少见,这种情况需要人工判断。
我们先讨论本端多账的情况。
本端多账是对账系统最常见的一种情况。这种情况可能由于交易的时候发生日切问题,导致双方记账日期不一致,从而发生不平账。
我们先解释日切的概念。
日切,通俗的来说就是更换系统记账的时间,系统从当前工作日切换到下一工作日。这个过程中,若我方的交易订单刚好发生在 T 日 23:59:59,那么我方的记账时间为 T 日。第三方渠道接收到订单的时间为 T+1 日 00:00:01,这样第三方渠道该笔的交易的对账日期为 T+1 日。
第三方渠道 T 日对账文件将缺少这笔,但是我方 T 日数据却存在这笔,这就导致了核对过程中产生一笔本端多账差异数据。
对于这类差异数据,我们可以选择将这笔数据挂账,等待 T+1 工作日对账。T+1 日对账的时候,对账单会相应多出数据,这样在核对过程就会产生对端多账的差异数据。
然后在 T+1 日差异处理模块将前几日差异数据都提取出来,逐笔核对本端多账数据与对端多账数据。若核对一致,将两笔差异状态都更新成处理完成。最后若无剩余差异数据,当天账单平账。
伪代码如下:
![](https://mmbiz.qpic.cn/mmbiz_png/LEFcpfxrbq4ZMX8c2ErJTaTJbJkiaHS48jzCiatUA20Wvic3bRiap7DMr0RMiaFiarDHAcYC47hJKcNqT6MB5h4TlBUw/640?wx_fmt=png)
对端多账的产生情况可能可能有两种情况.
第一种情况测试环境与生产环境共用一份第三方渠道参数,这就导致测试环境交易订单也会出现在对账单中。若是这种情况,我们确认测试环境存在这批数据之后,我们忽略这批差异数据即可。
第二种情况,本端交易订单存在,但是状态不是成功状态。这种情况下,需要调用第三方渠道提供的查询接口,查询订单最终状态。若查询成功,更新订单状态,然后将差异数据状态更改为处理成功。
若第三方渠道无法查询到订单的状态。这种若与渠道确认订单最终支付成功,我们需要将支付订单改为支付成功,并修改差异账的状态。
最后我们再次重新对账,由于对端多账的数据会有对应的本端数据,将不会产生差异数据,这次对账完成且平账。
## 系统优化
目前系统的对账系统定时任务采用 Spring 定时功能。后期优化准备接入 elasticjob 这种分布式定时调度程序,可以做到快速修改定时任务的时间,而无需重启程序。以及可以快速触发定时任务。
总之,对账系统工作不难,就是细节比较繁琐,前期很难将所有细节都考虑完全,这个过程需要我们不断改进。
>参考链接:[https://mp.weixin.qq.com/s?__biz=MzIzMTgwODgyMw==&mid=2247483901&idx=1&sn=ff92ae8f0c1a2627454ddcefad573e90&chksm=e89fcba5dfe842b3886f1df6b42b7fe1a9e97b14dc9f41b3f6ec2b6bfbd7dc1f81c94037a2e7&scene=178&cur_album_id=1337216649245655040#rd](https://mp.weixin.qq.com/s?__biz=MzIzMTgwODgyMw==&mid=2247483901&idx=1&sn=ff92ae8f0c1a2627454ddcefad573e90&chksm=e89fcba5dfe842b3886f1df6b42b7fe1a9e97b14dc9f41b3f6ec2b6bfbd7dc1f81c94037a2e7&scene=178&cur_album_id=1337216649245655040#rd),出处:小黑十一点半,整理:沉默王二
\ No newline at end of file
---
title: 轻轻一扫立刻扣款,深度解析付款码背后的原理
shortTitle: 轻轻一扫,立刻扣款,付款码背后的原理你不想知道吗?|原创
description: 详解付款支付流程以及撤销支付一些坑
author: 楼下小黑哥
category:
- 微信公众号
head:
---
## 前言
最近由于业务需求,需要开发付款码功能,该接口底层将会聚合市面上主流钱包 APP 的付款码功能,如微信支付,支付宝支付。
> ps:付款码支付别称有很多,如微信支付端支付产品为**付款码支付(之前的文档叫做刷卡支付)**,而支付宝端产品为**当面付-条支付**,而有些文档会成为**二维码被扫支付**。
>
> 下文统一使用微信的定义方式,统称为付款码支付。
可能有些同学对于付款码支付这个听起来很陌生,其实这个功能我们可能每天都在使用。
像我们在便利店买个早饭,最后结账时,使用支付宝/微信支付付款。收银员会让我们展示支付宝/微信付款码,然后使用扫码枪获取此码,最后上送给微信/支付宝服务端完成一次扣款。
以支付宝为例,具体用户端支付流程如下:
![](https://mmbiz.qpic.cn/mmbiz_jpg/LEFcpfxrbq4RNgb88O91mWhExKo782jby2hDb96VniaaCBV6M7Xiacp5EOYku7icdSEznS3vzGGD0NBx6cMhZ5icjg/640?wx_fmt=jpeg)
付款码支付后台调用流程如下:
![](https://mmbiz.qpic.cn/mmbiz_png/LEFcpfxrbq4RNgb88O91mWhExKo782jbsEFwvLEPxiavUyfr53SYIJYk7I05HWscUBFgbJXt7kNn8YWbvP0nl1Q/640?wx_fmt=png)
## 付款码支付详细版流程
微信/支付宝付款码支付调用流程大同小异,官网写的都比较清楚,这里直接用支付宝的官网的流程。
![](https://mmbiz.qpic.cn/mmbiz_jpg/LEFcpfxrbq4RNgb88O91mWhExKo782jbbztduicSAmRXDdJCempgiaY30cyT6KE8zuBsibf9gJT7KMiajc12H0XbZg/640?wx_fmt=jpeg)
从上面的流程可以看到,付款码支付可以说是一个**同步**的接口,即接口同步返回扣款结果,无需通过另外异步通知获取结果。
不过这里我们需要注意,由于涉及**安全风控**等问题,付款码支付过程用户端可能需要输入密码确认支付,此时付款码接口将会返回等待用户支付。
接入时务必正确判断返回信息,若返回以下结果,代表此时用户正在输入密码。
* 微信支付: **err\_code=USERPAYING 或 err\_code=SYSTEMERROR**
* 支付宝:**code=10003 或 code=20000**
微信付款码支付在以下情况需要输入密码二次确认。
![](https://mmbiz.qpic.cn/mmbiz_png/LEFcpfxrbq4RNgb88O91mWhExKo782jby3n2XVJUPdwPEAQ3TyOGrPEFMCucn1IovvibCQF3jXFzz6pR7Mz8ia1Q/640?wx_fmt=png)
支付宝官方文档暂未找到相关规则,经过测试当支付金额大于 **2000** ,需要输入密码。如果有熟悉其他验密规则的同学,可以在评论区留言一下。
另外一点需要注意的是,微信/支付宝其他支付接口,支付成功之后,微信/支付宝服务端将会发送消息通知支付结果。但是付款码不一样,该接口是不会有消息通知的。
所以如果付款码支付若返回等待用户输入密码,商家后台服务必须定时调用调用微信支付/支付宝查询接口,获取支付结果。
![](https://mmbiz.qpic.cn/mmbiz_png/LEFcpfxrbq4RNgb88O91mWhExKo782jbicKsxjeyFdJBbsG3u7KFflRzjzroWyldSq7GNJCBXcVBjM5c5862t4Q/640?wx_fmt=png)
## 撤销支付
如果在一段时间内比如 **30s**,轮询查询支付结果返回都是等待用户支付,或者支付交易过程返回失败或支付系统超时,这两种情况官方文档都是建议立刻调用撤销接口撤销交易。
如果此订单用户支付失败,撤销接口将会订单关闭;如果用户支付成功,撤销接口将会订单资金退还给用户。
也就是说撤销支付接口功能上等同与**关闭订单**加上**退款**。虽然撤销也具有退款功能,但是两者存在比较大的区别:
**支付类型限制**
微信/支付宝撤销支付**仅能撤销付款码支付类型的订单**,而退款可以支持多种支付类型的订单。
**退款金额**
撤销接口只能是**全额退款**,而退款接口支持传入金额,可以全额退款,也可以部分退款。
**时间限制**
撤销接口时间限制比较短,比如微信支付撤销支持 **7 天**内的订单,而支付宝撤销接口仅支持当天的订单。
但是退款接口可以支持较长时间订单退款,比如微信支付退款支持**一年内**的订单,而支付宝仅支持 **3 个月**内订单。
**基于以上区别,其他正常支付的单如需实现相同功能请调用退款接口,官方文档建议仅在异常的情况下才建议调用撤销支付接口。**
另外再说一点,有些地方这个功能接口称为**冲正接口**,如下面工商二维码支付。
![](https://mmbiz.qpic.cn/mmbiz_png/LEFcpfxrbq4RNgb88O91mWhExKo782jb0OYA7RFvic0rhE96IBp2Y7giar8BQ2kibCpNicNOfjXeniaIiaueiaP0mlDUg/640?wx_fmt=png)
实际上提供的功能与微信/支付宝撤销类似,这里需要各家支付公司提供文档具体研究。
## 撤销支付相关问题
由于撤销支付,可能导致退款,也可能关闭订单,接入之前一直有些问题弄不清楚,在官方文档处也没有查询到任何资料,没办法只好实测验证相关问题。
> 由于规定,支付机构不能直连微信/支付宝,所以以下测试基于银联微信/支付宝通道。
>
> 银联提供的接口与直连微信/支付宝存在些许差别,但是主要功能一样。
### 重复撤销
通过实测,微信/支付宝撤销接口幂等实现,重复撤销返回结果一致。
不过需要注意需要正确判断撤销的返回结果。
比如微信撤销接口成功判断还需要结合 `recall` 字段,支付宝也有类似字段。
![](https://mmbiz.qpic.cn/mmbiz_png/LEFcpfxrbq4RNgb88O91mWhExKo782jbjHc5blXPxvb6O1iaibsku93hxZjkE8MGFmgcwnxKdKvkpTV2vVjiaP59w/640?wx_fmt=png)
### 订单状态
微信/支付宝订单状态处理不太一致,微信订单状态比较复杂:
![](https://mmbiz.qpic.cn/mmbiz_jpg/LEFcpfxrbq4RNgb88O91mWhExKo782jbCst89CBISDnTdQgCIM01icVTia8v3zIJXQv1JicOg2AT0gKxFVpic7sFjQ/640?wx_fmt=jpeg)
也就是说,付款码订单一旦被撤销成功,再次查询订单,状态将会返回为**已撤销(REVOKED)**
另外微信对于付款码支付订单有限制,是无法调用关闭订单接口关闭订单,所以在付款码的场景中,是不存在订单状态为 **CLOSED—已关闭**
接下来说下支付宝的状态,支付宝文档没要给出类似的订单状态机,我根据官方一些文档,以及一些测试结果总结出下方订单状态图。
![](https://mmbiz.qpic.cn/mmbiz_png/LEFcpfxrbq4RNgb88O91mWhExKo782jb4ic30ySVu4cRG9mHZTSvjBHiaC4M42HJmHueHibacDQFFtLerLungqSYg/640?wx_fmt=png)
所以支付宝的付款码订单一旦撤销成功,再次查询原单状态将会返回 **TRADE\_CLOSED**
### 对账文件数据
当天产生交易之后,次日我们需要拉取微信/支付宝对账文件,逐一核对数据,防止少账,多账问题。
>对账系统的设计方案随后会分享出来。
微信/支付宝对账文件只会记录交易成功的订单,所以未支付的订单被撤销是不会出现在对账文件中。但是如果支付成功了,然后又被撤销成功,将会在对账文件中产生**两笔记录**,一笔正交易,一笔反向退款记录。
正常交易与普通的退款的记录都比较好识别,可以使用我们上送给微信支付宝的订单号。但是撤销导致退款记录,我们无法仅用一个单号识别,我们需要结合另外的字段区分判断。
微信对账文件撤销产生那笔退款,交易状态为 **REVOKED**,所以我们可以采用商户订单号加交易状态识别出一条记录是否为撤销产生退款记录。
![](https://mmbiz.qpic.cn/mmbiz_png/LEFcpfxrbq4RNgb88O91mWhExKo782jbFUejQwJn0slrC7bcib2mlIticF3unUaKbicnMyywMbDjaNjSgvEe55WGw/640?wx_fmt=png)
> 上面银联订单号可以当做是微信支付宝内部产生订单号
支付宝对账文件比较麻烦,撤销产生的退款记录不能跟微信根据交易状态区分。从对账文件上看支付宝撤销产生退款与普通退款接口产生退款记录是一样的。
![](https://mmbiz.qpic.cn/mmbiz_png/LEFcpfxrbq4RNgb88O91mWhExKo782jbbTHXYJibXickibv62zNDYz4vib1wD0uKRDmvd6seWiaPRW9wqZH9cfb9wRw/640?wx_fmt=png)
仔细研究对账文件可以发现一些区别,撤销导致退款记录退款批次与正交易支付宝内部订单号是一致的。而正常退款记录,退款批次号是由商户自己上送的。所以我们可以以此筛选出撤销产生的退款记录。
### 撤销失败
极端情况下,有可能产生多次撤销都失败的奇葩情况,那怎么办?
这种情况下就不用往系统自动处理方向考虑了,通过线下人工介入处理吧,毕竟这种概率太低了。
引用知乎 **@天顺** 的文章中一句话:
> 很多时候人工保障比你动脑筋想异常中的异常如何系统自动处理来得反而高效和低成本
这句话大家仔细品,越品越有道理!
>参考链接:[https://mp.weixin.qq.com/s/De71w6C49YJJb_VXeLL0vw](https://mp.weixin.qq.com/s/De71w6C49YJJb_VXeLL0vw),出处:小黑十一点半,整理:沉默王二
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册