有20天没有更新了,主要原因有二:其一这期间对自己的职业规划做了一些调整;其二生了一场小病。所以大家一定要保重身体,平时得多锻炼锻炼了。

根据大家反馈,大家对 Payment 还是很认同,这让我很开心。五一花了两天时间把招商一网通集成进来了。希望能够帮助到更多的人。


Payment使用文档https://helei112g.github.io/categories/payment-3/

项目GitHub地址https://github.com/helei112g/payment

言归正传,这篇拖了很久了,借着五一假期的最后一天,搞定它!先上一个支付的一般流程图。

image
图片来源:支付宝

异步与同步

在我们完成支付后,要确认用户是否真的支付了这笔钱,以及这笔钱支付的金额是否符合预期。怎么知道这些事情呢?

用户告诉我们?我们肯定不能确认是否真假。比较恰当的方式是,第三方(支付宝、微信、招商一网通等)收到用户付款后,他来告诉我们。所以这里就引出了通知这个概念。

通知的方式又有两种:同步通知,异步通知。同步通知的概念存在于网站支付或者H5支付中,因为只有在浏览器中才可以通过url进行跳转。那么我们应该使用同步通知作为支付成功的依据还是使用异步通知呢?

我的答案是:同步通知不做服务端的更新,可用于客户端的显示,异步收到通知时才做相关的更新处理。原因有三:

  1. 并不是所有的支付模块都有同步通知这个概念;
  2. 同步通知的参数在url中,就算采用https协议,也存在更大被篡改的风险;
  3. 异步通知提供完整的失败重发机制,更值得信耐。

异步通知处理

所以在 Payment 中只针对异步通知到达的数据进行了签名相关处理。下面我们来看看代码。

不管你是支付宝的异步通知,还是微信的异步通知或者招商一网通的异步通知,Payment 都提供了统一的异步处理接口,并且调用者完全不必去关心如何验证签名,如何核实数据,只需要专注自己的业务逻辑即可。

回调使用了一个开发的小技巧:依赖注入,我先上代码,后面稍微解释下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$callback = new TestNotify();

$type = 'ali_charge';// ali_charge wx_charge cmb_charge

try {
// 获取第三方的原始数据,未进行签名检查,根据自己需要决定是否需要该步骤
//$retData = Notify::getNotifyData($type, $config);

$ret = Notify::run($type, $config, $callback);// 处理回调,内部进行了签名检查
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}

return $ret;

回调 的代码就这么简单,其中 Notify::getNotifyData($type, $config); 可以获取到第三方的回调数据,sdk仅仅是解析成数组返回,没有做签名检查,不能直接用来进行回调处理。

Notify::run($type, $config, $callback); 则是进行回调相关的处理,它返回的值,是需要返回给第三方支付机构的,第三方机构通过返回值来识别商户是否正确处理了回调通知,并且以此来处理是否需要重发。

这里的重点就是 $callback,注意看上面的代码 $callback = new TestNotify();

大家的重点就是创建这个 TestNotify 这个类,我们就叫它:商户回调业务处理类,名字大家随便取,根据自己的需要。看一下我示例中 TestNotify.php 的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use Payment\Notify\PayNotifyInterface;
use Payment\Config;

/**
* 客户端需要继承该接口,并实现这个方法,在其中实现对应的业务逻辑
* Class TestNotify
* anthor helei
*/
class TestNotify implements PayNotifyInterface
{
public function notifyProcess(array $data)
{
$channel = $data['channel'];
if ($channel === Config::ALI_CHARGE) {// 支付宝支付

} elseif ($channel === Config::WX_CHARGE) {// 微信支付

} elseif ($channel === Config::CMB_CHARGE) {// 招商支付

} elseif ($channel === Config::CMB_BIND) {// 招商签约

} else {
// 其它类型的通知
}

// 执行业务逻辑,成功后返回true
return true;
}
}

这里的核心就是,客户端创建的 商户回调业务处理类 一定要实现 PayNotifyInterface 这个接口,并且实现 notifyProcess 这个方法,在这个方法中完成自己的商户逻辑。然后根据处理结果返回布尔值。

这里要重点说明的是 TestNotify::notifyProcess(array $data) 这个方法的参数 $data,它是通过sdk内部传过来的,它返回的信息与配置文件 return_raw的设置有关系。

如果 return_raw = false 返回的是我映射的结果,这里需要小心,如果你的php报错级别设置的太高,可能会出现报错,因为内部有很多 Undefined index 错误,因为我并未进行检查,当前可能暂时也不打算修复这个问题。
如果 return_raw = true 则返回的是第三方支付机构的原始数据。

不管是设置成什么,该参数里边都有一个 channel 参数,大家可以根据这个参数区分回调属于那一个类别。像上面的示例一样。

PayNotifyInterface 接口

最近有很多人在微信中问我,干嘛一定要实现这个接口?我这里举个例子:
你出去旅行,到了突然发现ios的手机充电线没带,怎么办?找个便利店买一根好了(因为附近没有苹果店),那么怎么买的充电线确保你手机可以用呢?这个很简单,因为苹果把自己充电线的接口标准公布了,凡事想造苹果手机充电线的按照这个标准造出来的,你都可以用。

所以接口的作用就是制定一个标准;你们都实现我规定的接口,然后返回布尔值给我,我就可以处理了。至于内部你们想怎么做,是你们的事情。

通过这个接口完全将签名与商户业务逻辑进行了分离,以后你的调整只围绕这一个类即可。当然这里涉及到的思想有 ioc/di ,这又是另外一个话题,空的时候我们聊聊。