Commit e0421351 by yeran

fix the tlpay notify bug

0 parents
Showing with 11533 additions and 0 deletions
.idea
.DS_Store
/codecept.phar
/examples/test.php
/examples/wx/pem
/vendor/
/examples_bak/
/composer.lock
/examples/wxconfig-bak.php
/examples/wx/pem/
Payment只有在大家的使用反馈中才能得到不断的完善。
我希望通过真实的项目来驱动它不断发展,在为工作带来方便的同时尽力保持它的简洁。
# issue报Bug
由于Payment高度依赖第三方接口,因此第三方一个小的变动也会导致项目产生一个大版本号。当前主要有:
- 2.x 应该没多少人使用了,已经放弃维护
- 3.x 继续维护,只修bug,不做接口更新
- 4.x 当前开发版本,均保持当前第三方的最新接口
由于版本比较多。因此报bug建议采用以下格式:
[3/4.x]版本,在什么环境下(沙盒还是正式),调用了什么接口,出现了什么错误(最好有截图)。自己尝试过哪些办法去解决未达到预期效果
推荐所有的bug在提交时,先使用demo代码运行一下,看看能否通过。
**只提供标题,或者没有重现步骤的,将不处理**
# 贡献代码
请代码书写遵循以下规则:
- [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)
- 使用4个空格作为缩紧(不是tab)
- 命名使用驼峰命名(不准使用拼音)
- 请给类、方法、变量添加注释,注释需要包含:日期、修改人、含义
## 开发流程
1. Fork [helei112g/payment](https://github.com/helei112g/payment) 到本地
2. 创建新的分支:
```shell
$ git checkout -b new_feature
```
3. 编写代码
4. Push 到你的分支:
```shell
$ git push origin new_feature
```
5. 创建 Pull Request 并描述你完成的功能或者做出的修改
所提交的部分一定自己真实测试完毕,如果是新的支付功能,需要添加对应的demo以及
相关功能的文档(暂无开源文档地址,请根据功能名称,提供 `.md`的说明文档)。
## 代码说明
为了让大家快速理解代码结构,将项目相关结构图进行说明
---------
**`遇到bug,90%都是秘钥相关导致的,微信可能与后台配置有关。请仔细检查。`**
\ No newline at end of file
Copyright (c) 2016-2017 Daniele Alessandri
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
# payment
> 支付工具包 v1.0 from riverslei/payment
\ No newline at end of file
**打赏名单**
名字 | 金额 | 说明 | 时间
---|---|---|---
北国之雪 | 30.00 | Payment好用 | 2017-05-04
里暮色中 | 50.00 | Payment越来越好(感谢发现一个bug issue#39) | 2017-05-01
lobtao | 8.88 | 你的内裤非常好用 | 2017-04-26
182xxxx | 10.00 | 支持 | 2017-04-19
来自简单 | 200.00 | 支付宝服务商模式咨询 | 2017-04-16
刘华 | 50.00 | 微信支付咨询 | 2017-04-14
靖 | 88.00 | 感谢开源 | 2017-03-19
李 | 150.00 | 打赏支持 | 2017-03-14
Alex.Ma | 6.66 | 支持 | 2017-03-13
阿笨 | 10.00 | 打算使用,先感谢一下 | 2017-03-10
彦 | 88.00 | 感觉还不错,特打赏88元,略表感谢。 | 2017-02-28
汤明洋 | 66.66 | 支持一下 | 2017-02-19
李仕建同学 | 18.88 | 新春快乐 | 2017-02-09
凡额 | 50.00 | 帮助调试,谢谢了 | 2017-01-18
Thans秦 | 66.66 | 商业使用 | 2017-01-08
John | 10.00 | 设计很棒 | 2017-01-06
Davidw | 699.00 | 支持开发2.0 | 2016-12-15
宁静致远 | 10.00 | 鼓励你,加油额 | 2016-12-13
k7 | 8.00 | 批量付款,一次成功 | 2016-11-24
洋 | 50.00 | 资助开源 | 2016-11-23
张仲东 | 50.00 | 接口封装的不错 | 2016-11-17
放下...快乐 | 1000.00 | 支付宝即时到帐处理 | 2016-11-15
Robin Core Animation | 50.00 | 解决微信支付问题 | 2016-11-04
5Z4 | 20.00 | 解决回调问题 | 2016-10-31
哈罗Joe | 1.00 | 加油~~ | 2016-8-23
小兵~招UI前端 | 50.00 | 继续努力,喝杯水吧:-) | 2016-8-14
尊称韦爵爷 | 1.00 | 赶紧出个yii的扩展 | 2016-7-22
一米市集 | 1000.00 | 希望提供技术长期合作 | 2016-7-20
张松 | 15.00 | 不错,已用到项目中 | 2016-6-17
\ No newline at end of file
<?php
namespace Payment\Notify;
use Payment\Common\PayException;
use Payment\Common\TLConfig;
use Payment\Config;
use Payment\Utils\ArrayUtil;
/**
* Class TLNotify
* 微信回调处理
* @package Payment\Notify
* anthor yeran
*/
class TLNotify extends NotifyStrategy
{
/**
* TLNotify constructor.
* @param array $config
* @throws PayException
*/
public function __construct(array $config)
{
parent::__construct($config);
try {
$this->config = new TLConfig($config);
} catch (PayException $e) {
throw $e;
}
}
/**
* 获取返回的异步通知数据
* @return array|bool
* @author yeran
*/
public function getNotifyData()
{
// php://input 带来的内存压力更小
// "{ \"key\": \"123456\" }";
$data = @file_get_contents('php://input');// 等同于微信提供的:$GLOBALS['HTTP_RAW_POST_DATA']
// 将xml数据格式化为数组
$arrData = json_decode($data,true);
if (empty($arrData)) {
return false;
}
// 移除值中的空格 xml转化为数组时,CDATA 数据会被带入额外的空格。
$arrData = ArrayUtil::paraFilter($arrData);
return $arrData;
}
/**
* 检查异步通知的数据是否正确
* @param array $data
*
* @author yeran
* @return boolean
*/
public function checkNotifyData(array $data)
{
// 检查返回数据签名是否正确
return $this->verifySign($data);
}
/**
* 检查微信返回的数据是否被篡改过
* @param array $retData
* @return boolean
* @author yeran
*/
protected function verifySign(array $retData)
{
$retSign = $retData['sign'];
$values = ArrayUtil::removeKeys($retData, ['sign', 'sign_type']);
$values = ArrayUtil::paraFilter($values);
$signStr = ArrayUtil::SignArray($values,$this->config->md5Key);
return strtoupper($signStr) === $retSign;
}
/**
*
* 封装回调函数需要的数据格式
*
* @param array $data
*
* @return array
* @author yeran
*/
protected function getRetData(array $data)
{
if ($this->config->returnRaw) {
$data['channel'] = Config::TL_CHARGE;
return $data;
}
return $data;
}
/**
* 处理完后返回的数据格式
* @param bool $flag
* @param string $msg 通知信息,错误原因
* @author yeran
* @return string
*/
protected function replyNotify($flag, $msg = 'OK')
{
// 默认为成功
$return_code ='SUCCESS';
if (! $flag) {
$return_code ='FAIL';
}
return [$return_code,$msg];
}
}
<?php
function classLoader($class)
{
$path = str_replace('\\', DIRECTORY_SEPARATOR, $class);
$path = str_replace('Payment' . DIRECTORY_SEPARATOR, '', $path);
$file = __DIR__ . '/src/' . $path . '.php';
if (file_exists($file)) {
require_once $file;
}
}
spl_autoload_register('classLoader');
{
"name": "ldy/payment",
"type": "library",
"description": "支付宝支付、微信支付、招商一网通支付、通联支付 php SDK。",
"keywords": ["alipay", "weixin", "支付宝支付", "微信支付","通联支付", "集成支付接口SDK", "招商一网通", "一网通"],
"homepage": "",
"license": "MIT",
"authors": [
{
"name": "ldy",
"email": "932497180@qq.com",
"homepage": ""
}
],
"require": {
"php": ">=5.6",
"ext-bcmath": "*",
"ext-mbstring": "*"
},
"require-dev": {
"endroid/qrcode": "~1.9"
},
"autoload": {
"psr-4": {"Payment\\": "src/"}
}
}
<?php
/**
* @author: helei
* @createTime: 2016-08-01 11:37
* @description: 微信配置文件
*/
return [
'app_id' => '00020281', // 公众账号ID
'cus_id' => '55079104816PJXP',// 商户id
'md5_key' => '55079104816PJXP04',// md5 秘钥
'sign_type' => 'MD5',// MD5 HMAC-SHA256
'limit_pay' => ['no_credit'],
'notify_url' => 'http://172.16.2.46:8080/vo-apidemo/OrderServlet',
'redirect_url' => 'https://helei112g.github.io/',// 如果是h5支付,可以设置该值,返回到指定页面
'return_raw' => false,// 在处理回调时,是否直接返回原始数据,默认为true
];
//测试账号
//商户号:55079104816PJXP
//交易密码:Zyzswz1234
// 登录密码:Zyzs123456
//appid00020281 交易密钥:55079104816PJXP04
\ No newline at end of file
<?php
/**
* 手机app支付demo
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 下午5:38
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Client\Charge;
use Payment\Config;
date_default_timezone_set('Asia/Shanghai');
$aliConfig = require_once __DIR__ . '/../aliconfig.php';
// 订单信息
$orderNo = time() . rand(1000, 9999);
$payData = [
'body' => 'ali qr pay',
'subject' => '测试支付宝扫码支付',
'order_no' => $orderNo,
'timeout_express' => time() + 600,// 表示必须 600s 内付款
'amount' => '0.01',// 单位为元 ,最小为0.01
'return_param' => '123123',
'client_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1',// 客户地址
'goods_type' => '1',
'store_id' => '',
];
try {
$str = Charge::run(Config::ALI_CHANNEL_APP, $aliConfig, $payData);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
echo $str;// 这里如果直接输出到页面,&not 会被转义,请注意
\ No newline at end of file
<?php
/**
* 条码支付
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 下午4:32
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Client\Charge;
use Payment\Config;
date_default_timezone_set('Asia/Shanghai');
$aliConfig = require_once __DIR__ . '/../aliconfig.php';
// 订单信息
$orderNo = time() . rand(1000, 9999);
$payData = [
'body' => 'ali bar pay',
'subject' => '测试支付宝条码支付',
'order_no' => $orderNo,
'timeout_express' => time() + 600,// 表示必须 600s 内付款
'amount' => '0.01',// 单位为元 ,最小为0.01
'return_param' => '123123',
'client_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1',// 客户地址
'goods_type' => '1',
'store_id' => '',
'operator_id' => '',
'terminal_id' => '',// 终端设备号(门店号或收银设备ID) 默认值 web
'alipay_store_id' => '',
'scene' => 'bar_code',// 条码支付:bar_code 声波支付:wave_code
'auth_code' => '1231212232323123123',
];
try {
$ret = Charge::run(Config::ALI_CHANNEL_BAR, $aliConfig, $payData);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
var_dump($ret);
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>支付宝支付demo</title>
</head>
<body>
<ul>
<li><a href="appCharge.php" target="_blank">手机APP支付</a></li>
<li><a href="wapCharge.php" target="_blank">支付宝手机网站支付</a></li>
<li><a href="qrCharge.php" target="_blank">扫码支付</a></li>
<li><a href="barCharge.php" target="_blank">条码支付</a></li>
<li><a href="webCharge.php" target="_blank">电脑支付(即时到账)</a></li>
</ul>
<ul>
<li><a href="queryOrder.php" target="_blank">查询支付的订单</a></li>
<li><a href="queryRefund.php" target="_blank">查询退款的订单</a></li>
<li><a href="queryTransfer.php" target="_blank">查询转账的订单</a></li>
</ul>
<ul>
<li><a href="refund.php" target="_blank">退款订单</a></li>
<li><a href="transfer.php" target="_blank">转账操作</a></li>
</ul>
</body>
</html>
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 下午4:29
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Client\Charge;
use Payment\Config;
date_default_timezone_set('Asia/Shanghai');
$aliConfig = require_once __DIR__ . '/../aliconfig.php';
// 订单信息
$orderNo = time() . rand(1000, 9999);
$payData = [
'body' => 'ali qr pay',
'subject' => '测试支付宝扫码支付',
'order_no' => $orderNo,
'timeout_express' => time() + 600,// 表示必须 600s 内付款
'amount' => '0.01',// 单位为元 ,最小为0.01
'return_param' => '123123',
'client_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1',// 客户地址
'goods_type' => '1',
'store_id' => '',
];
try {
$url = Charge::run(Config::ALI_CHANNEL_QR, $aliConfig, $payData);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
echo $url;
\ No newline at end of file
<?php
/**
* 查询订单
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 下午5:35
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Client\Query;
use Payment\Config;
date_default_timezone_set('Asia/Shanghai');
$aliConfig = require_once __DIR__ . '/../aliconfig.php';
$data = [
'out_trade_no' => '14935448529859',
'trade_no' => '2017043021001004350200163279',
];
try {
$ret = Query::run(Config::ALI_CHARGE, $aliConfig, $data);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
echo json_encode($ret, JSON_UNESCAPED_UNICODE);
\ No newline at end of file
<?php
/**
* 查询订单退款状态
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 下午5:55
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Client\Query;
use Payment\Config;
date_default_timezone_set('Asia/Shanghai');
$aliConfig = require_once __DIR__ . '/../aliconfig.php';
$data = [
'out_trade_no' => '14935460661343',
'trade_no' => '',
'refund_no' => '14935460994756',
];
try {
$ret = Query::run(Config::ALI_CHARGE, $aliConfig, $data);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
echo json_encode($ret, JSON_UNESCAPED_UNICODE);
\ No newline at end of file
<?php
/**
* 转账查询
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 下午6:00
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Client\Query;
use Payment\Config;
date_default_timezone_set('Asia/Shanghai');
$aliConfig = require_once __DIR__ . '/../aliconfig.php';
$data = [
'trans_no' => '1493546355',
'transaction_id' => '20170430110070001500680000004577',
];
try {
$ret = Query::run(Config::ALI_TRANSFER, $aliConfig, $data);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
echo json_encode($ret, JSON_UNESCAPED_UNICODE);
\ No newline at end of file
<?php
/**
* 退款操作
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 下午5:43
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Client\Refund;
use Payment\Config;
date_default_timezone_set('Asia/Shanghai');
$aliConfig = require_once __DIR__ . '/../aliconfig.php';
$refundNo = time() . rand(1000, 9999);
$data = [
'out_trade_no' => '14935460661343',
'refund_fee' => '0.01',
'reason' => '测试帐号退款',
'refund_no' => $refundNo,
];
try {
$ret = Refund::run(Config::ALI_REFUND, $aliConfig, $data);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
echo json_encode($ret, JSON_UNESCAPED_UNICODE);
\ No newline at end of file
<?php
/**
* 支付转账操作
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 下午5:57
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Client\Transfer;
use Payment\Config;
date_default_timezone_set('Asia/Shanghai');
$aliConfig = require_once __DIR__ . '/../aliconfig.php';
$data = [
'trans_no' => time(),
'payee_type' => 'ALIPAY_LOGONID',
'payee_account' => 'aaqlmq0729@sandbox.com',// ALIPAY_USERID: 2088102169940354 ALIPAY_LOGONID:aaqlmq0729@sandbox.com
'amount' => '0.01',
'remark' => '转账拉,有钱了',
'payer_show_name' => '何磊',
];
try {
$ret = Transfer::run(Config::ALI_TRANSFER, $aliConfig, $data);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
echo json_encode($ret, JSON_UNESCAPED_UNICODE);
\ No newline at end of file
<?php
/**
* wap网站支付
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 上午11:31
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Client\Charge;
use Payment\Config;
date_default_timezone_set('Asia/Shanghai');
$aliConfig = require_once __DIR__ . '/../aliconfig.php';
// 订单信息
$orderNo = time() . rand(1000, 9999);
$payData = [
'body' => 'ali wap pay',
'subject' => '测试支付宝手机网站支付',
'order_no' => $orderNo,
'timeout_express' => time() + 600,// 表示必须 600s 内付款
'amount' => '0.01',// 单位为元 ,最小为0.01
'return_param' => 'tata',// 一定不要传入汉字,只能是 字母 数字组合
'client_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1',// 客户地址
'goods_type' => '1',
'store_id' => '',
];
try {
$url = Charge::run(Config::ALI_CHANNEL_WAP, $aliConfig, $payData);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
header('Location:' . $url);
\ No newline at end of file
<?php
/**
* 电脑支付 即时到账接口
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 下午4:34
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Client\Charge;
use Payment\Config;
date_default_timezone_set('Asia/Shanghai');
$aliConfig = require_once __DIR__ . '/../aliconfig.php';
// 订单信息
$orderNo = time() . rand(1000, 9999);
$payData = [
'body' => 'ali web pay',
'subject' => '测试支付宝电脑网站支付',
'order_no' => $orderNo,
'timeout_express' => time() + 600,// 表示必须 600s 内付款
'amount' => '0.01',// 单位为元 ,最小为0.01
'return_param' => '123123',
'client_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1',// 客户地址
'goods_type' => '1',
'store_id' => '',
// 说明地址:https://doc.open.alipay.com/doc2/detail.htm?treeId=270&articleId=105901&docType=1
// 建议什么也不填
'qr_mod' => '',
];
try {
$url = Charge::run(Config::ALI_CHANNEL_WEB, $aliConfig, $payData);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
header('Location:' . $url);
\ No newline at end of file
<?php
/**
* @author: helei
* @createTime: 2016-07-15 17:19
* @description:
*/
// 一下配置均为本人的沙箱环境,贡献出来,大家测试
// 个人沙箱帐号:
/*
* 商家账号 naacvg9185@sandbox.com
* 商户UID 2088102169252684
* appId 2016073100130857
*/
/*
* 买家账号 aaqlmq0729@sandbox.com
* 登录密码 111111
* 支付密码 111111
*/
return [
'use_sandbox' => true,// 是否使用沙盒模式
'partner' => '2088102169252684',
'app_id' => '2016073100130857',
'sign_type' => 'RSA2',// RSA RSA2
// 可以填写文件路径,或者密钥字符串 当前字符串是 rsa2 的支付宝公钥(开放平台获取)
'ali_public_key' => 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmBjJu2eA5HVSeHb7jZsuKKbPp3w0sKEsLTVvBKQOtyb7bjQRWMWBI7FrcwEekM1nIL+rDv71uFtgv7apMMJdQQyF7g6Lnn9niG8bT1ttB8Fp0eud5L97eRjFTOa9NhxUVFjGDqQ3b88o6u20HNJ3PRckZhNaFJJQzlahCpxaiIRX2umAWFkaeQu1fcjmoS3l3BLj8Ly2zRZAnczv8Jnkp7qsVYeYt01EPsAxd6dRZRw3uqsv9pxSvyEYA7GV7XL6da+JdvXECalQeyvUFzn9u1K5ivGID7LPUakdTBUDzlYIhbpU1VS8xO1BU3GYXkAaumdWQt7f+khoFoSw+x8yqQIDAQAB',
// 可以填写文件路径,或者密钥字符串 我的沙箱模式,rsa与rsa2的私钥相同,为了方便测试
'rsa_private_key' => 'MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC/z+Ue/oS0GjO2
myYrkdopw5qq6Ih/xlHBx0HBE0xA2dRinpMuZeI0LUUtN54UAUZbDz8rcaOCb0je
loeYolw54tadcIw4Q2hbdeJPplldJZyi1BDYtBJZvAveeRSidHdmBSUtOtCBXUBl
JUP3I8/R4c34Ii4Pm/K4vmhwLf/zqZAedKGhYP6m5q+p8sfBHRPy97/KluLPiSTR
FqGSRmd0IitUGK+KQ5qsAfJXyN1oVR4jBYaxfx7dWkTWmxAfNqtKfMvu2a5lH6hv
ClN+w4RUDBu3939bLjCYKcAomkv3QMquMP46m+D8Ny+3mGk5L9Ul4jyxlFTlV4L4
JM3g/02xAgMBAAECggEBALZliwseHDLnd6V9g56K41ozlzBOTv6yJ6yNPgnLwAcr
HLtq76p/V8smAVIuQTPkwnJ03S0CsumlyTVhDzAltG2XN14fWDdoYiQWxU3YccIR
shFkd2CaW5jZKLA1k1moRqHM4r1P4FYjxshn12l7tHNwtdvvJL3THcxvxABovauF
OVtznpRlnfJLjn2Lg+xNsxaYy3zL8L6nL7MXUWLKvmLiZn64PFcw7cf+9n2exRDs
wn0wDCpypGqOVVXVFeZaXTwmOoxgIUAZfAExdLtabGGCAz1lTsA0+r4DW2nSTe8C
Fy1Db+fcCTm+uQ3y6jDwuS3tB8V+PQKog3+ReZp/9sECgYEA/NEr+ln6DTy7u4rC
Wq7mixRJ1kaiAUph/hADrUwhkMiUapSMNAIXblFB+BQUjFZQmXEbcvz0Y70g9Zi9
JCXVTiDTBe7jj/FK63MU0F9KY5OducpVV+RhSpNy/i1M2qeW4gO351PpPHUpRUYr
GkYvAKktqrSOdBEWD3IeKLYDXxMCgYEAwjoavGjWzD9Xckbpb8yrQ+gHfLeWDKh7
BgvoBGagyqbzIOZU9wg3dSQ2F5eMWDxWVRGqap3fIHxcA0/VMqXG1DrvSIUC4SE8
Zys515fR00c9h3W3IugHnKgdYcV7nZrJoPZXlMjPOo39FCBnfbrUOgnKwxMlz3lV
vC6465ODhKsCgYEAmUtTuTd5kTE0O+FFO6s1iztAEjc94D5z8JNRR3EUITAeHgn4
gUiLYI7Qy1WRqA5mTMPyeuS6Ywe4xnJYrWRrVDY+/if9v7f1T5K2GirNdld5mb//
w41tGMUTQt/A7AwWRvEuP4v3rnr0DVcgp4vK0EHEuO9GOUZq8+6kLtc+cBUCgYBF
J/kzEsVAjmEtkHA33ZExqaFY1+l2clrziTPAtWYVIiK5mSmxl9xfOliER/KxzDIV
MigStEmpQH5ms3s/AGXuVVmz4aBn1rSyK2L6D9WnO9t9qv1dUW68aeOkV3OvZ1jZ
lj0S/flDaSEulGclDmvYinoGwX+aAyLy0VQIlUqj5wKBgHEUEf7YDnvw/IBnF1E4
983/7zBx9skoHhpEZsh2+1or7LIw6z0m3lsNBnK0MZZBmW/7HwOtVfhXUUPbVrOJ
di70YoMynX3gjK3LTXhzISheZgcNRKTqiJgVunPokJxQRyYcAfaQeuIm9O8cCPE1
rZpNAzCdd4NSj83UZRm3YOmC',
'limit_pay' => [
//'balance',// 余额
//'moneyFund',// 余额宝
//'debitCardExpress',// 借记卡快捷
//'creditCard',//信用卡
//'creditCardExpress',// 信用卡快捷
//'creditCardCartoon',//信用卡卡通
//'credit_group',// 信用支付类型(包含信用卡卡通、信用卡快捷、花呗、花呗分期)
],// 用户不可用指定渠道支付当有多个渠道时用“,”分隔
// 与业务相关参数
'notify_url' => 'https://helei112g.github.io/v1/notify/ali',
'return_url' => 'https://helei112g.github.io/',
'return_raw' => false,// 在处理回调时,是否直接返回原始数据,默认为 true
];
<?php
/**
* 招商绑卡操作 签约
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 下午1:39
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Config;
use Payment\Client\Helper;
date_default_timezone_set('Asia/Shanghai');
$cmbConfig = require_once __DIR__ . '/../cmbconfig.php';
$signData = [
'date' => date('Ymd'),
'agr_no' => '430802198004014374',
'serial_no' => time() . rand(1000, 9999),
'mobile' => '13500007108',
'user_id' => '100',
'lon' => '',
'lat' => '',
'riskLevel' => '1',
];
try {
$data = Helper::run(Config::CMB_BIND, $cmbConfig, $signData);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
$btnText = '点我开始签约';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>签约一网通支付</title>
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport">
<meta content="telephone=no" name="format-detection">
<style>
.box{
padding:6px 10px
}
.button {
color: #f5efef;
background-color: #10a737;
border-color: #EEE;
font-weight: 300;
font-size: 16px;
font-family: "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
text-decoration: none;
text-align: center;
line-height: 40px;
height: 100px;
padding: 0 40px;
margin: 0;
width: 100%;
display: inline-block;
appearance: none;
cursor: pointer;
border: none;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-transition-property: all;
transition-property: all;
-webkit-transition-duration: .3s;
transition-duration: .3s;
}
.button-rounded {
border-radius: 4px;
}
.button-uppercase {
text-transform: uppercase;
}
</style>
</head>
<body>
<div class="box">
<form method="post" action="<?php echo $data['url'] ?>">
<input type="hidden" name="<?php echo $data['name'] ?>" value='<?php echo $data['value'] ?>'>
<button type="submit" class="button button-rounded button-uppercase"><?php echo $btnText ?></button>
</form>
</div>
</body>
</html>
\ No newline at end of file
<?php
/**
* 招商一网通支付
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 上午11:55
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Client\Charge;
use Payment\Config;
date_default_timezone_set('Asia/Shanghai');
$cmbConfig = require_once __DIR__ . '/../cmbconfig.php';
$orderNo = time() . rand(1000, 9999);
// 订单信息
$payData = [
'body' => 'test body',
'subject' => 'test subject',
'order_no' => $orderNo,// 招行订单位数变更为32位
'timeout_express' => time() + 600,// 表示必须 600s 内付款
'amount' => '0.01',// 单位为元 ,最小为0.01
'return_param' => 'tatata',
'client_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1',// 客户地址
'date' => date('Ymd'),
'agr_no' => '430802198004014358',// 建议用身份证
'serial_no' => time() . rand(1000, 9999),// 协议开通请求流水号,开通协议时必填
'user_id' => '888',
'mobile' => '13500007107',
'lon' => '',
'lat' => '',
'risk_level' => '3',
];
try {
$data = Charge::run(Config::CMB_CHANNEL_APP, $cmbConfig, $payData);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
$btnText = '点我开始支付';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>一网通支付</title>
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport">
<meta content="telephone=no" name="format-detection">
<style>
.box{
padding:6px 10px
}
.button {
color: #f5efef;
background-color: #10a737;
border-color: #EEE;
font-weight: 300;
font-size: 16px;
font-family: "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
text-decoration: none;
text-align: center;
line-height: 40px;
height: 100px;
padding: 0 40px;
margin: 0;
width: 100%;
display: inline-block;
appearance: none;
cursor: pointer;
border: none;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-transition-property: all;
transition-property: all;
-webkit-transition-duration: .3s;
transition-duration: .3s;
}
.button-rounded {
border-radius: 4px;
}
.button-uppercase {
text-transform: uppercase;
}
</style>
</head>
<body>
<div class="box">
<form method="post" action="<?php echo $data['url'] ?>">
<input type="hidden" name="<?php echo $data['name'] ?>" value='<?php echo $data['value'] ?>'>
<button type="submit" class="button button-rounded button-uppercase"><?php echo $btnText ?></button>
</form>
</div>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>招商一网通支付demo</title>
</head>
<body>
<ul>
<li><a href="charge.php" target="_blank">一网通支付</a></li>
<li><a href="queryPubKey.php" target="_blank">查询招行公钥</a></li>
<li><a href="bindCard.php" target="_blank">签约招商一网通</a></li>
</ul>
<ul>
<li><a href="queryOrder.php" target="_blank">查询支付订单信息</a></li>
<li><a href="queryRefund.php" target="_blank">查询退款订单信息</a></li>
</ul>
<ul>
<li><a href="refund.php" target="_blank">退款订单</a></li>
</ul>
</body>
</html>
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 下午2:18
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Config;
use Payment\Client\Query;
date_default_timezone_set('Asia/Shanghai');
$cmbConfig = require_once __DIR__ . '/../cmbconfig.php';
$data = [
'out_trade_no' => '9336161758',
'date' => '20170428',
'transaction_id' => '17242823500000000010',
];
try {
$ret = Query::run(Config::CMB_CHARGE, $cmbConfig, $data);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
echo json_encode($ret, JSON_UNESCAPED_UNICODE);
\ No newline at end of file
<?php
/**
* 招商一网通支付
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 上午11:55
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Config;
use Payment\Client\Helper;
date_default_timezone_set('Asia/Shanghai');
$cmbConfig = require_once __DIR__ . '/../cmbconfig.php';
$channel = 'cmb_pub_key';
try {
$ret = \Payment\Client\Helper::run(Config::CMB_PUB_KEY, $cmbConfig);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
echo json_encode($ret, JSON_UNESCAPED_UNICODE);
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 下午2:29
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Config;
use Payment\Client\Query;
date_default_timezone_set('Asia/Shanghai');
$cmbConfig = require_once __DIR__ . '/../cmbconfig.php';
$data = [
'out_trade_no' => '9354737499',
'refund_no' => '',// 商户退款流水号,长度不超过20位
'date' => '20170430',
'refund_id' => '',// 银行退款流水号,长度不超过20位
];
try {
$ret = Query::run(Config::CMB_REFUND, $cmbConfig, $data);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
echo json_encode($ret, JSON_UNESCAPED_UNICODE);
\ No newline at end of file
<?php
/**
* 招商退款操作
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 下午6:03
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Config;
use Payment\Client\Refund;
date_default_timezone_set('Asia/Shanghai');
$cmbConfig = require_once __DIR__ . '/../cmbconfig.php';
$refundNo = time() . rand(1000, 9999);
$data = [
'out_trade_no' => '9354737499',
'date' => '20170430',
'refund_no' => $refundNo,
'refund_fee' => 0.01,
'reason' => '测试帐号退款',
'operator_id' => '9999',
];
try {
$ret = Refund::run(Config::CMB_REFUND, $cmbConfig, $data);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
echo json_encode($ret, JSON_UNESCAPED_UNICODE);
\ No newline at end of file
<?php
/**
* 招商一网通配置文件
* Created by PhpStorm.
* User: helei
* Date: 2017/4/27
* Time: 上午11:29
*/
return [
'use_sandbox' => true,// 是否使用 招商测试系统
'branch_no' => 'xxx', // 商户分行号,4位数字
'merchant_no' => 'xxxx',// 商户号,6位数字
'mer_key' => 'xxxxxx',// 秘钥16位,包含大小写字母 数字
// 招商的公钥,建议每天凌晨2:15发起查询招行公钥请求更新公钥。
'cmb_pub_key' => 'xxxxx',
'op_pwd' => 'xxxxx',// 操作员登录密码。
'sign_type' => 'SHA-256',// 签名算法,固定为“SHA-256”
'limit_pay' => [
//'A',
],// 允许支付的卡类型,默认对支付卡种不做限制,储蓄卡和信用卡均可支付 A:储蓄卡支付,即禁止信用卡支付
'notify_url' => 'http://114.215.86.31/__readme/phpinfo.php',// 支付成功的回调
'sign_notify_url' => 'http://114.215.86.31/__readme/phpinfo.php',// 成功签约结果通知地址
'return_url' => 'https://helei112g.github.io/',// 如果是h5支付,可以设置该值,返回到指定页面
'return_raw' => false,// 在处理回调时,是否直接返回原始数据,默认为true
];
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>支付演示demo</title>
</head>
<body>
<ul>
<li><a href="ali/index.html">支付宝支付demo</a></li>
<li><a href="wx/index.html">微信支付demo</a></li>
<li><a href="cmb/index.html">招商一网通支付demo</a></li>
</ul>
</body>
</html>
\ No newline at end of file
<?php
/**
* 第三方支付回调处理
* @author: helei
* @createTime: 2016-07-25 15:57
* @description: 支付通知回调
*/
require_once __DIR__ . '/../autoload.php';
require_once __DIR__ . '/testNotify.php';
use Payment\Common\PayException;
use Payment\Client\Notify;
date_default_timezone_set('Asia/Shanghai');
$aliConfig = require_once __DIR__ . '/aliconfig.php';
$wxConfig = require_once __DIR__ . '/wxconfig.php';
$cmbConfig = require_once __DIR__ . '/cmbconfig.php';
$tlConfig = require_once __DIR__ . '/TLConfig.php';
$callback = new TestNotify();
$type = 'tl_charge';// xx_charge
if (stripos($type, 'ali') !== false) {
$config = $aliConfig;
} elseif (stripos($type, 'wx') !== false) {
$config = $wxConfig;
} elseif (stripos($type, 'cmb') !== false) {
$config = $cmbConfig;
}elseif (stripos($type, 'tl') !== false){
$config = $tlConfig;
}
try {
if (!empty($config)){
// $retData = Notify::getNotifyData($type, $config);// 获取第三方的原始数据,未进行签名检查
// $retData = "{}";
$ret = Notify::run($type, $config, $callback);// 处理回调,内部进行了签名检查
}
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
var_dump($ret);
exit;
<?php
use Payment\Notify\PayNotifyInterface;
use Payment\Config;
/**
* @author: helei
* @createTime: 2016-07-20 18:31
* @description:
*/
/**
* 客户端需要继承该接口,并实现这个方法,在其中实现对应的业务逻辑
* 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 {
// 其它类型的通知
echo '---';
var_dump($data);
}
// 执行业务逻辑,成功后返回true
return true;
}
}
<?php
/**
* 常量配置
*/
class AppConfig{
const APPID = '00000000';
const CUSID = '990440153996000';
const APPKEY = '43df939f1e7f5c6909b3f4b63f893994';
const APIURL = "https://vsp.allinpay.com/apiweb/unitorder";//生产环境
const APIVERSION = '11';
}
?>
\ No newline at end of file
<?php
class AppUtil{
/**
* 将参数数组签名
*/
public static function SignArray(array $array,$appkey){
$array['key'] = $appkey;// 将key放到数组中一起进行排序和组装
ksort($array);
$blankStr = AppUtil::ToUrlParams($array);
$sign = md5($blankStr);
return $sign;
}
public static function ToUrlParams(array $array)
{
$buff = "";
foreach ($array as $k => $v)
{
if($v != "" && !is_array($v)){
$buff .= $k . "=" . $v . "&";
}
}
$buff = trim($buff, "&");
return $buff;
}
/**
* 校验签名
* @param array 参数
* @param unknown_type appkey
*/
public static function ValidSign(array $array,$appkey){
$sign = $array['sign'];
unset($array['sign']);
$array['key'] = $appkey;
$mySign = AppUtil::SignArray($array, $appkey);
return strtolower($sign) == strtolower($mySign);
}
}
?>
\ No newline at end of file
<?php
/**
* 公众号支付
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 下午3:33
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Client\Charge;
use Payment\Config;
date_default_timezone_set('Asia/Shanghai');
$tlConfig = require_once __DIR__ . '/../TLConfig.php';
$orderNo = 12345678;//time() . rand(1000, 9999);
// 订单信息
$payData = [
'body' => '商品名称',
'reqsn' => $orderNo,
// 'timeout_express' => time() + 600,// 表示必须 600s 内付款
'trxamt' => '1',// 微信沙箱模式,需要金额固定为3.01
'randomstr' => '1450432107647',//md5(uniqid(md5(microtime(true)), true)),
'remark' => '备注信息',
// 如果是服务商,请提供以下参数
'validtime' => 6,//微信分配的子商户公众账号ID
'acct' => 'odNM24283i2wwQPzqy8Fxyax2y8U',// 微信支付分配的子商户号
'limit_pay' => 'no_credit'
];
try {
$ret = Charge::run(Config::TL_CHANNEL_LITE, $tlConfig, $payData);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
echo json_encode($ret, JSON_UNESCAPED_UNICODE);
\ No newline at end of file
header("Content-type: text/html; charset=gb2312");
<?php
require_once 'AppConfig.php';
require_once 'AppUtil.php';
$params = array();
foreach($_POST as $key=>$val) {//动态遍历获取所有收到的参数,此步非常关键,因为收银宝以后可能会加字段,动态获取可以兼容由于收银宝加字段而引起的签名异常
$params[$key] = $val;
}
if(count($params)<1){//如果参数为空,则不进行处理
echo "error";
exit();
}
if(AppUtil::ValidSign($params, AppConfig::APPKEY)){//验签成功
//此处进行业务逻辑处理
echo "success";
}
else{
echo "erro";
}
?>
<?php
/**
* 查询支付的订单
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 下午3:43
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Client\Query;
use Payment\Config;
date_default_timezone_set('Asia/Shanghai');
$wxConfig = require_once __DIR__ . '/../TLconfig.php';
$data = [
'randomstr' => '123413',
'reqsn' => '123456',//商户的交易订单号
'trxid' => '123456789123',//支付的收银宝平台流水
];
try {
$ret = Query::run(Config::TL_QUERY, $wxConfig, $data);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
echo json_encode($ret, JSON_UNESCAPED_UNICODE);
\ No newline at end of file
<?php
/**
* 退款处理 金额必须是 3.01
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 下午3:51
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Client\Refund;
use Payment\Config;
date_default_timezone_set('Asia/Shanghai');
$wxConfig = require_once __DIR__ . '/../TLConfig.php';
$data = [
'randomstr' => 'asdasda',
'reqsn' => '14935385689468',
'oldreqsn'=> '123456',
'trxamt' => '100',
'remark' => 'dasd',
];
try {
$ret = Refund::run(Config::TL_REFUND, $wxConfig, $data);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
echo json_encode($ret, JSON_UNESCAPED_UNICODE);
\ No newline at end of file
<?php
header("Content-type:text/html;charset=utf-8");
require_once 'AppConfig.php';
require_once 'AppUtil.php';
function pay(){
$params = array();
$params["cusid"] = AppConfig::CUSID;
$params["appid"] = AppConfig::APPID;
$params["version"] = AppConfig::APIVERSION;
$params["trxamt"] = "1";
$params["reqsn"] = "123456";//订单号,自行生成
$params["paytype"] = "W06";
$params["randomstr"] = "1450432107647";//
$params["body"] = "商品名称";
$params["remark"] = "备注信息";
$params["acct"] = "openid";
$params["limit_pay"] = "no_credit";
$params["notify_url"] = "http://172.16.2.46:8080/vo-apidemo/OrderServlet";
$params["sign"] = AppUtil::SignArray($params,AppConfig::APPKEY);//签名
var_dump(json_encode($params));
$paramsStr = AppUtil::ToUrlParams($params);
$url = AppConfig::APIURL . "/pay";
$rsp = request($url, $paramsStr);
echo "请求返回:".$rsp;
echo "<br/>";
$rspArray = json_decode($rsp, true);
if(validSign($rspArray)){
echo "验签正确,进行业务处理";
}
}
//当天交易用撤销
function cancel(){
$params = array();
$params["cusid"] = AppConfig::CUSID;
$params["appid"] = AppConfig::APPID;
$params["version"] = AppConfig::APIVERSION;
$params["trxamt"] = "1";
$params["reqsn"] = "123456788";
$params["oldreqsn"] = "123456";//原来订单号
$params["randomstr"] = "1450432107647";//
$params["sign"] = AppUtil::SignArray($params,AppConfig::APPKEY);//签名
$paramsStr = AppUtil::ToUrlParams($params);
$url = AppConfig::APIURL . "/cancel";
$rsp = request($url, $paramsStr);
echo "请求返回:".$rsp;
echo "<br/>";
$rspArray = json_decode($rsp, true);
if(validSign($rspArray)){
echo "验签正确,进行业务处理";
}
}
//当天交易请用撤销,非当天交易才用此退货接口
function refund(){
$params = array();
$params["cusid"] = AppConfig::CUSID;
$params["appid"] = AppConfig::APPID;
$params["version"] = AppConfig::APIVERSION;
$params["trxamt"] = "1";
$params["reqsn"] = "1234567889";
$params["oldreqsn"] = "123456";//原来订单号
$params["randomstr"] = "1450432107647";//
$params["sign"] = AppUtil::SignArray($params,AppConfig::APPKEY);//签名
$paramsStr = AppUtil::ToUrlParams($params);
$url = AppConfig::APIURL . "/refund";
$rsp = request($url, $paramsStr);
echo "请求返回:".$rsp;
echo "<br/>";
$rspArray = json_decode($rsp, true);
if(validSign($rspArray)){
echo "验签正确,进行业务处理";
}
}
function query(){
$params = array();
$params["cusid"] = AppConfig::CUSID;
$params["appid"] = AppConfig::APPID;
$params["version"] = AppConfig::APIVERSION;
$params["reqsn"] = "123456";
$params["randomstr"] = "1450432107647";//
$params["sign"] = AppUtil::SignArray($params,AppConfig::APPKEY);//签名
$paramsStr = AppUtil::ToUrlParams($params);
$url = AppConfig::APIURL . "/query";
$rsp = request($url, $paramsStr);
echo "请求返回:".$rsp;
echo "<br/>";
$rspArray = json_decode($rsp, true);
if(validSign($rspArray)){
echo "验签正确,进行业务处理";
}
}
//发送请求操作仅供参考,不为最佳实践
function request($url,$params){
$ch = curl_init();
$this_header = array("content-type: application/x-www-form-urlencoded;charset=UTF-8");
curl_setopt($ch,CURLOPT_HTTPHEADER,$this_header);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)');
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);//如果不加验证,就设false,商户自行处理
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
$output = curl_exec($ch);
curl_close($ch);
return $output;
}
//验签
function validSign($array){
if("SUCCESS"==$array["retcode"]){
$signRsp = strtolower($array["sign"]);
$array["sign"] = "";
$sign = strtolower(AppUtil::SignArray($array, AppConfig::APPKEY));
if($sign==$signRsp){
return TRUE;
}
else {
echo "验签失败:".$signRsp."--".$sign;
}
}
else{
echo $array["retmsg"];
}
return FALSE;
}
pay();
//cancel();
//refund();
//query();
?>
\ No newline at end of file
<?php
/**
* app支付
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 上午11:50
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Client\Charge;
use Payment\Config;
date_default_timezone_set('Asia/Shanghai');
$wxConfig = require_once __DIR__ . '/../wxconfig.php';
$orderNo = time() . rand(1000, 9999);
// 订单信息
$payData = [
'body' => 'test body',
'subject' => 'test subject',
'order_no' => $orderNo,
'timeout_express' => time() + 600,// 表示必须 600s 内付款
'amount' => '3.01',// 微信沙箱模式,需要金额固定为3.01
'return_param' => '123',
'client_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1',// 客户地址
];
try {
$ret = Charge::run(Config::WX_CHANNEL_APP, $wxConfig, $payData);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
echo json_encode($ret, JSON_UNESCAPED_UNICODE);
\ No newline at end of file
<?php
/**
* 刷卡支付
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 下午3:12
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Client\Charge;
use Payment\Config;
date_default_timezone_set('Asia/Shanghai');
$wxConfig = require_once __DIR__ . '/../wxconfig.php';
$orderNo = time() . rand(1000, 9999);
// 订单信息
$payData = [
'body' => 'test body',
'subject' => 'test subject',
'order_no' => $orderNo,
'timeout_express' => time() + 600,// 表示必须 600s 内付款
'amount' => '0.01',// 微信沙箱模式,需要金额固定为0.01
'return_param' => '123',
'client_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1',// 客户地址
'openid' => '',
'product_id' => '123',
'terminal_id' => 'web',// 终端设备号(门店号或收银设备ID) 默认值 web
'auth_code' => '1231212232323123123',
// 如果是服务商,请提供以下参数
'sub_appid' => '',//微信分配的子商户公众账号ID
'sub_mch_id' => '',// 微信支付分配的子商户号
];
try {
$ret = Charge::run(Config::WX_CHANNEL_BAR, $wxConfig, $payData);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
echo json_encode($ret, JSON_UNESCAPED_UNICODE);
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>微信支付demo</title>
</head>
<body>
<ul>
<li><a href="appCharge.php" target="_blank">微信手机app支付</a></li>
<li><a href="qrCharge.php" target="_blank">微信扫码支付</a></li>
<li><a href="pubCharge.php" target="_blank">微信公众号支付</a></li>
<li><a href="liteCharge.php" target="_blank">小程序支付</a></li>
<li><a href="barCharge.php" target="_blank">微信刷卡支付</a></li>
<li><a href="wapCharge.php" target="_blank">H5手机网站支付</a></li>
</ul>
<ul>
<li><a href="queryOrder.php" target="_blank">查询支付的订单</a></li>
<li><a href="queryRefund.php" target="_blank">查询退款的订单</a></li>
<li><a href="queryTransfer.php" target="_blank">查询转账的订单(不支持沙箱)</a></li>
</ul>
<ul>
<li><a href="refund.php" target="_blank">退款订单</a></li>
<li><a href="transfer.php" target="_blank">转账操作(不支持沙箱)</a></li>
</ul>
</body>
</html>
\ No newline at end of file
<?php
/**
* 公众号支付
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 下午3:33
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Client\Charge;
use Payment\Config;
date_default_timezone_set('Asia/Shanghai');
$wxConfig = require_once __DIR__ . '/../wxconfig.php';
$orderNo = time() . rand(1000, 9999);
// 订单信息
$payData = [
'body' => 'test body',
'subject' => 'test subject',
'order_no' => $orderNo,
'timeout_express' => time() + 600,// 表示必须 600s 内付款
'amount' => '3.01',// 微信沙箱模式,需要金额固定为3.01
'return_param' => '123',
'client_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1',// 客户地址
'openid' => 'o-e_mwTXTaxEhBM8xDoj1ui1f950',
'product_id' => '123',
];
try {
$ret = Charge::run(Config::WX_CHANNEL_LITE, $wxConfig, $payData);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
echo json_encode($ret, JSON_UNESCAPED_UNICODE);
\ No newline at end of file
<?php
/**
* 公众号支付
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 下午3:33
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Client\Charge;
use Payment\Config;
date_default_timezone_set('Asia/Shanghai');
$wxConfig = require_once __DIR__ . '/../wxconfig.php';
$orderNo = time() . rand(1000, 9999);
// 订单信息
$payData = [
'body' => 'test body',
'subject' => 'test subject',
'order_no' => $orderNo,
'timeout_express' => time() + 600,// 表示必须 600s 内付款
'amount' => '3.01',// 微信沙箱模式,需要金额固定为3.01
'return_param' => '123',
'client_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1',// 客户地址
'openid' => 'o-e_mwTXTaxEhBM8xDoj1ui1f950',
'product_id' => '123',
// 如果是服务商,请提供以下参数
'sub_appid' => '',//微信分配的子商户公众账号ID
'sub_mch_id' => '',// 微信支付分配的子商户号
'sub_openid' => '',// 用户在子商户appid下的唯一标识
];
try {
$ret = Charge::run(Config::WX_CHANNEL_PUB, $wxConfig, $payData);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
echo json_encode($ret, JSON_UNESCAPED_UNICODE);
\ No newline at end of file
<?php
/**
* 微信扫码支付
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 下午2:46
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Client\Charge;
use Payment\Config;
date_default_timezone_set('Asia/Shanghai');
$wxConfig = require_once __DIR__ . '/../wxconfig.php';
$orderNo = time() . rand(1000, 9999);
// 订单信息
$payData = [
'body' => 'test body',
'subject' => 'test subject',
'order_no' => $orderNo,
'timeout_express' => time() + 600,// 表示必须 600s 内付款
'amount' => '3.01',// 微信沙箱模式,需要金额固定为3.01
'return_param' => '123',
'client_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1',// 客户地址
'openid' => 'ottkCuO1PW1Dnh6PWFffNk-2MPbY',
'product_id' => '123',
// 如果是服务商,请提供以下参数
'sub_appid' => '',//微信分配的子商户公众账号ID
'sub_mch_id' => '',// 微信支付分配的子商户号
];
try {
$ret = Charge::run(Config::WX_CHANNEL_QR, $wxConfig, $payData);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
echo $ret;
\ No newline at end of file
<?php
/**
* 查询支付的订单
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 下午3:43
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Client\Query;
use Payment\Config;
date_default_timezone_set('Asia/Shanghai');
$wxConfig = require_once __DIR__ . '/../wxconfig.php';
$data = [
'out_trade_no' => '14935505602169',
'transaction_id' => '20170430190922203640695',
];
try {
$ret = Query::run(Config::WX_CHARGE, $wxConfig, $data);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
echo json_encode($ret, JSON_UNESCAPED_UNICODE);
\ No newline at end of file
<?php
/**
* 查询退款订单
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 下午3:43
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Client\Query;
use Payment\Config;
date_default_timezone_set('Asia/Shanghai');
$wxConfig = require_once __DIR__ . '/../wxconfig.php';
$data = [
'out_trade_no' => '14935385689468',
'refund_no' => '14935506214648',
'transaction_id' => '12345678920170430191024123337865',
'refund_id' => '1234567892017043019102412333',
];
try {
$ret = Query::run(Config::WX_REFUND, $wxConfig, $data);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
echo json_encode($ret, JSON_UNESCAPED_UNICODE);
\ No newline at end of file
<?php
/**
* 转账查询 没有沙箱模式
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 下午3:48
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Client\Query;
use Payment\Config;
date_default_timezone_set('Asia/Shanghai');
$wxConfig = require_once __DIR__ . '/../wxconfig.php';
$data = [
'trans_no' => '1489852933',
];
try {
$ret = Query::run(Config::WX_CHARGE, $wxConfig, $data);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
echo json_encode($ret, JSON_UNESCAPED_UNICODE);
\ No newline at end of file
<?php
/**
* 退款处理 金额必须是 3.01
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 下午3:51
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Client\Refund;
use Payment\Config;
use Payment\Common\WxConfig;
date_default_timezone_set('Asia/Shanghai');
$wxConfig = require_once __DIR__ . '/../wxconfig.php';
$refundNo = time() . rand(1000, 9999);
$data = [
'out_trade_no' => '14935385689468',
'total_fee' => '3.01',
'refund_fee' => '3.01',
'refund_no' => $refundNo,
'refund_account' => WxConfig::REFUND_RECHARGE,// REFUND_RECHARGE:可用余额退款 REFUND_UNSETTLED:未结算资金退款(默认)
];
try {
$ret = Refund::run(Config::WX_REFUND, $wxConfig, $data);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
echo json_encode($ret, JSON_UNESCAPED_UNICODE);
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 下午4:13
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Client\Transfer;
use Payment\Config;
date_default_timezone_set('Asia/Shanghai');
$wxConfig = require_once __DIR__ . '/../wxconfig.php';
$data = [
'trans_no' => time(),
'openid' => 'o-e_mwTXTaxEhBM8xDoj1ui1f950',
'check_name' => 'NO_CHECK',// NO_CHECK:不校验真实姓名 FORCE_CHECK:强校验真实姓名 OPTION_CHECK:针对已实名认证的用户才校验真实姓名
'payer_real_name' => '何磊',
'amount' => '1',
'desc' => '测试转账',
'spbill_create_ip' => '127.0.0.1',
];
try {
$ret = Transfer::run(Config::WX_TRANSFER, $wxConfig, $data);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
echo json_encode($ret, JSON_UNESCAPED_UNICODE);
\ No newline at end of file
<?php
/**
* 微信wap支付
* Created by PhpStorm.
* User: helei
* Date: 2017/4/30
* Time: 下午3:40
*/
require_once __DIR__ . '/../../autoload.php';
use Payment\Common\PayException;
use Payment\Client\Charge;
use Payment\Config;
date_default_timezone_set('Asia/Shanghai');
$wxConfig = require_once __DIR__ . '/../wxconfig.php';
$orderNo = time() . rand(1000, 9999);
// 订单信息
$payData = [
'body' => 'test body',
'subject' => 'test subject',
'order_no' => $orderNo,
'timeout_express' => time() + 600,// 表示必须 600s 内付款
'amount' => '3.01',// 微信沙箱模式,需要金额固定为3.01
'return_param' => '123',
'client_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1',// 客户地址
//{"h5_info": {"type":"Wap","wap_url": "https://pay.qq.com","wap_name": "腾讯充值"}}
'scene_info' => [
'type' => 'Wap',// IOS Android Wap 腾讯建议 IOS ANDROID 采用app支付
'wap_url' => 'helei112g.github.io',//自己的 wap 地址
'wap_name' => '测试充值',
],
];
try {
$url = Charge::run(Config::WX_CHANNEL_WAP, $wxConfig, $payData);
} catch (PayException $e) {
echo $e->errorMessage();
exit;
}
echo $url;
\ No newline at end of file
<?php
/**
* @author: helei
* @createTime: 2016-08-01 11:37
* @description: 微信配置文件
*/
return [
'use_sandbox' => true,// 是否使用 微信支付仿真测试系统
'app_id' => 'wxxxxxx', // 公众账号ID
'mch_id' => 'xxxxx',// 商户id
'md5_key' => 'xxxxxxx',// md5 秘钥
'app_cert_pem' => dirname(__FILE__) . DIRECTORY_SEPARATOR . 'wx' . DIRECTORY_SEPARATOR . 'pem' . DIRECTORY_SEPARATOR . 'weixin_app_cert.pem',
'app_key_pem' => dirname(__FILE__) . DIRECTORY_SEPARATOR . 'wx' . DIRECTORY_SEPARATOR . 'pem' . DIRECTORY_SEPARATOR . 'weixin_app_key.pem',
'sign_type' => 'MD5',// MD5 HMAC-SHA256
'limit_pay' => [
//'no_credit',
],// 指定不能使用信用卡支付 不传入,则均可使用
'fee_type' => 'CNY',// 货币类型 当前仅支持该字段
'notify_url' => 'https://helei112g.github.io/v1/notify/wx',
'redirect_url' => 'https://helei112g.github.io/',// 如果是h5支付,可以设置该值,返回到指定页面
'return_raw' => false,// 在处理回调时,是否直接返回原始数据,默认为true
];
\ No newline at end of file
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnxj/9qwVfgoUh/y2W89L6BkRA
FljhNhgPdyPuBV64bfQNN1PjbCzkIM6qRdKBoLPXmKKMiFYnkd6rAoprih3/PrQE
B/VsW8OoM8fxn67UDYuyBTqA23MML9q1+ilIZwBC2AQ2UBVOrFXfFl75p6/B5Ksi
NG9zpgmLCUYuLkxpLQIDAQAB
-----END PUBLIC KEY-----
\ No newline at end of file
-----BEGIN CERTIFICATE-----
MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy
dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1
MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx
dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B
AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f
BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A
cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC
AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ
MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm
aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw
ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj
IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF
MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA
A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y
7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh
1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4
-----END CERTIFICATE-----
\ No newline at end of file
<?php
/**
* @author: yeran
* @createTime: 2018-04-22 17:42
* @description: 交易撤销接口
*/
namespace Payment;
use Payment\Charge\Wx\TLCancel;
use Payment\Common\BaseStrategy;
use Payment\Common\PayException;
/**
* Class CancelContext
* @package Payment
*/
class CancelContext
{
/**
* 退款的渠道
* @var BaseStrategy
*/
protected $cancelHandler;
/**
* 设置对应的退款渠道
* @param string $channel 退款渠道
* - @see Config
*
* @param array $config 配置文件
* @throws PayException
* @author yeran
*/
public function initCancelHandler($channel, array $config)
{
try {
switch ($channel) {
case Config::TL_CANCEL:
$this->cancelHandler = new TLCancel($config);
break;
default:
throw new PayException('当前仅支持:通联支付平台');
}
} catch (PayException $e) {
throw $e;
}
}
/**
* 通过环境类调用交易撤销操作
*
* @param array $data
*
* @return array
* @throws PayException
* @author yeran
*/
public function cancel(array $data)
{
if (! $this->cancelHandler instanceof BaseStrategy) {
throw new PayException('请检查初始化是否正确');
}
try {
return $this->cancelHandler->handle($data);
} catch (PayException $e) {
throw $e;
}
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-14 18:20
* @description: 支付宝移动支付接口
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment\Charge\Ali;
use Payment\Common\Ali\AliBaseStrategy;
use Payment\Common\Ali\Data\Charge\AppChargeData;
use Payment\Common\AliConfig;
use Payment\Utils\ArrayUtil;
use Payment\Utils\StrUtil;
class AliAppCharge extends AliBaseStrategy
{
/**
* 获取支付对应的数据完成类
* @return string
* @author helei
*/
public function getBuildDataClass()
{
$this->config->method = AliConfig::APP_PAY_METHOD;
// 以下两种方式任选一种
return AppChargeData::class;
}
/**
* 返回app 支付数据 sign 在签名内部,已经进行 base64_encode 了
* @param array $data
* @return string
*/
protected function retData(array $data)
{
$sign = $data['sign'];
unset($data['sign']);
$data = ArrayUtil::arraySort($data);// 因为签名时进行了排序,此处返回时也要进行排序,否则支付验证签名无法通过
foreach ($data as &$value) {
$value = StrUtil::characet($value, $this->config->charset);
}
$data['sign'] = $sign;// sign 需要放在末尾
return http_build_query($data);
}
}
<?php
namespace Payment\Charge\Ali;
use Payment\Common\Ali\AliBaseStrategy;
use Payment\Common\Ali\Data\Charge\BarChargeData;
use Payment\Common\AliConfig;
use Payment\Common\PayException;
/**
* 商户扫用户的二维码
*
* Class AliBarCharge
* @package Payment\Charge\Weixin
*
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
class AliBarCharge extends AliBaseStrategy
{
public function getBuildDataClass()
{
$this->config->method = AliConfig::BAR_PAY_METHOD;
return BarChargeData::class;
}
/**
* 处理扫码支付的返回值
* @param array $ret
* $data = [
'code' => 10000,
'msg' => 'Success',
'buyer_logon_id' => 'day***@gmail.com',
'buyer_pay_amount' => '0.01',
'buyer_user_id' => '2088002162809334',
'fund_bill_list' => [
['amount' => '0.01', 'fund_channel' => 'ALIPAYACCOUNT'],
],
'gmt_payment' => '2017-03-05 22:27:46',
'open_id' => '20880008025007264081318860117433',
'out_trade_no' => '14887240631516',
'point_amount' => '0.00',
'receipt_amount' => '0.01',
'total_amount' => '0.01',
'trade_no' => '2017030521001004330274482163',
];
*
* @throws PayException
* @return string 可生产二维码的uri
* @author helei
*/
protected function retData(array $ret)
{
$url = parent::retData($ret);
// 发起网络请求
try {
$data = $this->sendReq($url);
} catch (PayException $e) {
throw $e;
}
return $data;
}
}
<?php
namespace Payment\Charge\Ali;
use Payment\Common\Ali\AliBaseStrategy;
use Payment\Common\Ali\Data\Charge\QrChargeData;
use Payment\Common\AliConfig;
use Payment\Common\PayException;
/**
* 支付宝扫码支付- 用户扫商户生成的二维码完成支付
*
* Class AliQrCharge
* @package Payment\Charge\Weixin
*
* @link https://github.com/helei112g/payment
* @link https://helei112g.github.io/
*/
class AliQrCharge extends AliBaseStrategy
{
public function getBuildDataClass()
{
$this->config->method = AliConfig::QR_PAY_METHOD;
return QrChargeData::class;
}
/**
* 处理扫码支付的返回值
* @param array $ret
*
* @throws PayException
* @return string 可生产二维码的uri
* @author helei
*/
protected function retData(array $ret)
{
$url = parent::retData($ret);
// 发起网络请求
try {
$data = $this->sendReq($url);
} catch (PayException $e) {
throw $e;
}
return $data['qr_code'];
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-14 18:19
* @description: 支付宝 手机网站支付 接口
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment\Charge\Ali;
use Payment\Common\Ali\AliBaseStrategy;
use Payment\Common\Ali\Data\Charge\WapChargeData;
use Payment\Common\AliConfig;
class AliWapCharge extends AliBaseStrategy
{
/**
* 获取支付对应的数据完成类
* @return string
* @author helei
*/
public function getBuildDataClass()
{
$this->config->method = AliConfig::WAP_PAY_METHOD;
// 以下两种方式任选一种
return WapChargeData::class;
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-14 17:56
* @description: 支付宝 即时到账 接口
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment\Charge\Ali;
use Payment\Common\Ali\AliBaseStrategy;
use Payment\Common\Ali\Data\Charge\WebChargeData;
use Payment\Common\AliConfig;
class AliWebCharge extends AliBaseStrategy
{
/**
* 获取支付对应的数据完成类
* @return string
* @author helei
*/
public function getBuildDataClass()
{
$this->config->method = AliConfig::PC_PAY_METHOD;
// 以下两种方式均可以
return WebChargeData::class;
//return 'Payment\Common\Ali\Data\Charge\WebChargeData';
}
}
<?php
namespace Payment\Charge\Cmb;
use Payment\Common\Cmb\CmbBaseStrategy;
use Payment\Common\Cmb\Data\Charge\ChargeData;
/**
* 一网通支付API 接口
* Created by PhpStorm.
* User: helei
* Date: 2017/4/27
* Time: 下午12:36
*/
class CmbCharge extends CmbBaseStrategy
{
public function getBuildDataClass()
{
$this->config->getewayUrl = 'https://netpay.cmbchina.com/netpayment/BaseHttp.dll?MB_EUserPay';
if ($this->config->useSandbox) {// 测试
$this->config->getewayUrl = 'http://121.15.180.66:801/NetPayment/BaseHttp.dll?MB_EUserPay';
}
return ChargeData::class;
}
}
\ No newline at end of file
<?php
/**
* Created by IntelliJ IDEA.
* User: yeran
* Date: 2018/4/23
* Time: 下午5:44
*/
namespace Payment\Charge\Wx;
use Payment\Common\TLConfig;
use Payment\Common\TLpay\Data\Cancel\TLCancelData;
use Payment\Common\TLpay\TLBaseStrategy;
/**
* Class TLCancel
* @package Payment\Charge\Wx
*/
class TLCancel extends TLBaseStrategy{
/**
* 交易撤销 的url
* @return null|string
* @author helei
*/
protected function getReqUrl()
{
return TLConfig::ORDER_CANCEL_URL;
}
/**
* 获取交易退款对应的数据完成类
* @return TLCancelData
* @author helei
*/
public function getBuildDataClass()
{
// TODO: Implement getBuildDataClass() method.
$this->config->paytype = 'W06';
return TLCancelData::class;
}
}
\ No newline at end of file
<?php
/**
* @author: helei
* @createTime: 2016-07-14 18:28
* @description: 微信 公众号 支付接口
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment\Charge\TLpay;
use Payment\Common\TLpay\Data\BackTLChargeData;
use Payment\Common\TLpay\Data\Charge\TLChargeData;
use Payment\Common\TLpay\TLBaseStrategy;
/**
* Class TLLiteCharge
*
* 通联支付-微信小程序支付
*
* @package Payment\Charge\TLpay
* anthor helei
*/
class TLLiteCharge extends TLBaseStrategy
{
public function getBuildDataClass()
{
$this->config->paytype = 'W06';
return TLChargeData::class;
}
/**
* 处理返回值。直接返回与微信小程序文档对应的字段
* @param array $ret
*
* @return array $backData 包含以下键
*
* ```php
* $backData = [
* 'appId' => '', // 小程序id
* 'package' => '', // 订单详情扩展字符串 统一下单接口返回的prepay_id参数值,提交格式如:prepay_id=***
* 'nonceStr' => '', // 随机字符串
* 'timeStamp' => '', // 时间戳
* 'signType' => '', // 签名算法,暂支持MD5
* 'paySign' => '', // 签名
* ];
* ```
* @author helei
*/
protected function retData(array $ret)
{
return json_decode($ret['payinfo'],true);
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-14 17:56
* @description: 微信 app 支付接口
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment\Charge\Wx;
use Payment\Common\Weixin\Data\BackAppChargeData;
use Payment\Common\Weixin\Data\Charge\AppChargeData;
use Payment\Common\Weixin\WxBaseStrategy;
class WxAppCharge extends WxBaseStrategy
{
public function getBuildDataClass()
{
$this->config->tradeType = 'APP';
return AppChargeData::class;
}
/**
* 处理APP支付的返回值。直接返回与微信文档对应的字段
* @param array $ret
*
* @return array $data
*
* ```php
* $data = [
* 'appid' => '', // 应用ID
* 'partnerid' => '', // 商户号
* 'prepayid' => '', // 预支付交易会话ID
* 'package' => '', // 扩展字段 固定值:Sign=WXPay
* 'noncestr' => '', // 随机字符串
* 'timestamp' => '', // 时间戳
* 'sign' => '', // 签名
* ];
* ```
* @author helei
*/
protected function retData(array $ret)
{
$back = new BackAppChargeData($this->config, $ret);
$back->setSign();
$backData = $back->getData();
return $backData;
}
}
<?php
/**
* @author: helei
* @createTime: 2017-03-06 18:29
* @description: 微信 刷卡支付 对应支付宝的条码支付
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment\Charge\Wx;
use Payment\Common\Weixin\Data\Charge\BarChargeData;
use Payment\Common\Weixin\WxBaseStrategy;
use Payment\Common\WxConfig;
class WxBarCharge extends WxBaseStrategy
{
public function getBuildDataClass()
{
return BarChargeData::class;
}
/**
* 刷卡支付 的请求地址是另外一个
* @return string
*/
protected function getReqUrl()
{
return WxConfig::MICROPAY_URL;
}
protected function retData(array $ret)
{
$ret['total_fee'] = bcdiv($ret['total_fee'], 100, 2);
$ret['cash_fee'] = bcdiv($ret['cash_fee'], 100, 2);
if ($this->config->returnRaw) {
return $ret;
}
return $ret;
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-14 18:28
* @description: 微信 公众号 支付接口
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment\Charge\Wx;
use Payment\Common\Weixin\Data\BackPubChargeData;
use Payment\Common\Weixin\Data\Charge\PubChargeData;
use Payment\Common\Weixin\WxBaseStrategy;
/**
* Class WxPubCharge
*
* 微信公众号支付
*
* @package Payment\Charge\Weixin
* anthor helei
*/
class WxPubCharge extends WxBaseStrategy
{
public function getBuildDataClass()
{
$this->config->tradeType = 'JSAPI';
return PubChargeData::class;
}
/**
* 处理公众号支付的返回值。直接返回与微信文档对应的字段
* @param array $ret
*
* @return string $data 包含以下键
*
* ```php
* $data = [
* 'appId' => '', // 公众号id
* 'package' => '', // 订单详情扩展字符串 统一下单接口返回的prepay_id参数值,提交格式如:prepay_id=***
* 'nonceStr' => '', // 随机字符串
* 'timeStamp' => '', // 时间戳
* 'signType' => '', // 签名算法,暂支持MD5
* 'paySign' => '', // 签名
* ];
* ```
* @author helei
*/
protected function retData(array $ret)
{
$back = new BackPubChargeData($this->config, $ret);
$back->setSign();
$backData = $back->getData();
$backData['paySign'] = $backData['sign'];
// 移除sign
unset($backData['sign']);
// 公众号支付返回数组结构
return $backData;
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-14 18:29
* @description: 微信 扫码支付 主要用于网站上
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment\Charge\Wx;
use Payment\Common\Weixin\Data\Charge\QrChargeData;
use Payment\Common\Weixin\WxBaseStrategy;
class WxQrCharge extends WxBaseStrategy
{
public function getBuildDataClass()
{
$this->config->tradeType = 'NATIVE';// 微信文档这里写错了
return QrChargeData::class;
}
/**
* 处理扫码支付的返回值
* @param array $ret
* @return string 可生产二维码的uri
* @author helei
*/
protected function retData(array $ret)
{
if ($this->config->returnRaw) {
return $ret;
}
// 扫码支付,返回链接
return $ret['code_url'];
}
}
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/3/6
* Time: 下午5:44
*/
namespace Payment\Charge\Wx;
use Payment\Common\Weixin\Data\Charge\WapChargeData;
use Payment\Common\Weixin\WxBaseStrategy;
/**
* 微信h5支付
* Class WxWapCharge
* @package Payment\Charge\Weixin
*/
class WxWapCharge extends WxBaseStrategy
{
public function getBuildDataClass()
{
$this->config->tradeType = 'MWEB';
return WapChargeData::class;
}
/**
* 这里由于
* @param array $ret
* @return mixed
*/
protected function retData(array $ret)
{
if ($this->config->returnRaw) {
return $ret;
}
$wabUrl = $ret['mweb_url'];
if ($this->config->returnUrl) {
$wabUrl .= '&redirect_url=' . urlencode($this->config->returnUrl);
}
return $wabUrl;
}
}
\ No newline at end of file
<?php
/**
* @author: helei
* @createTime: 2016-07-14 17:42
* @description: 暴露给客户端调用的接口
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment;
use Payment\Charge\Ali\AliAppCharge;
use Payment\Charge\Ali\AliBarCharge;
use Payment\Charge\Ali\AliWapCharge;
use Payment\Charge\Ali\AliWebCharge;
use Payment\Charge\Ali\AliQrCharge;
use Payment\Charge\Cmb\CmbCharge;
use Payment\Charge\TLpay\TLLiteCharge;
use Payment\Charge\Wx\WxAppCharge;
use Payment\Charge\Wx\WxBarCharge;
use Payment\Charge\Wx\WxPubCharge;
use Payment\Charge\Wx\WxQrCharge;
use Payment\Charge\Wx\WxWapCharge;
use Payment\Common\BaseStrategy;
use Payment\Common\PayException;
/**
* Class ChargeContext
*
* 支付的上下文类
*
* @package Payment
* anthor helei
*/
class ChargeContext
{
/**
* 支付的渠道
* @var BaseStrategy
*/
protected $channel;
/**
* 设置对应的支付渠道
* @param string $channel 支付渠道
* - @see Config
* @param array $config 配置文件
* @throws PayException
* @author helei
*/
public function initCharge($channel, array $config)
{
// 初始化时,可能抛出异常,再次统一再抛出给客户端进行处理
try {
switch ($channel) {
case Config::ALI_CHANNEL_WAP:
$this->channel = new AliWapCharge($config);
break;
case Config::ALI_CHANNEL_APP:
$this->channel = new AliAppCharge($config);
break;
case Config::ALI_CHANNEL_WEB:
$this->channel = new AliWebCharge($config);
break;
case Config::ALI_CHANNEL_QR:
$this->channel = new AliQrCharge($config);
break;
case Config::ALI_CHANNEL_BAR:
$this->channel = new AliBarCharge($config);
break;
case Config::WX_CHANNEL_APP:
$this->channel = new WxAppCharge($config);
break;
case Config::WX_CHANNEL_LITE:// 小程序支付与公众号支付一样,仅仅是客户端的调用方式不同
case Config::WX_CHANNEL_PUB:
$this->channel = new WxPubCharge($config);
break;
case Config::WX_CHANNEL_WAP:
$this->channel = new WxWapCharge($config);
break;
case Config::WX_CHANNEL_QR:
$this->channel = new WxQrCharge($config);
break;
case Config::WX_CHANNEL_BAR:
$this->channel = new WxBarCharge($config);
break;
case Config::CMB_CHANNEL_WAP:
case Config::CMB_CHANNEL_APP:
$this->channel = new CmbCharge($config);
break;
case Config::TL_CHANNEL_LITE:
$this->channel = new TLLiteCharge($config);
break;
default:
throw new PayException('当前支持:支付宝 微信 招商一网通 通联支付');
}
} catch (PayException $e) {
throw $e;
}
}
/**
* 通过环境类调用支付
* @param array $data
*
* ```php
* $payData = [
* "order_no" => createPayid(),
* "amount" => '0.01',// 单位为元 ,最小为0.01
* "client_ip" => '127.0.0.1',
* "subject" => '测试支付',
* "body" => '支付接口测试',
* "extra_param" => '',
* ];
* ```
*
* @return array
* @throws PayException
* @author helei
*/
public function charge(array $data)
{
if (! $this->channel instanceof BaseStrategy) {
throw new PayException('请检查初始化是否正确');
}
try {
return $this->channel->handle($data);
} catch (PayException $e) {
throw $e;
}
}
}
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/3/4
* Time: 下午5:40
*/
namespace Payment\Client;
use Payment\ChargeContext;
use Payment\Common\PayException;
use Payment\Config;
class Charge
{
private static $supportChannel = [
Config::ALI_CHANNEL_APP,// 支付宝 APP 支付
Config::ALI_CHANNEL_WAP, // 支付宝手机网页支付
Config::ALI_CHANNEL_WEB, // 支付宝电脑网站支付
Config::ALI_CHANNEL_QR, // 支付宝当面付-扫码支付
Config::ALI_CHANNEL_BAR,// 支付宝当面付-条码支付
Config::WX_CHANNEL_APP,// 微信 APP 支付
Config::WX_CHANNEL_PUB,// 微信公众号支付
Config::WX_CHANNEL_QR,// 微信公众号扫码支付
Config::WX_CHANNEL_BAR,// 微信刷卡支付
Config::WX_CHANNEL_WAP,// 微信 WAP 支付(此渠道仅针对特定客户开放)
Config::WX_CHANNEL_LITE,// 微信小程序支付
Config::CMB_CHANNEL_APP,// 招行一网通
'applepay_upacp',// Apple Pay
Config::TL_CHANNEL_LITE,
];
/**
* 异步通知类
* @var ChargeContext
*/
protected static $instance;
protected static function getInstance($channel, $config)
{
if (is_null(self::$instance)) {
static::$instance = new ChargeContext();
try {
static::$instance->initCharge($channel, $config);
} catch (PayException $e) {
throw $e;
}
}
return static::$instance;
}
/**
* @param string $channel
* @param array $config
* @param array $metadata
*
* @return mixed
* @throws PayException
*/
public static function run($channel, $config, $metadata)
{
if (! in_array($channel, self::$supportChannel)) {
throw new PayException('sdk当前不支持该支付渠道,当前仅支持:' . implode(',', self::$supportChannel));
}
try {
$instance = self::getInstance($channel, $config);
$ret = $instance->charge($metadata);
} catch (PayException $e) {
throw $e;
}
return $ret;
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/4/28
* Time: 上午10:44
*/
namespace Payment\Client;
use Payment\Common\PayException;
use Payment\Config;
use Payment\HelperContext;
/**
* 提供给客户端的辅助类
* Class Helper
* @package Payment\Client
*/
class Helper
{
private static $supportChannel = [
Config::CMB_BIND,// 招商绑卡操作
Config::CMB_PUB_KEY,// 招商公钥查询操作
];
/**
* 异步通知类
* @var HelperContext
*/
protected static $instance;
protected static function getInstance($channel, $config)
{
if (is_null(self::$instance)) {
static::$instance = new HelperContext();
try {
static::$instance->initHelper($channel, $config);
} catch (PayException $e) {
throw $e;
}
}
return static::$instance;
}
/**
* @param string $channel
* @param array $config
* @param array $metadata
*
* @return mixed
* @throws PayException
*/
public static function run($channel, $config, array $metadata = [])
{
if (! in_array($channel, self::$supportChannel)) {
throw new PayException('sdk当前不支持该渠道,当前仅支持:' . implode(',', self::$supportChannel));
}
try {
$instance = self::getInstance($channel, $config);
$ret = $instance->helper($metadata);
} catch (PayException $e) {
throw $e;
}
return $ret;
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/3/7
* Time: 下午6:29
*/
namespace Payment\Client;
use Payment\Common\PayException;
use Payment\Config;
use Payment\Notify\PayNotifyInterface;
use Payment\NotifyContext;
class Notify
{
private static $supportChannel = [
Config::ALI_CHARGE,// 支付宝
Config::WX_CHARGE,// 微信
Config::CMB_CHARGE,// 招行一网通
'applepay_upacp',// Apple Pay
Config::TL_CHARGE
];
/**
* 异步通知类
* @var NotifyContext
*/
protected static $instance;
protected static function getInstance($type, $config)
{
if (is_null(self::$instance)) {
static::$instance = new NotifyContext();
try {
static::$instance->initNotify($type, $config);
} catch (PayException $e) {
throw $e;
}
}
return static::$instance;
}
/**
* 执行异步工作
* @param string $type
* @param array $config
* @param PayNotifyInterface $callback
* @return array
* @throws PayException
*/
public static function run($type, $config, $callback)
{
if (! in_array($type, self::$supportChannel)) {
throw new PayException('sdk当前不支持该异步方式,当前仅支持:' . implode(',', self::$supportChannel));
}
try {
$instance = self::getInstance($type, $config);
$ret = $instance->notify($callback);
} catch (PayException $e) {
throw $e;
}
return $ret;
}
/**
* 返回异步通知的结果
* @param $type
* @param $config
* @return array|false
* @throws PayException
*/
public static function getNotifyData($type, $config)
{
try {
$instance = self::getInstance($type, $config);
return $instance->getNotifyData();
} catch (PayException $e) {
throw $e;
}
}
}
\ No newline at end of file
<?php
/**
* Created by IntelliJ IDEA.
* User: yeran
* Date: 2018/4/23
* Time: 下午6:07
*/
namespace Payment\Client;
use Payment\CancelContext;
use Payment\Common\PayException;
use Payment\Config;
class OrderCancel {
private static $supportChannel = [
Config::TL_CHANNEL_LITE,
];
/**
* @var CancelContext
*/
protected static $instance;
protected static function getInstance($channel, $config)
{
if (is_null(self::$instance)) {
static::$instance = new CancelContext();
try {
static::$instance->initCancelHandler($channel, $config);
} catch (PayException $e) {
throw $e;
}
}
return static::$instance;
}
/**
* @param string $channel
* @param array $config
* @param array $metadata
*
* @return mixed
* @throws PayException
*/
public static function run($channel, $config, $metadata)
{
if (! in_array($channel, self::$supportChannel)) {
throw new PayException('sdk当前不支持该支付渠道,当前仅支持:' . implode(',', self::$supportChannel));
}
try {
$instance = self::getInstance($channel, $config);
$ret = $instance->cancel($metadata);
} catch (PayException $e) {
throw $e;
}
return $ret;
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/3/6
* Time: 下午9:08
*/
namespace Payment\Client;
use Payment\Common\PayException;
use Payment\Config;
use Payment\QueryContext;
/**
* 查询的客户端类
* Class Query
* @package Payment\Client
*/
class Query
{
protected static $supportType = [
Config::ALI_CHARGE,
Config::ALI_REFUND,
Config::ALI_TRANSFER,
Config::ALI_RED,
Config::WX_CHARGE,
Config::WX_REFUND,
Config::WX_RED,
Config::WX_TRANSFER,
Config::CMB_CHARGE,
Config::CMB_REFUND,
Config::TL_QUERY
];
/**
* 异步通知类
* @var QueryContext
*/
protected static $instance;
protected static function getInstance($queryType, $config)
{
if (is_null(self::$instance)) {
static::$instance = new QueryContext();
try {
static::$instance->initQuery($queryType, $config);
} catch (PayException $e) {
throw $e;
}
}
return static::$instance;
}
/**
* @param string $queryType
* @param array $config
* @param array $metadata
* @return array
* @throws PayException
*/
public static function run($queryType, $config, $metadata)
{
if (! in_array($queryType, self::$supportType)) {
throw new PayException('sdk当前不支持该类型查询,当前仅支持:' . implode(',', self::$supportType) . __LINE__);
}
try {
$instance = self::getInstance($queryType, $config);
$ret = $instance->query($metadata);
} catch (PayException $e) {
throw $e;
}
return $ret;
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/3/7
* Time: 上午10:50
*/
namespace Payment\Client;
use Payment\Common\PayException;
use Payment\Config;
use Payment\RefundContext;
/**
* 退款操作客户端接口
* Class Refund
* @package Payment\Client
*/
class Refund
{
private static $supportChannel = [
Config::ALI_REFUND,// 支付宝
Config::WX_REFUND,// 微信
Config::CMB_REFUND,// 招行一网通
'applepay_upacp',// Apple Pay
Config::TL_REFUND
];
/**
* 异步通知类
* @var RefundContext
*/
protected static $instance;
protected static function getInstance($channel, $config)
{
if (is_null(self::$instance)) {
static::$instance = new RefundContext();
try {
static::$instance->initRefund($channel, $config);
} catch (PayException $e) {
throw $e;
}
}
return static::$instance;
}
public static function run($channel, $config, $refundData)
{
if (! in_array($channel, self::$supportChannel)) {
throw new PayException('sdk当前不支持该退款渠道,当前仅支持:' . implode(',', self::$supportChannel));
}
try {
$instance = self::getInstance($channel, $config);
$ret = $instance->refund($refundData);
} catch (PayException $e) {
throw $e;
}
return $ret;
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/3/7
* Time: 下午3:16
*/
namespace Payment\Client;
use Payment\Common\PayException;
use Payment\Config;
use Payment\TransferContext;
class Transfer
{
private static $supportChannel = [
Config::ALI_TRANSFER,// 支付宝
Config::WX_TRANSFER,// 微信
'cmb_wallet',// 招行一网通
'applepay_upacp',// Apple Pay
];
/**
* 异步通知类
* @var TransferContext
*/
protected static $instance;
protected static function getInstance($channel, $config)
{
if (is_null(self::$instance)) {
static::$instance = new TransferContext();
try {
static::$instance->initTransfer($channel, $config);
} catch (PayException $e) {
throw $e;
}
}
return static::$instance;
}
/**
* @param $channel
* @param $config
* @param $metadata
*
* @return array
* @throws PayException
*/
public static function run($channel, $config, $metadata)
{
if (! in_array($channel, self::$supportChannel)) {
throw new PayException('sdk当前不支持该退款渠道,当前仅支持:' . implode(',', self::$supportChannel));
}
try {
$instance = self::getInstance($channel, $config);
$ret = $instance->transfer($metadata);
} catch (PayException $e) {
throw $e;
}
return $ret;
}
}
\ No newline at end of file
<?php
/**
* @author: helei
* @createTime: 2016-07-15 17:10
* @description: 支付宝支付接口的基类。
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment\Common\Ali;
use Payment\Common\AliConfig;
use Payment\Common\BaseData;
use Payment\Common\BaseStrategy;
use Payment\Common\PayException;
use Payment\Config;
use Payment\Utils\ArrayUtil;
use Payment\Utils\Curl;
use Payment\Utils\Rsa2Encrypt;
use Payment\Utils\RsaEncrypt;
use Payment\Utils\StrUtil;
abstract class AliBaseStrategy implements BaseStrategy
{
/**
* 支付宝的配置文件
* @var AliConfig $config
*/
protected $config;
/**
* 支付数据
* @var BaseData $reqData
*/
protected $reqData;
/**
* AliBaseStrategy constructor.
* @param array $config
* @throws PayException
*/
public function __construct(array $config)
{
/* 设置内部字符编码为 UTF-8 */
mb_internal_encoding("UTF-8");
try {
$this->config = new AliConfig($config);
} catch (PayException $e) {
throw $e;
}
}
public function handle(array $data)
{
$buildClass = $this->getBuildDataClass();
try {
$this->reqData = new $buildClass($this->config, $data);
} catch (PayException $e) {
throw $e;
}
$this->reqData->setSign();
$data = $this->reqData->getData();
return $this->retData($data);
}
/**
* 支付宝业务发送网络请求,并验证签名
* @param $url
* @return mixed
* @throws PayException
*/
protected function sendReq($url)
{
// 发起网络请求
$curl = new Curl();
$responseTxt = $curl->set([
'CURLOPT_SSL_VERIFYPEER' => true,
'CURLOPT_SSL_VERIFYHOST' => 2,
'CURLOPT_HEADER' => 0,// 为了便于解析,将头信息过滤掉
//'CURLOPT_CAINFO' => $this->config->cacertPath,
])->get($url);
if ($responseTxt['error']) {
throw new PayException('网络发生错误,请稍后再试curl返回码:' . $responseTxt['message']);
}
$body = $responseTxt['body'];
$responseKey = str_ireplace('.', '_', $this->config->method . '.response');
$body = json_decode($body, true);
if ($body[$responseKey]['code'] != 10000) {
throw new PayException($body[$responseKey]['sub_msg']);
}
// 验证签名,检查支付宝返回的数据
$flag = $this->verifySign($body[$responseKey], $body['sign']);
if (! $flag) {
throw new PayException('支付宝返回数据被篡改。请检查网络是否安全!');
}
return $body[$responseKey];
}
/**
* 处理支付宝的返回值并返回给客户端
* @param array $data
* @return string|array
* @author helei
*/
protected function retData(array $data)
{
$sign = $data['sign'];
$data = ArrayUtil::removeKeys($data, ['sign']);
$data = ArrayUtil::arraySort($data);
// 支付宝新版本 需要转码
foreach ($data as &$value) {
$value = StrUtil::characet($value, $this->config->charset);
}
$data['sign'] = $sign;// sign 需要放在末尾
return $this->config->getewayUrl . http_build_query($data);
}
/**
* 返回统一的交易状态 做一些转化,方便处理
* @param $status
* @return string
* @author helei
*/
protected function getTradeStatus($status)
{
switch ($status) {
case 'TRADE_SUCCESS':
//no break
case 'TRADE_FINISHED':
return Config::TRADE_STATUS_SUCC;
case 'WAIT_BUYER_PAY':
case 'TRADE_CLOSED':
default:
return Config::TRADE_STATUS_FAILD;
}
}
/**
* 检查支付宝数据 签名是否被篡改
* @param array $data
* @param string $sign 支付宝返回的签名结果
* @return bool
* @author helei
*/
protected function verifySign(array $data, $sign)
{
$preStr = json_encode($data);
if ($this->config->signType === 'RSA') {// 使用RSA
$rsa = new RsaEncrypt($this->config->rsaAliPubKey);
return $rsa->rsaVerify($preStr, $sign);
} elseif ($this->config->signType === 'RSA2') {// 使用rsa2方式
$rsa = new Rsa2Encrypt($this->config->rsaAliPubKey);
return $rsa->rsaVerify($preStr, $sign);
} else {
return false;
}
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-15 17:28
* @description: 支付宝相关数据的基类
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment\Common\Ali\Data;
use Payment\Common\BaseData;
use Payment\Utils\ArrayUtil;
use Payment\Utils\Rsa2Encrypt;
use Payment\Utils\RsaEncrypt;
/**
* Class BaseData
*
* @property string $getewayUrl 支付宝网关
* @property string $appId 支付宝分配给开发者的应用ID
* @property string $method 接口名称
* @property string $format 仅支持JSON
* @property string $returnUrl HTTP/HTTPS开头字符串
* @property string $charset 请求使用的编码格式,如utf-8,gbk,gb2312等 当前仅支持 utf-8
* @property string $timestamp 发送请求的时间,格式"yyyy-MM-dd HH:mm:ss"
* @property string $version 调用的接口版本,固定为:1.0
* @property string $notifyUrl 支付宝服务器主动通知商户服务器里指定的页面http/https路径
*
* @property string $rsaPrivateKey rsa私钥路径
* @property string $rsaAliPubKey rsa支付宝公钥路径
*
* @property string $partner 合作id
*
* @package Payment\Charge\Ali\Data
* anthor helei
*/
abstract class AliBaseData extends BaseData
{
public function getData()
{
$data = parent::getData();
// 新版需要对数据进行排序
$data = ArrayUtil::arraySort($data);
return $data;
}
/**
* 签名算法实现
* @param string $signStr
* @return string
* @author helei
*/
protected function makeSign($signStr)
{
switch ($this->signType) {
case 'RSA':
$rsa = new RsaEncrypt($this->rsaPrivateKey);
$sign = $rsa->encrypt($signStr);
break;
case 'RSA2':
$rsa = new Rsa2Encrypt($this->rsaPrivateKey);
$sign = $rsa->encrypt($signStr);
break;
default:
$sign = '';
}
return $sign;
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-25 14:49
* @description:
*/
namespace Payment\Common\Ali\Data\Charge;
use Payment\Utils\ArrayUtil;
class AppChargeData extends ChargeBaseData
{
/**
* 业务请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递
*
* @return string
*/
protected function getBizContent()
{
$content = [
'body' => strval($this->body),
'subject' => strval($this->subject),
'out_trade_no' => strval($this->order_no),
'total_amount' => strval($this->amount),
'seller_id' => $this->partner,
// 销售产品码,商家和支付宝签约的产品码,为固定值QUICK_MSECURITY_PAY
'product_code' => 'QUICK_MSECURITY_PAY',
'goods_type' => $this->goods_type,
'passback_params' => $this->return_param,
'disable_pay_channels' => $this->limitPay,
'store_id' => $this->store_id,
];
$timeExpire = $this->timeout_express;
if (! empty($timeExpire)) {
$express = floor(($timeExpire - strtotime($this->timestamp)) / 60);
$express && $content['timeout_express'] = $express . 'm';// 超时时间 统一使用分钟计算
}
$content = ArrayUtil::paraFilter($content);// 过滤掉空值,下面不用在检查是否为空
return json_encode($content, JSON_UNESCAPED_UNICODE);
}
}
<?php
/**
* Created by PhpStorm.
* User: helei <dayugog@gmail.com>
* Date: 2016/12/28
* Time: 20:24
*/
namespace Payment\Common\Ali\Data\Charge;
use Payment\Common\PayException;
/**
* 支付宝 条码支付
* - 扫户扫用户的二维码,完成支付
* Class BarChargeData
*
* @property string $operator_id 商户操作员编号
* @property string $terminal_id 商户机具终端编号
* @property string $alipay_store_id 支付宝店铺的门店ID
* @property string $scene 条码支付,取值:bar_code 声波支付,取值:wave_code
* @property string $auth_code 支付授权码 二维码的数值
*
* @package Payment\Common\Ali\Data\Charge
*/
class BarChargeData extends ChargeBaseData
{
/**
* 业务请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递
*
* @return string
*/
protected function getBizContent()
{
$content = [
'body' => strval($this->body),
'subject' => strval($this->subject),
'out_trade_no' => strval($this->order_no),
'total_amount' => strval($this->amount),
'seller_id' => $this->partner,
'store_id' => $this->store_id,
'operator_id' => $this->operator_id,
'terminal_id' => $this->terminal_id,
'alipay_store_id' => $this->alipay_store_id,
'scene' => $this->scene,
'auth_code' => $this->auth_code,
];
$timeExpire = $this->timeout_express;
if (! empty($timeExpire)) {
$express = floor(($timeExpire - strtotime($this->timestamp)) / 60);
$express && $content['timeout_express'] = $express . 'm';// 超时时间 统一使用分钟计算
}
return json_encode($content, JSON_UNESCAPED_UNICODE);
}
protected function checkDataParam()
{
parent::checkDataParam(); // TODO: Change the autogenerated stub
$scene = $this->scene;
$authCode = $this->auth_code;
if (empty($scene) || ! in_array($scene, ['bar_code', 'wave_code'])) {
throw new PayException('支付场景 scene 必须设置 条码支付:bar_code 声波支付:wave_code');
}
if (empty($authCode)) {
throw new PayException('请提供支付授权码');
}
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-20 14:33
* @description:
*/
namespace Payment\Common\Ali\Data\Charge;
use Payment\Common\Ali\Data\AliBaseData;
use Payment\Common\PayException;
use Payment\Config;
use Payment\Utils\ArrayUtil;
/**
* Class ChargeBaseData
*
* @inheritdoc
*
* @property string $body
* @property string $subject
* @property string $order_no
* @property integer $timeout_express
* @property string $amount
* @property string $goods_type
* @property string $return_param
* @property string $store_id 商户门店编号
*
* @package Payment\Common\Ali\Data\Charge
* anthor helei
*/
abstract class ChargeBaseData extends AliBaseData
{
/**
* 构建 APP支付 加密数据
* @author helei
*/
protected function buildData()
{
$signData = [
// 公共参数
'app_id' => $this->appId,
'method' => $this->method,
'format' => $this->format,
'return_url' => $this->returnUrl,
'charset' => $this->charset,
'sign_type' => $this->signType,
'timestamp' => $this->timestamp,
'version' => $this->version,
'notify_url' => $this->notifyUrl,
// 业务参数
'biz_content' => $this->getBizContent(),
];
// 移除数组中的空值
$this->retData = ArrayUtil::paraFilter($signData);
}
/**
* 支付宝构建请求支付的数据
* @return mixed
*/
abstract protected function getBizContent();
/**
* 检查传入的支付业务参数是否正确
*
* 如果输入参数不符合规范,直接抛出异常
*
* @author helei
*/
protected function checkDataParam()
{
$subject = $this->subject;
$orderNo = $this->order_no;
$amount = $this->amount;
$goodsType = $this->goods_type;
$passBack = $this->return_param;
// 检查订单号是否合法
if (empty($orderNo) || mb_strlen($orderNo) > 64) {
throw new PayException('订单号不能为空,并且长度不能超过64位');
}
// 检查金额不能低于0.01
if (bccomp($amount, Config::PAY_MIN_FEE, 2) === -1) {
throw new PayException('支付金额不能低于 ' . Config::PAY_MIN_FEE . ' 元');
}
// 检查 商品名称 与 商品描述
if (empty($subject)) {
throw new PayException('必须提供 商品的标题/交易标题/订单标题/订单关键字 等');
}
// 检查商品类型
if (empty($goodsType)) {// 默认为实物类商品
$this->goods_type = 1;
} elseif (! in_array($goodsType, [0 ,1])) {
throw new PayException('商品类型可取值为:0-虚拟类商品 1-实物类商品');
}
// 返回参数进行urlencode编码
if (! empty($passBack) && ! is_string($passBack)) {
throw new PayException('回传参数必须是字符串');
}
$this->return_param = urlencode($passBack);
}
}
<?php
/**
* Created by PhpStorm.
* User: helei <dayugog@gmail.com>
* Date: 2016/12/28
* Time: 20:24
*/
namespace Payment\Common\Ali\Data\Charge;
/**
* 支付宝 扫码支付
* Class QrChargeData
*
* @property string $operator_id 商户操作员编号
* @property string $terminal_id 商户机具终端编号
* @property string $alipay_store_id 支付宝店铺的门店ID
*
* @package Payment\Common\Ali\Data\Charge
*/
class QrChargeData extends ChargeBaseData
{
/**
* 业务请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递
*
* @return string
*/
protected function getBizContent()
{
$content = [
'body' => strval($this->body),
'subject' => strval($this->subject),
'out_trade_no' => strval($this->order_no),
'total_amount' => strval($this->amount),
'seller_id' => $this->partner,
'store_id' => $this->store_id,
'operator_id' => $this->operator_id,
'terminal_id' => $this->terminal_id,
'alipay_store_id' => $this->alipay_store_id,
];
$timeExpire = $this->timeout_express;
if (! empty($timeExpire)) {
$express = floor(($timeExpire - strtotime($this->timestamp)) / 60);
$express && $content['timeout_express'] = $express . 'm';// 超时时间 统一使用分钟计算
}
return json_encode($content, JSON_UNESCAPED_UNICODE);
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-22 17:02
* @description:
*/
namespace Payment\Common\Ali\Data\Charge;
use Payment\Utils\ArrayUtil;
class WapChargeData extends ChargeBaseData
{
/**
* 业务请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递
*
* @return string
*/
protected function getBizContent()
{
$content = [
'body' => strval($this->body),
'subject' => strval($this->subject),
'out_trade_no' => strval($this->order_no),
'total_amount' => strval($this->amount),
'seller_id' => $this->partner,
// 销售产品码,商家和支付宝签约的产品码,为固定值QUICK_WAP_PAY
'product_code' => 'QUICK_WAP_PAY',
'goods_type' => $this->goods_type,
'passback_params' => $this->return_param,
'disable_pay_channels' => $this->limitPay,
'store_id' => $this->store_id,
];
$timeExpire = $this->timeout_express;
if (! empty($timeExpire)) {
$express = floor(($timeExpire - strtotime($this->timestamp)) / 60);
$express && $content['timeout_express'] = $express . 'm';// 超时时间 统一使用分钟计算
}
$content = ArrayUtil::paraFilter($content);// 过滤掉空值,下面不用在检查是否为空
return json_encode($content, JSON_UNESCAPED_UNICODE);
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-15 17:28
* @description: 即时到帐 接口的数据处理类
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment\Common\Ali\Data\Charge;
use Payment\Common\PayException;
use Payment\Utils\ArrayUtil;
/**
* Class WebChargeData
*
* @inheritdoc
* @property integer $qr_mod
*
* @package Payment\Charge\Ali\Data
* anthor helei
*/
class WebChargeData extends ChargeBaseData
{
/**
* 业务请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递
*
* @return string
*/
protected function getBizContent()
{
$content = [
'body' => strval($this->body),
'subject' => strval($this->subject),
'out_trade_no' => strval($this->order_no),
'total_amount' => strval($this->amount),
// 销售产品码,商家和支付宝签约的产品码,为固定值QUICK_WAP_PAY
'product_code' => 'FAST_INSTANT_TRADE_PAY',
'goods_type' => $this->goods_type,
'passback_params' => $this->return_param,
'disable_pay_channels' => $this->limitPay,
'store_id' => $this->store_id,
'qr_pay_mode' => $this->qr_mod,
];
$timeExpire = $this->timeout_express;
if (! empty($timeExpire)) {
$express = floor(($timeExpire - strtotime($this->timestamp)) / 60);
$express && $content['timeout_express'] = $express . 'm';// 超时时间 统一使用分钟计算
}
$content = ArrayUtil::paraFilter($content);// 过滤掉空值,下面不用在检查是否为空
return json_encode($content, JSON_UNESCAPED_UNICODE);
}
}
<?php
/**
* @author: helei
* @createTime: 2017-03-06 22:32
* @description:
*/
namespace Payment\Common\Ali\Data\Query;
use Payment\Common\PayException;
use Payment\Utils\ArrayUtil;
/**
* Class ChargeQueryData
*
* @property string $trade_no 支付宝的订单号,优先使用
* @property string $out_trade_no 商户系统内部的订单号
*
* @package Payment\Common\Ali\Data\Query
* anthor helei
*/
class ChargeQueryData extends QueryBaseData
{
/**
* 检查参数
* @author helei
*/
protected function checkDataParam()
{
$tradeNo = $this->trade_no;// 支付宝交易号,查询效率高
$outTradeNo = $this->out_trade_no;// 商户订单号,查询效率低,不建议使用
// 二者不能同时为空
if (empty($outTradeNo) && empty($tradeNo)) {
throw new PayException('必须提供支付宝交易号或者商户网站唯一订单号。建议使用支付宝交易号');
}
}
protected function getBizContent()
{
$content = [
'out_trade_no' => $this->out_trade_no,
'trade_no' => $this->trade_no,
];
$content = ArrayUtil::paraFilter($content);// 过滤掉空值,下面不用在检查是否为空
return json_encode($content, JSON_UNESCAPED_UNICODE);
}
}
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/3/6
* Time: 下午10:37
*/
namespace Payment\Common\Ali\Data\Query;
use Payment\Common\Ali\Data\AliBaseData;
use Payment\Utils\ArrayUtil;
abstract class QueryBaseData extends AliBaseData
{
/**
* 构建 APP支付 加密数据
* @author helei
*/
protected function buildData()
{
$signData = [
// 公共参数
'app_id' => $this->appId,
'method' => $this->method,
'format' => $this->format,
'charset' => $this->charset,
'sign_type' => $this->signType,
'timestamp' => $this->timestamp,
'version' => $this->version,
// 业务参数
'biz_content' => $this->getBizContent(),
];
// 移除数组中的空值
$this->retData = ArrayUtil::paraFilter($signData);
}
/**
* 支付宝构建请求查询的数据
* @return mixed
*/
abstract protected function getBizContent();
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/3/7
* Time: 下午1:45
*/
namespace Payment\Common\Ali\Data\Query;
use Payment\Common\PayException;
use Payment\Utils\ArrayUtil;
/**
* 支付宝退款查询
* Class RefundQueryData
*
* @property string $trade_no 支付宝的订单号,优先使用
* @property string $out_trade_no 商户系统内部的订单号
* @property string $refund_no 请求退款接口时,传入的退款请求号,如果在退款请求时未传入,则该值为创建交易时的外部交易号
*
* @package Payment\Common\Ali\Data\Query
*/
class RefundQueryData extends QueryBaseData
{
protected function getBizContent()
{
$content = [
'out_trade_no' => $this->out_trade_no,
'trade_no' => $this->trade_no,
'out_request_no' => $this->refund_no,
];
$content = ArrayUtil::paraFilter($content);// 过滤掉空值,下面不用在检查是否为空
return json_encode($content, JSON_UNESCAPED_UNICODE);
}
protected function checkDataParam()
{
$tradeNo = $this->trade_no;// 支付宝交易号,查询效率高
$outTradeNo = $this->out_trade_no;// 商户订单号,查询效率低,不建议使用
// 二者不能同时为空
if (empty($outTradeNo) && empty($tradeNo)) {
throw new PayException('必须提供支付宝交易号或者商户网站唯一订单号。建议使用支付宝交易号');
}
$refundNo = $this->refund_no;
if (empty($refundNo)) {
throw new PayException('支付宝查询退款,必须传入提款的请求号。如果在退款请求时未传入,则该值为创建交易时的外部交易号');
}
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/3/7
* Time: 下午3:59
*/
namespace Payment\Common\Ali\Data\Query;
use Payment\Common\PayException;
use Payment\Utils\ArrayUtil;
/**
* 构建查询转账情况的数据
*
* @property string $trans_no 商户转账唯一订单号
* @property string $transaction_id 支付宝转账单据号:和商户转账唯一订单号不能同时为空
*
* Class TransferQueryData
* @package Payment\Common\Ali\Data\Query
*/
class TransferQueryData extends QueryBaseData
{
protected function getBizContent()
{
$content = [
'out_biz_no' => $this->trans_no,
'order_id' => $this->transaction_id,
];
$content = ArrayUtil::paraFilter($content);// 过滤掉空值,下面不用在检查是否为空
return json_encode($content, JSON_UNESCAPED_UNICODE);
}
protected function checkDataParam()
{
$transNo = $this->trans_no;
$transactionId = $this->transaction_id;
// 二者不能同时为空
if (empty($transactionId) && empty($transNo)) {
throw new PayException('必须提供支付宝转账单据号或者商户转账单号');
}
}
}
\ No newline at end of file
<?php
/**
* @author: helei
* @createTime: 2016-07-26 18:18
* @description:
*/
namespace Payment\Common\Ali\Data;
use Payment\Common\PayException;
use Payment\Utils\ArrayUtil;
/**
* Class RefundData
*
* @property string $trade_no 支付宝的订单号,优先使用
* @property string $out_trade_no 商户系统内部的订单号
* @property float $refund_fee 退款总金额,订单总金额,只能为整数
* @property string $reason 退款的原因说明
* @property string $refund_no 商户系统内部的退款单号,商户系统内部唯一,同一退款单号多次请求只退一笔(3~24位)
* @property string $operator_id 商户的操作员编号
* @property string $terminal_id 商户机具终端编号
* @property string $store_id 商户门店编号
*
* @package Payment\Common\Ali\Data
* anthor helei
*/
class RefundData extends AliBaseData
{
/**
* 检查退款数据是否正常
* @author helei
*/
protected function checkDataParam()
{
$tradeNo = $this->trade_no;// 支付宝交易号,查询效率高
$outTradeNo = $this->out_trade_no;// 商户订单号,查询效率低,不建议使用
$refundAmount = $this->refund_fee;
// 二者不能同时为空
if (empty($outTradeNo) && empty($tradeNo)) {
throw new PayException('必须提供支付宝交易号或者商户网站唯一订单号。建议使用支付宝交易号');
}
if (empty($refundAmount) || ! is_numeric($refundAmount)) {
throw new PayException('refund_fee 退款的金额,该金额不能大于订单金额,单位为元,支持两位小数');
}
}
protected function buildData()
{
$signData = [
// 公共参数
'app_id' => $this->appId,
'method' => $this->method,
'format' => $this->format,
'charset' => $this->charset,
'sign_type' => $this->signType,
'timestamp' => $this->timestamp,
'version' => $this->version,
// 业务参数
'biz_content' => $this->getBizContent(),
];
// 移除数组中的空值
$this->retData = ArrayUtil::paraFilter($signData);
}
/**
* 业务请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递
*
* @return string
*/
private function getBizContent()
{
$content = [
'out_trade_no' => $this->out_trade_no,
'trade_no' => $this->trade_no,
'refund_amount' => $this->refund_fee,
'refund_reason' => $this->reason,
'out_request_no' => $this->refund_no,
'operator_id' => $this->operator_id,
'store_id' => $this->store_id,
'terminal_id' => $this->terminal_id,
];
$content = ArrayUtil::paraFilter($content);// 过滤掉空值,下面不用在检查是否为空
return json_encode($content, JSON_UNESCAPED_UNICODE);
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-27 13:05
* @description:
*/
namespace Payment\Common\Ali\Data;
use Payment\Common\PayException;
use Payment\Config;
use Payment\Utils\ArrayUtil;
/**
* 转账到支付宝帐号
* Class TransData
*
* @property string $trans_no 商户转账唯一订单号
* @property string $payee_type 收款方账户类型。可取值: 1、ALIPAY_USERID:支付宝账号对应的支付宝唯一用户号。以2088开头的16位纯数字组成。 2、ALIPAY_LOGONID:支付宝登录号,支持邮箱和手机号格式。
* @property string $payee_account 收款方账户。与payee_type配合使用。付款方和收款方不能是同一个账户。
* @property string $amount 转账金额,单位:元。 只支持2位小数,小数点前最大支持13位,金额必须大于0。
*
* // 可选参数
* @property string $payer_real_name 付款方真实姓名
* @property string $payee_real_name 收款方真实姓名
* @property string $payer_show_name 默认显示该账户在支付宝登记的实名。收款方可见。
* @property string $remark 转账备注 当付款方为企业账户,且转账金额达到(大于等于)50000元,remark不能为空
*
* @package Payment\Common\Ali\Data
* anthor helei
*/
class TransData extends AliBaseData
{
/**
* 检查参数是否合法
* @author helei
*/
protected function checkDataParam()
{
$transNo = $this->trans_no;
$payeeType = $this->payee_type;
$payeeAccount = $this->payee_account;
$amount = $this->amount;
$remark = $this->remark;
if (empty($transNo)) {
throw new PayException('请传入 商户转账唯一订单号');
}
if (empty($payeeType) || ! in_array($payeeType, ['ALIPAY_USERID', 'ALIPAY_LOGONID'])) {
throw new PayException('请传入收款账户类型');
}
if (empty($payeeAccount)) {
throw new PayException('请传入转账帐号');
}
if (empty($amount) || bccomp($amount, 0, 2) !== 1) {
throw new PayException('请输入转账金额,且大于0');
}
if (bccomp($amount, Config::TRANS_FEE, 2) !== -1 && empty($remark)) {
throw new PayException('转账金额大于等于' . Config::TRANS_FEE , '必须设置 remark');
}
}
protected function buildData()
{
$signData = [
// 公共参数
'app_id' => $this->appId,
'method' => $this->method,
'format' => $this->format,
'charset' => $this->charset,
'sign_type' => $this->signType,
'timestamp' => $this->timestamp,
'version' => $this->version,
// 业务参数
'biz_content' => $this->getBizContent(),
];
// 移除数组中的空值
$this->retData = ArrayUtil::paraFilter($signData);
}
/**
* 业务请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递
*
* @return string
*/
private function getBizContent()
{
$content = [
'out_biz_no' => $this->trans_no,
'payee_type' => strtoupper($this->payee_type),
'payee_account' => $this->payee_account,
'amount' => $this->amount,
'payer_real_name' => $this->payer_real_name,
'payer_show_name' => $this->payer_show_name,
'payee_real_name' => $this->payee_real_name,
'remark' => $this->remark,
];
$content = ArrayUtil::paraFilter($content);// 过滤掉空值,下面不用在检查是否为空
return json_encode($content, JSON_UNESCAPED_UNICODE);
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-15 14:56
* @description: 支付宝配置文件 所有支付的配置文件,均需要继承 ConfigInterface 这个接口
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment\Common;
use Payment\Utils\ArrayUtil;
use Payment\Utils\StrUtil;
final class AliConfig extends ConfigInterface
{
// 支付宝的网关
public $getewayUrl;
// 支付宝分配给开发者的应用ID
public $appId;
// 接口名称
public $method;// 参考 定义的常量
// 仅支持JSON
public $format = 'JSON';
// 用于同步通知的地址
public $returnUrl;
// 采用的编码
public $charset = 'UTF-8';
// 发送请求的时间,格式"yyyy-MM-dd HH:mm:ss"
public $timestamp;
// 调用的接口版本,固定为:1.0
public $version = '1.0';
// 合作者身份ID
public $partner;
// 用于rsa加密的私钥文件路径
public $rsaPrivateKey;
// 用于rsa解密的支付宝公钥文件路径
public $rsaAliPubKey;
// 支付宝各类method名称
// wap 支付
const WAP_PAY_METHOD = 'alipay.trade.wap.pay';
// app 支付
const APP_PAY_METHOD = 'alipay.trade.app.pay';
// 即时到账 web支付 新版接口
const PC_PAY_METHOD = 'alipay.trade.page.pay';
// 扫码支付 用户扫商户的二维码
const QR_PAY_METHOD = 'alipay.trade.precreate';
// 条码支付 商户扫用户的二维码
const BAR_PAY_METHOD = 'alipay.trade.pay';
// 统一收单线下交易查询
const TRADE_QUERY_METHOD = 'alipay.trade.query';
// 统一收单交易退款查询 未完成
const REFUND_QUERY_METHOD = 'alipay.trade.fastpay.refund.query';
// 转账情况查询
const TRANS_QUERY_METHOD = 'alipay.fund.trans.order.query';
// 统一收单交易退款接口
const TRADE_REFUND_METHOD = 'alipay.trade.refund';
// 单笔转账到支付宝账户接口
const TRANS_TOACCOUNT_METHOD = 'alipay.fund.trans.toaccount.transfer';
public function __construct(array $config)
{
// 初始化配置信息
try {
$this->initConfig($config);
} catch (PayException $e) {
throw $e;
}
}
/**
* 检查传入的配置文件信息是否正确
* @param array $config
* @throws PayException
* @author helei
*/
private function initConfig(array $config)
{
$config = ArrayUtil::paraFilter($config);// 过滤掉空值,下面不用在检查是否为空
// 初始 支付宝网关地址
$this->getewayUrl = 'https://openapi.alipay.com/gateway.do?';
if (isset($config['use_sandbox']) && $config['use_sandbox'] === true) {
$this->getewayUrl = 'https://openapi.alipaydev.com/gateway.do?';
} else {
$this->useSandbox = false;// 不是沙箱模式
}
// 支付宝分配给开发者的应用ID
if (key_exists('app_id', $config) && is_numeric($config['app_id'])) {
$this->appId = $config['app_id'];
} else {
throw new PayException('缺少支付宝分配给开发者的应用ID,请在开发者中心查看');
}
// 初始 支付宝异步通知地址,可为空
if (key_exists('notify_url', $config)) {
$this->notifyUrl = $config['notify_url'];
}
// 初始 支付宝 同步通知地址,可为空
if (key_exists('return_url', $config)) {
$this->returnUrl = $config['return_url'];
}
// 初始 支付宝 签名方式,可为空
if (key_exists('sign_type', $config) && in_array($config['sign_type'], ['RSA', 'RSA2'])) {
$this->signType = $config['sign_type'];
} else {
throw new PayException('目前支付宝仅支持RSA2和RSA,推荐使用RSA2');
}
// 新版本,需要提供独立的公钥信息。每一个应用,公钥都不相同
if (key_exists('ali_public_key', $config) && (file_exists($config['ali_public_key']) || ! empty($config['ali_public_key']))) {
$this->rsaAliPubKey = StrUtil::getRsaKeyValue($config['ali_public_key'], 'public');
} else {
throw new PayException('请提供支付宝对应的rsa公钥');
}
// 初始 RSA私钥文件 需要检查该文件是否存在
if (key_exists('rsa_private_key', $config) && (file_exists($config['rsa_private_key']) || ! empty($config['ali_public_key']))) {
$this->rsaPrivateKey = StrUtil::getRsaKeyValue($config['rsa_private_key'], 'private');
} else {
throw new PayException('请提供商户的rsa私钥文件');
}
// 发送请求的时间,格式"yyyy-MM-dd HH:mm:ss" 需要正确设置时区
$this->timestamp = date('Y-m-d H:i:s', time());
// 初始 合作者身份ID 如果该值为空,则默认为商户签约账号对应的支付宝用户ID
if (key_exists('partner', $config)) {
$this->partner = $config['partner'];
}
// 设置禁止使用的支付方式
if (key_exists('limit_pay', $config) && is_array($config['limit_pay'])) {
$this->limitPay = implode(',', $config['limit_pay']);
}
if (key_exists('return_raw', $config)) {
$this->returnRaw = filter_var($config['return_raw'], FILTER_VALIDATE_BOOLEAN);
}
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-28 18:05
* @description: 支付相关接口的数据基类
*/
namespace Payment\Common;
use Payment\Config;
use Payment\Utils\ArrayUtil;
/**
* Class BaseData
* 支付相关接口的数据基类
* @package Payment\Common\Weixin\Dataa
*
* @property string $limitPay 用户不可用指定渠道支付
* @property boolean $returnRaw 是否返回原始数据,只进行签名检查
* @property string $useSandbox 是否使用的测试模式
* @property string $signType 签名算法
*
*/
abstract class BaseData
{
/**
* 支付的请求数据
* @var array $data
*/
protected $data;
/**
* 支付返回的数据
* @var array $retData
*/
protected $retData;
/**
* 配置类型
* @var string $configType
*/
protected $channel;
/**
* BaseData constructor.
* @param ConfigInterface $config
* @param array $reqData
* @throws PayException
*/
public function __construct(ConfigInterface $config, array $reqData)
{
if ($config instanceof WxConfig) {
$this->channel = Config::WECHAT_PAY;
} elseif ($config instanceof AliConfig) {
$this->channel = Config::ALI_PAY;
} elseif ($config instanceof CmbConfig) {
$this->channel = Config::CMB_PAY;
}elseif ($config instanceof TLConfig) {
$this->channel = Config::TL_PAY;
}
$this->data = array_merge($config->toArray(), $reqData);//配置信息合并
try {
$this->checkDataParam();
} catch (PayException $e) {
throw $e;
}
}
/**
* 获取变量,通过魔术方法
* @param string $name
* @return null|string
* @author helei
*/
public function __get($name)
{
if (isset($this->data[$name])) {
return $this->data[$name];
}
return null;
}
/**
* 设置变量
* @param $name
* @param $value
* @author helei
*/
public function __set($name, $value)
{
$this->data[$name] = $value;
}
/**
* 设置签名
* @author helei
*/
public function setSign()
{
$this->buildData();
if ($this->channel === Config::CMB_PAY) {
$data = $this->retData['reqData'];
} else {
$data = $this->retData;
}
if($this->channel === Config::TL_PAY){
$this->retData['sign'] = ArrayUtil::SignArray($data,$this->md5Key);//签名
}else {
$values = ArrayUtil::removeKeys($data, ['sign']);
$values = ArrayUtil::arraySort($values);
$signStr = ArrayUtil::createLinkstring($values);
$this->retData['sign'] = $this->makeSign($signStr);
}
}
/**
* 返回处理之后的数据
* @return array
* @author helei
*/
public function getData()
{
return $this->retData;
}
/**
* 签名算法实现 便于后期扩展微信不同的加密方式
* @param string $signStr
* @return string
*/
abstract protected function makeSign($signStr);
/**
* 构建用于支付的签名相关数据
* @return array
*/
abstract protected function buildData();
/**
* 检查传入的参数. $reqData是否正确.
* @return mixed
* @throws PayException
*/
abstract protected function checkDataParam();
}
<?php
/**
* @author: helei
* @createTime: 2016-07-28 16:45
* @description: 所有的策略类接口
*/
namespace Payment\Common;
interface BaseStrategy
{
/**
* 处理具体的业务
* @param array $data
* @return mixed
* @author helei
*/
public function handle(array $data);
/**
* 获取支付对应的数据完成类
* @return BaseData
* @author helei
*/
public function getBuildDataClass();
}
<?php
namespace Payment\Common\Cmb;
use Payment\Common\BaseData;
use Payment\Common\BaseStrategy;
use Payment\Common\CmbConfig;
use Payment\Common\PayException;
use Payment\Config;
use Payment\Utils\Curl;
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/4/27
* Time: 下午12:36
*/
abstract class CmbBaseStrategy implements BaseStrategy
{
/**
* 招商的配置文件
* @var CmbConfig $config
*/
protected $config;
/**
* 请求数据
* @var BaseData $reqData
*/
protected $reqData;
/**
* CmbBaseStrategy constructor.
* @param array $config
* @throws PayException
*/
public function __construct(array $config)
{
/* 设置内部字符编码为 UTF-8 */
mb_internal_encoding("UTF-8");
try {
$this->config = new CmbConfig($config);
} catch (PayException $e) {
throw $e;
}
}
/**
* 所有支付能力的入口
* @param array $data
* @return mixed
* @throws PayException
*/
public function handle(array $data)
{
$buildClass = $this->getBuildDataClass();
try {
$this->reqData = new $buildClass($this->config, $data);
} catch (PayException $e) {
throw $e;
}
$this->reqData->setSign();
$data = $this->reqData->getData();
return $this->retData($data);
}
/**
* 处理微信的返回值并返回给客户端
* @param array $ret
* @return mixed
* @author helei
*/
protected function retData(array $ret)
{
$json = json_encode($ret, JSON_UNESCAPED_UNICODE);
$reqData = [
'url' => $this->config->getewayUrl,
'name' => CmbConfig::REQ_FILED_NAME,
'value' => $json,
];
return $reqData;
}
/**
* 发送完了请求
* @param string $json
* @return mixed
* @throws PayException
* @author helei
*/
protected function sendReq($json)
{
$responseTxt = $this->curlPost($json, $this->config->getewayUrl);
if ($responseTxt['error']) {
throw new PayException('网络发生错误,请稍后再试curl返回码:' . $responseTxt['message']);
}
$body = json_decode($responseTxt['body'], true);
$rspData = $body['rspData'];
if ($rspData['rspCode'] !== CmbConfig::SUCC_TAG) {
throw new PayException('招商返回错误提示:' . $rspData['rspMsg']);
}
return $rspData;
}
/**
* 父类仅提供基础的post请求,子类可根据需要进行重写
* @param string $json
* @param string $url
* @return array
* @author helei
*/
protected function curlPost($json, $url)
{
$curl = new Curl();
return $curl->set([
'CURLOPT_HEADER' => 0,
])->post($json)->submit($url);
}
/**
* 返回统一的交易状态 做一些转化,方便处理
* @param $status
* @return string
* @author helei
*/
protected function getTradeStatus($status)
{
switch ($status) {
case '0':// 0:已结帐
return Config::TRADE_STATUS_SUCC;
case '1':// 1:已撤销
case '2':// 2:部分结帐
case '4':// 4:未结帐
case '7':// 7:冻结交易-冻结金额已经全部结账
case '8':// 8:冻结交易,冻结金额只结帐了一部分
default:
return Config::TRADE_STATUS_FAILD;// 以上状态全部设置为失败
}
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/4/28
* Time: 上午10:48
*/
namespace Payment\Common\Cmb\Data;
use Payment\Common\PayException;
/**
* 签约绑卡操作
* Class BindCardData
* @package Payment\Common\Cmb\Data
*
* @property string $lon 经度,商户app获取的手机定位数据,如30.949505
* @property string $lat 纬度,商户app获取的手机定位数据,如50.949506
*
*/
class BindCardData extends CmbBaseData
{
protected function checkDataParam()
{
parent::checkDataParam();
$agrNo = $this->agr_no;
if (empty($agrNo) || mb_strlen($agrNo) > 30 || ! is_numeric($agrNo)) {
throw new PayException('客户协议号。必须为纯数字串,不超过30位');
}
}
protected function getReqData()
{
$reqData = [
'dateTime' => $this->dateTime,
'merchantSerialNo' => $this->serial_no ? $this->serial_no : '',
'agrNo' => $this->agr_no,
'branchNo' => $this->branchNo,
'merchantNo' => $this->merchantNo,
'userID' => $this->user_id ? $this->user_id : '',
'mobile' => $this->mobile ? $this->mobile : '',
'lon' => $this->lon ? $this->lon : '',
'lat' => $this->lat ? $this->lat : '',
'riskLevel' => $this->risk_level ? $this->risk_level : '',
'noticeUrl' => $this->signNoticeUrl ? $this->signNoticeUrl : '',
'noticePara' => $this->return_param ? $this->return_param : '',
'returnUrl' => $this->returnUrl ? $this->returnUrl : '',
];
// 这里不能进行过滤空值,招商的空值也要加入签名中
return $reqData;
}
}
\ No newline at end of file
<?php
namespace Payment\Common\Cmb\Data\Charge;
use Payment\Common\Cmb\Data\CmbBaseData;
use Payment\Common\CmbConfig;
use Payment\Common\PayException;
use Payment\Config;
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/4/27
* Time: 下午1:01
*
* @property string $date 订单日期,格式:yyyyMMdd
* @property string $order_no 订单号, 10位数字,由商户生成,一天内不能重复。订单日期+订单号唯一定位一笔订单。
* @property string $amount 金额, 格式:xxxx.xx 固定两位小数,最大11位整数
* @property integer $timeout_express 过期时间
* @property string $lon 经度,商户app获取的手机定位数据,如30.949505
* @property string $lat 纬度,商户app获取的手机定位数据,如50.949506
*
*/
class ChargeData extends CmbBaseData
{
/**
* 发送请求
*/
protected function checkDataParam()
{
parent::checkDataParam();
$amount = $this->amount;
// 订单号交给支付系统自己检查
// 检查金额不能低于0.01
if (bccomp($amount, Config::PAY_MIN_FEE, 2) === -1) {
throw new PayException('支付金额不能低于 ' . Config::PAY_MIN_FEE . ' 元');
}
// 设置ip地址
$clientIp = $this->client_ip;
if (empty($clientIp)) {
$this->client_ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1';
}
$timeExpire = $this->timeout_express;
if (! empty($timeExpire)) {
$express = floor(($timeExpire - strtotime($this->dateTime)) / 60);
if ($express > CmbConfig::MAX_EXPIRE_TIME || $express < 0) {// 招商规定
$this->timeout_express = CmbConfig::MAX_EXPIRE_TIME;
} else {
$this->timeout_express = $express;
}
}
}
/**
* 请求数据
*/
protected function getReqData()
{
$reqData = [
'dateTime' => $this->dateTime,
'branchNo' => $this->branchNo,
'merchantNo' => $this->merchantNo,
'date' => $this->date ? $this->date : date('Ymd', time()),
'orderNo' => $this->order_no,
'amount' => $this->amount,
'expireTimeSpan' => $this->timeout_express ? $this->timeout_express : '',
'payNoticeUrl' => $this->notifyUrl,
'payNoticePara' => $this->return_param ? $this->return_param : '',
'returnUrl' => $this->returnUrl ? $this->returnUrl : '',
'clientIP' => $this->client_ip,
'cardType' => $this->limitPay ? $this->limitPay : '',
'agrNo' => $this->agr_no,
'merchantSerialNo' => $this->serial_no ? $this->serial_no : '',
'userID' => $this->user_id ? $this->user_id : '',
'mobile' => $this->mobile ? $this->mobile : '',
'lon' => $this->lon ? $this->lon : '',
'lat' => $this->lat ? $this->lat : '',
'riskLevel' => $this->risk_level ? $this->risk_level : '',
'signNoticeUrl' => $this->signNoticeUrl ? $this->signNoticeUrl : '',
'signNoticePara' => $this->return_param ? $this->return_param : '',
// 暂时先不支持下面方式
'extendInfo' => '',
'extendInfoEncrypType' => '',
];
// 这里不能进行过滤空值,招商的空值也要加入签名中
return $reqData;
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/4/27
* Time: 下午1:00
*/
namespace Payment\Common\Cmb\Data;
use Payment\Common\BaseData;
use Payment\Common\PayException;
use Payment\Utils\ArrayUtil;
/**
* Class BaseData
*
* @property string $version 调用的接口版本,固定为:1.0
* @property string $charset 参数编码,固定为“UTF-8”
* @property string $dateTime 求时间,格式为yyyyMMddHHmmss
* @property string $branchNo 商户分行号,4位数字
* @property string $merchantNo 商户号,6位数字
* @property string $notifyUrl 服务器主动通知商户服务器里指定的页面http/https路径
* @property string $signNoticeUrl 成功签约结果通知地址 商户接收成功签约结果通知的地址。
* @property string $returnUrl HTTP/HTTPS开头字符串
* @property string $merKey 用于加密的 key
* @property string $opPwd 用于加密的 key
* @property string $client_ip 用户端实际ip
* @property string $serial_no 协议开通请求流水号,开通协议时必填。
* @property string $agr_no 客户协议号。必须为纯数字串,不超过30位。
* @property string $user_id 用于标识商户用户的唯一ID。 商户系统内用户唯一标识,不超过20位,数字字母都可以,建议纯数字
* @property string $mobile 商户用户的手机号
* @property string $risk_level 风险等级:用户在商户系统内风险等级标识
* @property string $return_param 结果通知附加参数 该参数在发送成功签约结果通知时,将原样返回商户 注意:该参数可为空,商户如果需要不止一个参数,可以自行把参数组合、拼装,但组合后的结果不能带有’&’字符。
*
*
* @package Payment\Common\Weixin\Dataa
*/
abstract class CmbBaseData extends BaseData
{
/**
* 请求数据签名算法的实现
* @param string $signStr
* @return string
*/
protected function makeSign($signStr)
{
switch ($this->signType) {
case 'SHA-256':
$sign = hash('sha256', "$signStr&{$this->merKey}");
break;
default:
$sign = '';
}
return $sign;
}
/**
* 构建数据
*/
protected function buildData()
{
$signData = [
// 公共参数
'version' => $this->version,
'charset' => $this->charset,
'signType' => $this->signType,
'reqData' => $this->getReqData(),
];
// 移除数组中的空值
$this->retData = ArrayUtil::paraFilter($signData);
}
/**
* 检查基本数据
*/
protected function checkDataParam()
{
$branchNo = $this->branchNo;
$merchantNo = $this->merchantNo;
if (empty($branchNo) || mb_strlen($branchNo) !== 4) {
throw new PayException('商户分行号,4位数字');
}
if (empty($merchantNo) || mb_strlen($merchantNo) !== 6) {
throw new PayException('商户号,6位数字');
}
}
/**
* 请求数据
*
* @return array
*/
abstract protected function getReqData();
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/4/29
* Time: 上午9:52
*/
namespace Payment\Common\Cmb\Data;
use Payment\Common\CmbConfig;
/**
* 获取招商的公钥
* Class PubKeyData
* @package Payment\Common\Cmb\Data
*/
class PubKeyData extends CmbBaseData
{
protected function getReqData()
{
$reqData = [
'dateTime' => $this->dateTime,
'branchNo' => $this->branchNo,
'merchantNo' => $this->merchantNo,
'txCode' => CmbConfig::TRADE_CODE,
];
// 这里不能进行过滤空值,招商的空值也要加入签名中
return $reqData;
}
}
\ No newline at end of file
<?php
namespace Payment\Common\Cmb\Data\Query;
use Payment\Common\Cmb\Data\CmbBaseData;
use Payment\Common\PayException;
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/4/28
* Time: 下午12:52
*
* @property string $date 订单日期,格式:yyyyMMdd
* @property string $out_trade_no 商户系统内部的订单号
* @property string $operator_no 商户结账系统的操作员号
* @property string $type 查询类型,A:按银行订单流水号查询 B:按商户订单日期和订单号查询;
* @property string $transaction_id 招商的订单号,优先使用
*
*/
class ChargeQueryData extends CmbBaseData
{
protected function checkDataParam()
{
parent::checkDataParam();
$bankSerialNo = $this->transaction_id;
$date = $this->date;
$orderNo = $this->out_trade_no;
if (empty($date) || mb_strlen($date) !== 8) {
throw new PayException('商户订单日期必须提供,格式:yyyyMMdd');
}
if ($bankSerialNo && mb_strlen($bankSerialNo) === 20) {
$this->type = 'A';
} elseif ($orderNo && mb_strlen($bankSerialNo) <= 32) {
$this->type = 'B';
} else {
throw new PayException('必须设置商户订单信息或者招商流水号');
}
}
protected function getReqData()
{
$reqData = [
'dateTime' => $this->dateTime,
'branchNo' => $this->branchNo,
'merchantNo' => $this->merchantNo,
'type' => $this->type,
'bankSerialNo' => $this->transaction_id ? $this->transaction_id : '',
'date' => $this->date ? $this->date : '',
'orderNo' => $this->out_trade_no ? $this->out_trade_no : '',
'operatorNo' => $this->operator_no ? $this->operator_no : '',
];
// 这里不能进行过滤空值,招商的空值也要加入签名中
return $reqData;
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/4/28
* Time: 下午5:23
*/
namespace Payment\Common\Cmb\Data\Query;
use Payment\Common\Cmb\Data\CmbBaseData;
use Payment\Common\PayException;
/**
* Class RefundQueryData
* @package Payment\Common\Cmb\Data\Query
*
* @property string $date 订单日期,格式:yyyyMMdd
* @property string $out_trade_no 商户系统内部的订单号
* @property string $type 查询类型,A:按银行退款流水号查单笔 B:按商户订单号+商户退款流水号查单笔 C: 按商户订单号查退款
* @property string $refund_id 银行退款流水号,长度不超过20位
* @property string $refund_no 商户侧传给微信的退款单号
*
*/
class RefundQueryData extends CmbBaseData
{
protected function checkDataParam()
{
parent::checkDataParam();
$orderNo = $this->out_trade_no;
$date = $this->date;
$refundId = $this->refund_id;// 微信的退款交易号
$refundNo = $this->refund_no;// 商户的退款单号
if (empty($date)) {
throw new PayException('商户退款日期,格式:yyyyMMdd');
}
if (! empty($refundId)) {// 按银行退款流水号查单笔
$this->out_trade_no = '';
$this->refund_no = '';
$this->type = 'A';
} elseif (! empty($orderNo) && ! empty($refundNo)) {// 按商户订单号+商户退款流水号查单笔
$this->refund_id = '';
$this->type = 'B';
} elseif (! empty($orderNo)) {// 按商户订单号查退款
$this->refund_id = '';
$this->refund_no = '';
$this->type = 'C';
} else {
throw new PayException('请设置需要查询的商户订单号');
}
}
protected function getReqData()
{
$reqData = [
'dateTime' => $this->dateTime,
'branchNo' => $this->branchNo,
'merchantNo' => $this->merchantNo,
'type' => $this->type,
'orderNo' => $this->out_trade_no ? $this->out_trade_no : '',
'date' => $this->date,
'merchantSerialNo' => $this->refund_no ? $this->refund_no : '',
'bankSerialNo' => $this->refund_id ? $this->refund_id : '',
];
// 这里不能进行过滤空值,招商的空值也要加入签名中
return $reqData;
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/4/28
* Time: 下午3:16
*/
namespace Payment\Common\Cmb\Data;
use Payment\Common\PayException;
use Payment\Utils\Rc4Encrypt;
/**
* 退款API
* Class RefundData
* @package Payment\Common\Cmb\Data
*
* @property string $date 订单日期,格式:yyyyMMdd
* @property string $out_trade_no 商户系统内部的订单号
* @property string $refund_no 商户系统内部的退款单号,商户系统内部唯一,同一退款单号多次请求只退一笔
* @property float $refund_fee 退款总金额,订单总金额,只能为整数
* @property string $reason 退款的原因说明
* @property string $operator_id 商户的操作员编号
*
*/
class RefundData extends CmbBaseData
{
protected function checkDataParam()
{
parent::checkDataParam();
$refundNo = $this->refund_no;// 商户退款单号
$date = $this->date;// 商户订单日期,支付时的订单日期 格式:yyyyMMdd
$outTradeNo = $this->out_trade_no;
$refundFee = $this->refund_fee;
$operatorId = $this->operator_id;
if (empty($date) || mb_strlen($date) !== 8) {
throw new PayException('商户订单日期必须提供,格式:yyyyMMdd');
}
if (empty($outTradeNo)) {
throw new PayException('必须提供商户网站唯一订单号。');
}
if (empty($refundNo) && mb_strlen($refundNo) < 21) {
throw new PayException('退款流水号,商户生成,不能超过20位');
}
if (empty($refundFee) || ! is_numeric($refundFee)) {
throw new PayException('退款金额,格式xxxx.xx');
}
if (empty($operatorId)) {
throw new PayException('必须提供 商户结账系统的操作员号');
}
}
protected function getReqData()
{
$rc4 = new Rc4Encrypt($this->merKey);
$reqData = [
'dateTime' => $this->dateTime,
'branchNo' => $this->branchNo,
'merchantNo' => $this->merchantNo,
'date' => $this->date,
'orderNo' => $this->out_trade_no,
'refundSerialNo' => trim($this->refund_no),
'amount' => $this->refund_fee,
'desc' => $this->reason,
'operatorNo' => $this->operator_id,
'encrypType' => 'RC4',// 这里不让用户控制,直接采用 rc4加密
'pwd' => $rc4->encrypt($this->opPwd),
];
// 这里不能进行过滤空值,招商的空值也要加入签名中
return $reqData;
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/4/27
* Time: 上午11:28
*/
namespace Payment\Common;
use Payment\Utils\ArrayUtil;
use Payment\Utils\StrUtil;
class CmbConfig extends ConfigInterface
{
// 调用的接口版本,固定为:1.0
public $version = '1.0';
// 采用的编码
public $charset = 'UTF-8';
// 用于加密的 merKey
public $merKey;
// 发送请求的时间,格式"yyyyMMddHHmmss"
public $dateTime;
// 商户分行号,4位数字
public $branchNo;
// 商户号,6位数字
public $merchantNo;
// 用于同步通知的地址
public $returnUrl;
// 成功签约结果通知地址:首次签约,必填. 商户接收成功签约结果通知的地址。
public $signNoticeUrl;
// 操作员登录密码。
public $opPwd;
// 招商请求的网关
public $getewayUrl;
// 招商的公钥
public $rsaPubKey;
const MAX_EXPIRE_TIME = 30;// 过期时间最大 30分钟
const REQ_FILED_NAME = 'jsonRequestData';// 报文的参数名:jsonRequestData
const SUCC_TAG = 'SUC0000';// SUC0000表示成功,其他表示错误,具体错误码见详细API定义。
const TRADE_CODE = 'FBPK';// 交易码,固定为“FBPK”
const NOTICE_PAY = 'BKPAYRTN';// 支付成功回调
const NOTICE_SIGN = 'BKQY';// 签约成功回调
/**
* 初始化微信配置文件
* WxConfig constructor.
* @param array $config
* @throws PayException
*/
public function __construct(array $config)
{
try {
$this->initConfig($config);
} catch (PayException $e) {
throw $e;
}
}
/**
* 初始化配置文件
* @param array $config
* @throws PayException
*/
protected function initConfig(array $config)
{
$config = ArrayUtil::paraFilter($config);
// 初始 mer key
if (key_exists('mer_key', $config) && !empty($config['mer_key'])) {
$this->merKey = $config['mer_key'];
} else {
throw new PayException('Mer Key 不能为空,请前往招商一网通进行设置');
}
// 设置操作员登陆密码
if (key_exists('op_pwd', $config) && !empty($config['op_pwd'])) {
$this->opPwd = $config['op_pwd'];
} else {
throw new PayException('请设置操作员登陆密码');
}
// 检查 异步通知的url
if (key_exists('notify_url', $config) && !empty($config['notify_url'])) {
$this->notifyUrl = trim($config['notify_url']);
} else {
throw new PayException('异步通知的url必须提供.');
}
// 检查 签约异步通知的url
if (key_exists('sign_notify_url', $config) && !empty($config['sign_notify_url'])) {
$this->signNoticeUrl = trim($config['sign_notify_url']);
} else {
throw new PayException('签约 异步通知的url必须提供.');
}
// 商户分行号,4位数字
if (key_exists('branch_no', $config) && !empty($config['branch_no'])) {
$this->branchNo = trim($config['branch_no']);
} else {
throw new PayException('商户分行号必须提供,4位数字.');
}
// 商户号,6位数字
if (key_exists('merchant_no', $config) && !empty($config['merchant_no'])) {
$this->merchantNo = trim($config['merchant_no']);
} else {
throw new PayException('商户号必须提供,6位数字.');
}
$this->limitPay = '';
// 设置禁止使用的支付方式
if (
key_exists('limit_pay', $config) &&
!empty($config['limit_pay']) &&
strtoupper($config['limit_pay'][0]) === 'A'
) {
$this->limitPay = 'A';
}
if (key_exists('return_raw', $config)) {
$this->returnRaw = filter_var($config['return_raw'], FILTER_VALIDATE_BOOLEAN);
}
// 签名算法,固定为“SHA-256”
if (key_exists('sign_type', $config) && in_array($config['sign_type'], ['SHA-256'])) {
$this->signType = $config['sign_type'];
} else {
$this->signType = 'SHA-256';
}
if (isset($config['use_sandbox']) && $config['use_sandbox'] === true) {
$this->useSandbox = true;
} else {
$this->useSandbox = false;// 不是沙箱模式
}
if (key_exists('cmb_pub_key', $config) && (file_exists($config['cmb_pub_key']) || ! empty($config['cmb_pub_key']))) {
$this->rsaPubKey = StrUtil::getRsaKeyValue($config['cmb_pub_key'], 'public');
} else {
throw new PayException('请提供招商对应的rsa公钥,可通过Helper接口获取');
}
// 初始 支付宝 同步通知地址,可为空
if (key_exists('return_url', $config)) {
$this->returnUrl = $config['return_url'];
}
// 设置交易开始时间 格式为yyyyMMddHHmmss .再此之前一定要设置时区
$this->dateTime = date('YmdHis', time());
}
}
\ No newline at end of file
<?php
/**
* @author: helei
* @createTime: 2016-07-15 17:42
* @description: 配置文件接口,主要提供返回属性数组的功能
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment\Common;
abstract class ConfigInterface
{
// 是否返回原始数据
public $returnRaw = true;
// 是否使用测试模式
public $useSandbox = true;
// 禁止使用的支付渠道
public $limitPay;
// 用于异步通知的地址
public $notifyUrl;
// 加密方式
// 支付宝:默认使用RSA 目前支持RSA2和RSA
// 微信: 默认使用MD5
public $signType = 'RSA';
public function toArray()
{
return get_object_vars($this);
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-14 18:02
* @description: 统一的异常处理类
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment\Common;
class PayException extends \Exception
{
/**
* 获取异常错误信息
* @return string
* @author helei
*/
public function errorMessage()
{
return $this->getMessage();
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-15 14:56
* @description: 微信配置文件
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment\Common;
use Payment\Utils\ArrayUtil;
use Payment\Utils\StrUtil;
final class TLConfig extends ConfigInterface
{
// 应用ID
public $appId;
// 平台分配的商户号
public $cusid;
// 随机字符串,不长于32位
public $randomstr;
// 符合ISO 4217标准的三位字母代码
public $feeType = 'CNY';
// 用于加密的md5Key
public $md5Key;
// 交易方式,小程序:W06
public $paytype;
// 指定回调页面
public $returnUrl;
public $version = 11;//接口版本号
// 统一下单url
const UNIFIED_URL = 'https://vsp.allinpay.com/apiweb/unitorder/pay';
// 撤销交易url
const ORDER_CANCEL_URL = 'https://vsp.allinpay.com/apiweb/unitorder/cancel';
// 申请退款url
const REFUND_URL = 'https://vsp.allinpay.com/apiweb/unitorder/refund';
// 支付查询url
const CHARGE_QUERY_URL = 'https://vsp.allinpay.com/apiweb/unitorder/query';
// 退款账户
const REFUND_UNSETTLED = 'REFUND_SOURCE_UNSETTLED_FUNDS';// 未结算资金退款(默认使用未结算资金退款)
const REFUND_RECHARGE = 'REFUND_SOURCE_RECHARGE_FUNDS';// 可用余额退款(限非当日交易订单的退款)
/**
* 初始化配置文件
* TLConfig constructor.
* @param array $config
* @throws PayException
*/
public function __construct(array $config)
{
try {
$this->initConfig($config);
} catch (PayException $e) {
throw $e;
}
}
/**
* 初始化配置文件参数
* @param array $config
* @throws PayException
*/
private function initConfig(array $config)
{
$config = ArrayUtil::paraFilter($config);
// 检查 分配的公众账号ID
if (key_exists('app_id', $config) && !empty($config['app_id'])) {
$this->appId = $config['app_id'];
} else {
throw new PayException('必须提供支付平台分配的公众账号ID');
}
// 检查 平台支付分配的商户号
if (key_exists('cus_id', $config) && !empty($config['cus_id'])) {
$this->cusid = $config['cus_id'];
} else {
throw new PayException('必须提供支付平台分配的商户号');
}
// 初始 MD5 key
if (key_exists('md5_key', $config) && !empty($config['md5_key'])) {
$this->md5Key = $config['md5_key'];
} else {
throw new PayException('MD5 Key 不能为空,再通联商户后台可查看');
}
// 检查 异步通知的url
if (key_exists('notify_url', $config) && !empty($config['notify_url'])) {
$this->notifyUrl = trim($config['notify_url']);
} else {
throw new PayException('异步通知的url必须提供.');
}
// 设置禁止使用的支付方式
if (key_exists('limit_pay', $config) && !empty($config['limit_pay']) && $config['limit_pay'][0] === 'no_credit') {
$this->limitPay = $config['limit_pay'][0];
}
if (key_exists('return_raw', $config)) {
$this->returnRaw = filter_var($config['return_raw'], FILTER_VALIDATE_BOOLEAN);
}
if (key_exists('redirect_url', $config)) {
$this->returnUrl = $config['redirect_url'];
}
// 生成随机字符串
$this->randomstr = StrUtil::getNonceStr();
}
}
<?php
namespace Payment\Common\TLpay\Data\Cancel;
use Payment\Common\PayException;
use Payment\Common\TLpay\Data\TLBaseData;
use Payment\Utils\ArrayUtil;
/**
* 用户退款
* Class RefundData
*
* @package Payment\Common\TLpay\Data\Cancel
* anthor yeran
*/
class RefundData extends TLBaseData
{
/**
* 退款数据封装
*/
protected function buildData()
{
$this->retData = [
'appid' => $this->appId,
'cusid' => $this->cusid,
'randomstr' => $this->randomstr,
'version' => $this->version,
'trxamt' => $this->trxamt,// 退款金额,分
'reqsn' => $this->reqsn,// 商户退款单号
'oldreqsn' => $this->oldreqsn,//原交易的商户订单号
'oldtrxid' => $this->oldtrxid,//原交易的收银宝平台流水
'remark' => $this->remark,// 备注
];
$this->retData = ArrayUtil::paraFilter($this->retData);
}
/**
* 检查参数
* @author yeran
*/
protected function checkDataParam()
{
$reqsn = $this->reqsn;// 商户退款单号
$oldreqsn = $this->oldreqsn;
$oldtrxid = $this->oldtrxid;
$trxamt = $this->trxamt;
if (empty($reqsn)) {
throw new PayException('请设置退款单号 refund_no');
}
// 二者不能同时为空
if (empty($oldreqsn) && empty($oldtrxid)) {
throw new PayException('必须提供通联支付交易号或商户网站唯一订单号。建议使用通联支付交易号');
}
if ($trxamt<=1) {
throw new PayException('退款金额异常');
}
}
}
<?php
namespace Payment\Common\TLpay\Data\Cancel;
use Payment\Common\PayException;
use Payment\Common\TLpay\Data\TLBaseData;
use Payment\Utils\ArrayUtil;
/**
* Class TLChargeData
* 通联支付
*
* @package Payment\Common\TLpay\Data\Charge
* anthor yeran
*/
class TLCancelData extends TLBaseData
{
protected function checkDataParam()
{
$oldreqsn = $this->oldreqsn;//原交易的商户交易单号
$oldtrxid = $this->oldtrxid;//原交易的收银宝平台流水
$reqsn = $this->reqsn;//商户的退款交易订单号,商户平台唯一
if (empty($oldreqsn) || empty($oldtrxid) || empty($reqsn)) {
throw new PayException('单号数据缺失');
}
}
/**
* 组装数据,用于计算sign,以及与支付平台对接
*
* 交易撤销数据
*
*/
protected function buildData()
{
$signData = [
// 基本数据
'appid' => trim($this->appId),
'cusid' => trim($this->cusid),
'version' => $this->version,
'randomstr' => $this->randomstr,
// 业务数据
'reqsn' => trim($this->reqsn),//商户退款交易单号
'trxamt' => $this->trxamt,//单位分,原订单金额
'oldreqsn' => $this->oldreqsn,//原交易的商户交易单号
'oldtrxid' => $this->oldtrxid,//原交易的收银宝平台流水
];
// 移除数组中的空值
$this->retData = ArrayUtil::paraFilter($signData);
}
/**
* 处理支付平台的返回值并返回给客户端
* @param array $ret
* @return mixed
* @author yeran
*/
protected function retData(array $ret){
// 字段 含义 取值 可空 最大长度 备注
//cusid 商户号 平台分配的商户号 否 15
//appid 应用ID 平台分配的APPID 否 8
//trxid 交易单号 收银宝平台的退款交易流水号 否 20
//reqsn 商户订单号 商户的退款交易订单号 否 32
//trxstatus 交易状态 交易的状态 否 4 见3.1
//fintime 交易完成时间 yyyyMMddHHmmss 是 14
//errmsg 错误原因 失败的原因说明 是 100
//randomstr 随机字符串 随机生成的字符串 否 32
//sign 签名 否 32
return $ret;
}
}
<?php
/**
* @author: helei
* @createTime: 2016-08-02 10:27
* @description:
*/
namespace Payment\Common\TLpay\Data;
/**
* Class BackPubChargeData
* 小程序数据也在这里处理
* @property string $device_info 设备号
* @property string $trade_type 交易类型
* @property string $prepay_id 预支付交易会话标识
*
* @package Payment\Common\Weixin\Data
* anthor helei
*/
class BackTLChargeData extends TLBaseData
{
protected function buildData()
{
$this->retData = [
'appId' => $this->appid,
'timeStamp' => time() . '',
'nonceStr' => $this->randomstr,
'package' => 'prepay_id=' . $this->chnltrxid,
'signType' => 'MD5',// 签名算法,暂支持MD5
];
}
protected function checkDataParam()
{
// 不进行检查
}
}
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 16/7/30
* Time: 下午11:08
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment\Common\TLpay\Data\Charge;
use Payment\Common\PayException;
use Payment\Common\TLpay\Data\TLBaseData;
use Payment\Config;
/**
* Class ChargeBaseData
*
* @inheritdoc
*
* @property string $reqsn
* @property string $trxamt
* @property string $client_ip 用户端实际ip
* @property string $body
* @property string $return_param 附加数据,在查询API和支付通知中原样返回
* @property integer $validtime 订单有效时间,默认为5分钟,最长60分钟
*
* @package Payment\Common\TLpay\Data\Charge
*/
abstract class ChargeBaseData extends TLBaseData
{
/**
* 检查传入的支付信息是否正确
*/
protected function checkDataParam()
{
$reqsn = $this->reqsn;
$trxamt = $this->trxamt;//分
$body = $this->body;
// 检查订单号是否合法
if (empty($reqsn) || mb_strlen($reqsn) > 64) {
throw new PayException('订单号不能为空,并且长度不能超过64位');
}
// 检查金额不能低于0.01
if (bccomp($trxamt, 100 * Config::PAY_MIN_FEE, 2) === -1) {
throw new PayException('支付金额不能低于 ' . Config::PAY_MIN_FEE . ' 元');
}
// 检查 商品名称 与 商品描述
if (empty($body)) {
throw new PayException('必须提供订单商品名称');
}
// 订单有效时间,以分为单位,不填默认为5分钟,最大60分钟
if(!$this->validtime)
$this->validtime = 5;
if ($this->validtime >60) {
throw new PayException('订单有效时间最大60分钟');
}
// 设置ip地址
$clientIp = $this->client_ip;
if (empty($clientIp)) {
$this->client_ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1';
}
}
}
<?php
namespace Payment\Common\TLpay\Data\Charge;
use Payment\Common\PayException;
use Payment\Utils\ArrayUtil;
/**
* Class TLChargeData
* 通联支付
*
* @property string $openid trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识
* @property string $sub_appid 微信分配的子商户公众账号ID
* @property string $sub_mch_id 微信支付分配的子商户号
* @property string $sub_openid 用户在子商户appid下的唯一标识
*
* @package Payment\Common\Weixin\Data\Charge
* anthor yeran
*/
class TLChargeData extends ChargeBaseData
{
protected function checkDataParam()
{
parent::checkDataParam(); // TODO: Change the autogenerated stub
// 公众号支付,必须设置openid
$acct = $this->acct;
if (empty($acct)) {
throw new PayException('用户唯一标识,必须携带');
}
}
/**
* 组装数据,用于计算sign,以及与支付平台对接
*/
protected function buildData()
{
$signData = [
// 基本数据
'appid' => trim($this->appId),
'cusid' => trim($this->cusid),
'version' => $this->version,
'randomstr' => $this->randomstr,
'paytype' => $this->paytype,
'notify_url' => $this->notifyUrl,
'limit_pay' => $this->limitPay, // 指定不使用信用卡
// 业务数据
'body' => trim($this->body),
'remark' => trim($this->remark),
'reqsn' => trim($this->reqsn),
'trxamt' => $this->trxamt,//单位分
'acct' => $this->acct
];
// 移除数组中的空值
$this->retData = ArrayUtil::paraFilter($signData);
}
}
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/3/6
* Time: 下午10:05
*/
namespace Payment\Common\TLpay\Data\Query;
use Payment\Common\PayException;
use Payment\Common\TLpay\Data\TLBaseData;
use Payment\Utils\ArrayUtil;
/**
* 查询交易的数据结构
* Class TLQueryData
*
*
* @package Payment\Common\TLpay\Data\Query
*/
class TLQueryData extends TLBaseData
{
protected function buildData()
{
$this->retData = [
'appid' => $this->appId,
'cusid' => $this->cusid,
'version' => $this->version,
'randomstr' => $this->randomstr,
'reqsn' => $this->reqsn,//商户的交易订单号
'trxid' => $this->trxid,//支付的收银宝平台流水
];
$this->retData = ArrayUtil::paraFilter($this->retData);
}
protected function checkDataParam()
{
$trxid = $this->trxid;// 支付的收银宝平台流水
$reqsn = $this->reqsn;// 商户订单号,查询效率低,不建议使用
// 二者不能同时为空
if (empty($trxid) && empty($reqsn)) {
throw new PayException('必须提供通联支付交易号或商户网站唯一订单号。建议使用通联支付交易号');
}
}
}
\ No newline at end of file
<?php
/**
* @author: helei
* @createTime: 2016-07-28 18:05
* @description: 微信支付相关接口的数据基类
*/
namespace Payment\Common\TLpay\Data;
use Payment\Common\BaseData;
use Payment\Utils\StrUtil;
/**
* Class BaseData
*
*
* @package Payment\Common\TLpay\Data
*/
abstract class TLBaseData extends BaseData
{
/**
* 签名算法实现 通联支付sign生成算法:sign = md5(string.getbyte("utf-8")).toUpperCase();
* @param string $signStr
* @return string
*/
protected function makeSign($signStr){
return strtoupper(md5(StrUtil::getBytes($signStr)));
}
}
<?php
namespace Payment\Common\TLpay;
use Payment\Common\BaseData;
use Payment\Common\BaseStrategy;
use Payment\Common\PayException;
use Payment\Common\TLConfig;
use Payment\Utils\ArrayUtil;
use Payment\Utils\Curl;
/**
* Created by IntelliJ IDEA.
* User: yeran
* Date: 2018/4/21
* Time: 下午11:39
*/
abstract class TLBaseStrategy implements BaseStrategy{
/**
* 通联支付的配置文件
* @var TLConfig $config
*/
protected $config;
/**
* 支付数据
* @var BaseData $reqData
*/
protected $reqData;
/**
* WxBaseStrategy constructor.
* @param array $config
* @throws PayException
*/
public function __construct(array $config)
{
/* 设置内部字符编码为 UTF-8 */
mb_internal_encoding("UTF-8");
try {
$this->config = new TLConfig($config);
} catch (PayException $e) {
throw $e;
}
}
/**
* 发送完了请求
* @param array $body
* @return mixed
* @throws PayException
* @author helei
*/
protected function sendReq($body)
{
$url = $this->getReqUrl();
if (is_null($url)) {
throw new PayException('目前不支持该接口。请联系开发者添加');
}
$responseTxt = $this->curlPost($body, $url);
// 格式化为数组
$retData = json_decode($responseTxt,true);
// var_dump($retData);
if ($retData['retcode'] != 'SUCCESS') { //通信标识
throw new PayException('支付平台返回错误提示1:' . $retData['retmsg']);
}
if ($retData['trxstatus'] != '0000') {//交易标识
$msg = $retData['errmsg']?:'交易失败-系统繁忙';
throw new PayException('支付平台返回错误提示2:' . $msg);
}
return $retData;
}
/**
* 父类仅提供基础的post请求,子类可根据需要进行重写
* @param array $body
* @param string $url
* @return mixed
* @author helei
*/
protected function curlPost($body, $url)
{
$curl = new Curl();
$paramsStr = ArrayUtil::ToUrlParams($body);
return $curl->request($url, $paramsStr);
}
/**
* 获取需要的url 默认返回下单的url
* @author helei
* @return string|null
*/
protected function getReqUrl()
{
return TLConfig::UNIFIED_URL;
}
/**
* @param array $data
* @author helei
* @throws PayException
* @return array|string
*/
public function handle(array $data)
{
$buildClass = $this->getBuildDataClass();
try {
$this->reqData = new $buildClass($this->config, $data);
} catch (PayException $e) {
throw $e;
}
$this->reqData->setSign();//计算sign,整理数据格式
$body = $this->reqData->getData();
$ret = $this->sendReq($body);
// 检查返回的数据是否被篡改
$flag = $this->verifySign($ret);
if (!$flag) {
throw new PayException('支付平台返回数据被篡改。请检查网络是否安全!');
}
return $this->retData($ret);
}
/**
* 处理支付平台的返回值并返回给客户端
* @param array $ret
* @return mixed
* @author helei
*/
protected function retData(array $ret)
{
return $ret;
}
/**
* 检查支付平台返回的数据是否被篡改过:sign=md5(string.getbyte("utf-8")).toUpperCase()
* @param array $retData
* @return boolean
* @author helei
*/
protected function verifySign(array $retData)
{
$retSign = $retData['sign'];
$values = ArrayUtil::removeKeys($retData, ['sign', 'sign_type']);
$values = ArrayUtil::paraFilter($values);
$signStr = ArrayUtil::SignArray($values,$this->config->md5Key);
return strtoupper($signStr) === $retSign;
}
}
\ No newline at end of file
<?php
/**
* @author: helei
* @createTime: 2016-08-02 09:41
* @description:
*/
namespace Payment\Common\Weixin\Data;
/**
* Class BackAppChargeData
*
* @property string $device_info 设备号
* @property string $trade_type 交易类型
* @property string $prepay_id 预支付交易会话标识
*
* @package Payment\Common\Weixin\Data
* anthor helei
*/
class BackAppChargeData extends WxBaseData
{
protected function buildData()
{
$this->retData = [
'appid' => $this->appId,
'partnerid' => $this->mchId,
'prepayid' => $this->prepay_id,
'package' => 'Sign=WXPay',
'noncestr' => $this->nonceStr,
'timestamp' => time(),
];
}
protected function checkDataParam()
{
// 对于返回数据不做检查检查
}
}
<?php
/**
* @author: helei
* @createTime: 2016-08-02 10:27
* @description:
*/
namespace Payment\Common\Weixin\Data;
/**
* Class BackPubChargeData
* 小程序数据也在这里处理
* @property string $device_info 设备号
* @property string $trade_type 交易类型
* @property string $prepay_id 预支付交易会话标识
*
* @package Payment\Common\Weixin\Data
* anthor helei
*/
class BackPubChargeData extends WxBaseData
{
protected function buildData()
{
$this->retData = [
'appId' => $this->appId,
'timeStamp' => time() . '',
'nonceStr' => $this->nonceStr,
'package' => 'prepay_id=' . $this->prepay_id,
'signType' => 'MD5',// 签名算法,暂支持MD5
];
}
protected function checkDataParam()
{
// 不进行检查
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-28 18:05
* @description:
*/
namespace Payment\Common\Weixin\Data\Charge;
use Payment\Utils\ArrayUtil;
/**
* Class AppChargeData
* 微信APP支付
* @package Payment\Common\Weixin\Data\Charge
*/
class AppChargeData extends ChargeBaseData
{
protected function buildData()
{
$signData = [
// 基本数据
'appid' => trim($this->appId),
'mch_id' => trim($this->mchId),
'nonce_str' => $this->nonceStr,
'sign_type' => $this->signType,
'fee_type' => $this->feeType,
'notify_url' => $this->notifyUrl,
'trade_type' => $this->tradeType, //设置APP支付
'limit_pay' => $this->limitPay, // 指定不使用信用卡
// 业务数据
'device_info' => $this->terminal_id,
'body' => trim($this->subject),
//'detail' => json_encode($this->body, JSON_UNESCAPED_UNICODE);
'attach' => trim($this->return_param),
'out_trade_no' => trim($this->order_no),
'total_fee' => $this->amount,
'spbill_create_ip' => trim($this->client_ip),
'time_start' => $this->timeStart,
'time_expire' => $this->timeout_express,
];
// 移除数组中的空值
$this->retData = ArrayUtil::paraFilter($signData);
}
}
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 17/3/6
* Time: 上午8:49
*/
namespace Payment\Common\Weixin\Data\Charge;
use Payment\Common\PayException;
use Payment\Utils\ArrayUtil;
/**
* Class WebChargeData
*
* @inheritdoc
* @property string $auth_code 扫码支付授权码,设备读取用户微信中的条码或者二维码信息
* @property string $sub_appid 微信分配的子商户公众账号ID
* @property string $sub_mch_id 微信支付分配的子商户号
*
* @package Payment\Common\Weixin\Data\Charge
*/
class BarChargeData extends ChargeBaseData
{
protected function checkDataParam()
{
parent::checkDataParam(); // TODO: Change the autogenerated stub
// 刷卡支付,必须设置auth_code
$authCode = $this->auth_code;
if (empty($authCode)) {
throw new PayException('扫码支付授权码,必须设置该参数.');
}
}
/**
* 生成下单的数据
*/
protected function buildData()
{
$signData = [
// 基本数据
'appid' => trim($this->appId),
'mch_id' => trim($this->mchId),
'nonce_str' => $this->nonceStr,
'sign_type' => $this->signType,
'fee_type' => $this->feeType,
// 业务数据
'device_info' => $this->terminal_id,
'body' => trim($this->subject),
//'detail' => json_encode($this->body, JSON_UNESCAPED_UNICODE);
'attach' => trim($this->return_param),
'out_trade_no' => trim($this->order_no),
'total_fee' => $this->amount,
'spbill_create_ip' => trim($this->client_ip),
'auth_code' => $this->auth_code,
// 服务商
'sub_appid' => $this->sub_appid,
'sub_mch_id' => $this->sub_mch_id,
];
// 移除数组中的空值
$this->retData = ArrayUtil::paraFilter($signData);
}
}
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 16/7/30
* Time: 下午11:08
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment\Common\Weixin\Data\Charge;
use Payment\Common\PayException;
use Payment\Common\Weixin\Data\WxBaseData;
use Payment\Config;
/**
* Class ChargeBaseData
*
* @inheritdoc
*
* @property string $order_no
* @property string $amount
* @property string $client_ip 用户端实际ip
* @property string $subject 商品详情 商品详细列表,使用Json格式,传输签名前请务必使用CDATA标签将JSON文本串保护起来。 暂时未使用
* @property string $body
* @property string $return_param 附加数据,在查询API和支付通知中原样返回
* @property integer $timeout_express 订单失效时间 格式为yyyyMMddHHmmss
*
* @package Payment\Common\Weixin\Data\Charge
*/
abstract class ChargeBaseData extends WxBaseData
{
/**
* 检查传入的支付信息是否正确
*/
protected function checkDataParam()
{
$orderNo = $this->order_no;
$amount = $this->amount;
$subject = $this->subject;
$body = $this->body;
$deviceInfo = $this->terminal_id;
// 检查订单号是否合法
if (empty($orderNo) || mb_strlen($orderNo) > 64) {
throw new PayException('订单号不能为空,并且长度不能超过64位');
}
// 检查金额不能低于0.01
if (bccomp($amount, Config::PAY_MIN_FEE, 2) === -1) {
throw new PayException('支付金额不能低于 ' . Config::PAY_MIN_FEE . ' 元');
}
// 检查 商品名称 与 商品描述
if (empty($subject) || empty($body)) {
throw new PayException('必须提供商品名称与商品详情');
}
// 初始 微信订单过期时间,最短失效时间间隔必须大于5分钟
if ($this->timeout_express - strtotime($this->timeStart) < 5) {
throw new PayException('必须设置订单过期时间,且需要大于5分钟.如果不正确请检查是否正确设置时区');
} else {
$this->timeout_express = date('YmdHis', $this->timeout_express);
}
// 微信使用的单位位分.此处进行转化
$this->amount = bcmul($amount, 100, 0);
// 设置ip地址
$clientIp = $this->client_ip;
if (empty($clientIp)) {
$this->client_ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1';
}
// 设置设备号
if (empty($deviceInfo)) {
$this->terminal_id = 'WEB';
}
}
}
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 16/7/31
* Time: 上午9:20
*/
namespace Payment\Common\Weixin\Data\Charge;
use Payment\Common\PayException;
use Payment\Utils\ArrayUtil;
/**
* Class PubChargeData
* 微信公众号支付
*
* @property string $openid trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识
* @property string $sub_appid 微信分配的子商户公众账号ID
* @property string $sub_mch_id 微信支付分配的子商户号
* @property string $sub_openid 用户在子商户appid下的唯一标识
*
* @package Payment\Common\Weixin\Data\Charge
* anthor helei
*/
class PubChargeData extends ChargeBaseData
{
protected function checkDataParam()
{
parent::checkDataParam(); // TODO: Change the autogenerated stub
// 公众号支付,必须设置openid
$openid = $this->openid;
if (empty($openid)) {
throw new PayException('用户在商户appid下的唯一标识,公众号支付,必须设置该参数.');
}
$subMchId = $this->sub_mch_id;// 如果是服务商模式,则 sub_openid 必须提供
$subOpenid = $this->sub_openid;
if ($subMchId && empty($subOpenid)) {
throw new PayException('公众号的服务商模式,必须提供 sub_openid 参数.');
}
}
protected function buildData()
{
$signData = [
// 基本数据
'appid' => trim($this->appId),
'mch_id' => trim($this->mchId),
'nonce_str' => $this->nonceStr,
'sign_type' => $this->signType,
'fee_type' => $this->feeType,
'notify_url' => $this->notifyUrl,
'trade_type' => $this->tradeType, //设置APP支付
'limit_pay' => $this->limitPay, // 指定不使用信用卡
// 业务数据
'device_info' => $this->terminal_id,
'body' => trim($this->subject),
//'detail' => json_encode($this->body, JSON_UNESCAPED_UNICODE);
'attach' => trim($this->return_param),
'out_trade_no' => trim($this->order_no),
'total_fee' => $this->amount,
'spbill_create_ip' => trim($this->client_ip),
'time_start' => $this->timeStart,
'time_expire' => $this->timeout_express,
'openid' => $this->openid,
];
// 移除数组中的空值
$this->retData = ArrayUtil::paraFilter($signData);
}
}
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 16/7/31
* Time: 上午8:49
*/
namespace Payment\Common\Weixin\Data\Charge;
use Payment\Common\PayException;
use Payment\Utils\ArrayUtil;
/**
* Class WebChargeData
*
* @inheritdoc
* @property string $product_id 扫码支付时,必须设置该参数
* @property string $openid trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识
* @property string $sub_appid 微信分配的子商户公众账号ID
* @property string $sub_mch_id 微信支付分配的子商户号
*
* @package Payment\Common\Weixin\Data\Charge
*/
class QrChargeData extends ChargeBaseData
{
/**
* 生成下单的数据
*/
protected function buildData()
{
$signData = [
// 基本数据
'appid' => trim($this->appId),
'mch_id' => trim($this->mchId),
'nonce_str' => $this->nonceStr,
'sign_type' => $this->signType,
'fee_type' => $this->feeType,
'notify_url' => $this->notifyUrl,
'trade_type' => $this->tradeType, //设置APP支付
'limit_pay' => $this->limitPay, // 指定不使用信用卡
// 业务数据
'device_info' => $this->terminal_id,
'body' => trim($this->subject),
//'detail' => json_encode($this->body, JSON_UNESCAPED_UNICODE);
'attach' => trim($this->return_param),
'out_trade_no' => trim($this->order_no),
'total_fee' => $this->amount,
'spbill_create_ip' => trim($this->client_ip),
'time_start' => $this->timeStart,
'time_expire' => $this->timeout_express,
'openid' => $this->openid,
'product_id' => $this->product_id,
// 服务商
'sub_appid' => $this->sub_appid,
'sub_mch_id' => $this->sub_mch_id,
];
// 移除数组中的空值
$this->retData = ArrayUtil::paraFilter($signData);
}
protected function checkDataParam()
{
parent::checkDataParam(); // TODO: Change the autogenerated stub
// 扫码支付,必须设置product_id 参数
$productId = $this->product_id;
if (empty($productId)) {
throw new PayException('扫码支付,必须设置商品ID.');
}
}
}
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/3/6
* Time: 下午5:45
*/
namespace Payment\Common\Weixin\Data\Charge;
use Payment\Common\PayException;
use Payment\Utils\ArrayUtil;
/**
* 构建wap支付的下单数据
* Class WapChargeData
*
* @property array $scene_info 该字段用于上报支付的场景信息
*
* @package Payment\Common\Weixin\Data\Charge
*/
class WapChargeData extends ChargeBaseData
{
protected function checkDataParam()
{
parent::checkDataParam(); // TODO: Change the autogenerated stub
$info = $this->scene_info;
if (! is_array($info) || empty($info)) {
throw new PayException('微信 H5 支付,必须提供该参数');
}
}
protected function buildData()
{
$info = $this->scene_info;
$sceneInfo['h5_info'] = $info;
$signData = [
// 基本数据
'appid' => trim($this->appId),
'mch_id' => trim($this->mchId),
'nonce_str' => $this->nonceStr,
'sign_type' => $this->signType,
'fee_type' => $this->feeType,
'notify_url' => $this->notifyUrl,
'trade_type' => $this->tradeType, //设置APP支付
'limit_pay' => $this->limitPay, // 指定不使用信用卡
// 业务数据
'device_info' => $this->terminal_id,
'body' => trim($this->subject),
//'detail' => json_encode($this->body, JSON_UNESCAPED_UNICODE);
'attach' => trim($this->return_param),
'out_trade_no' => trim($this->order_no),
'total_fee' => $this->amount,
'spbill_create_ip' => trim($this->client_ip),
'time_start' => $this->timeStart,
'time_expire' => $this->timeout_express,
'scene_info' => json_encode($sceneInfo),
];
// 移除数组中的空值
$this->retData = ArrayUtil::paraFilter($signData);
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/3/6
* Time: 下午10:05
*/
namespace Payment\Common\Weixin\Data\Query;
use Payment\Common\PayException;
use Payment\Common\Weixin\Data\WxBaseData;
use Payment\Utils\ArrayUtil;
/**
* 查询交易的数据结构
* Class ChargeQueryData
*
* @property string $transaction_id 微信的订单号,优先使用
* @property string $out_trade_no 商户系统内部的订单号
*
* @package Payment\Common\Weixin\Data\Query
*/
class ChargeQueryData extends WxBaseData
{
protected function buildData()
{
$this->retData = [
'appid' => $this->appId,
'mch_id' => $this->mchId,
'nonce_str' => $this->nonceStr,
'sign_type' => $this->signType,
'transaction_id' => $this->transaction_id,
'out_trade_no' => $this->out_trade_no,
];
$this->retData = ArrayUtil::paraFilter($this->retData);
}
protected function checkDataParam()
{
$transaction_id = $this->transaction_id;// 微信交易号,查询效率高
$order_no = $this->out_trade_no;// 商户订单号,查询效率低,不建议使用
// 二者不能同时为空
if (empty($transaction_id) && empty($order_no)) {
throw new PayException('必须提供微信交易号或商户网站唯一订单号。建议使用微信交易号');
}
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/3/7
* Time: 下午1:32
*/
namespace Payment\Common\Weixin\Data\Query;
use Payment\Common\PayException;
use Payment\Common\Weixin\Data\WxBaseData;
use Payment\Utils\ArrayUtil;
/**
* 微信退款接口查询
*
* @property string $transaction_id 微信的订单号,优先使用
* @property string $out_trade_no 商户系统内部的订单号
* @property string $refund_no 商户侧传给微信的退款单号
* @property string $refund_id 微信生成的退款单号,在申请退款接口有返回
*
* Class RefundQueryData
* @package Payment\Common\Weixin\Data\Query
*/
class RefundQueryData extends WxBaseData
{
protected function buildData()
{
$this->retData = [
'appid' => $this->appId,
'mch_id' => $this->mchId,
'device_info' => $this->terminal_id,
'nonce_str' => $this->nonceStr,
'sign_type' => $this->signType,
'transaction_id' => $this->transaction_id,
'out_trade_no' => $this->out_trade_no,
'out_refund_no' => $this->refund_no,
'refund_id' => $this->refund_id,
];
$this->retData = ArrayUtil::paraFilter($this->retData);
}
protected function checkDataParam()
{
$transactionId = $this->transaction_id;// 微信交易号,查询效率高
$orderNo = $this->out_trade_no;// 商户订单号,查询效率低,不建议使用
$refundNo = $this->refund_no;// 商户的退款单号
$refundId = $this->refund_id;// 微信的退款交易号
// 四者不能同时为空
if (empty($transactionId) && empty($orderNo) && empty($refundNo) && empty($refundId)) {
throw new PayException('查询退款 必须提供微信交易号、商户订单号、商户退款单号、微信退款交易号中的一种');
}
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/3/7
* Time: 下午4:55
*/
namespace Payment\Common\Weixin\Data\Query;
use Payment\Common\PayException;
use Payment\Common\Weixin\Data\WxBaseData;
use Payment\Utils\ArrayUtil;
/**
* 微信转账查询 当前微信仅支持 商户订单号 来进行查询
*
* @property string $trans_no 商户转账唯一订单号
*
* Class TransferQueryData
* @package Payment\Common\Weixin\Data\Query
*/
class TransferQueryData extends WxBaseData
{
protected function buildData()
{
$this->retData = [
'appid' => $this->appId,
'mch_id' => $this->mchId,
'nonce_str' => $this->nonceStr,
//'sign_type' => $this->signType,// 转账查询,不能加入该数据
'partner_trade_no' => $this->trans_no,
];
$this->retData = ArrayUtil::paraFilter($this->retData);
}
protected function checkDataParam()
{
$transNo = $this->trans_no;
if (empty($transNo)) {
throw new PayException('请提供商户调用企业付款API时使用的商户订单号');
}
}
}
\ No newline at end of file
<?php
/**
* @author: helei
* @createTime: 2016-08-03 15:14
* @description:
*/
namespace Payment\Common\Weixin\Data;
use Payment\Common\PayException;
use Payment\Common\WxConfig;
use Payment\Utils\ArrayUtil;
/**
* 用户退款
* Class RefundData
*
* @property string $refund_no 商户系统内部的退款单号,商户系统内部唯一,同一退款单号多次请求只退一笔
* @property string $transaction_id 微信生成的订单号,在支付通知中有返回
* @property string $out_trade_no 商户侧传给微信的订单号
* @property int $total_fee 订单总金额,单位为分,只能为整数
* @property int $refund_fee 退款总金额,订单总金额,单位为分,只能为整数
* @property string $operator_id 操作员帐号, 默认为商户号
* @property string $refund_account 退款账户 UNSETTLED:未结算资金退款 RECHARGE:可用余额退款
*
* @package Payment\Common\Weixin\Data
* anthor helei
*/
class RefundData extends WxBaseData
{
protected function buildData()
{
$this->retData = [
'appid' => $this->appId,
'mch_id' => $this->mchId,
'device_info' => $this->terminal_id,
'nonce_str' => $this->nonceStr,
'refund_fee_type' => $this->feeType,
'transaction_id' => $this->transaction_id,
'out_trade_no' => $this->out_trade_no,
'out_refund_no' => $this->refund_no,// 商户退款单号
'total_fee' => $this->total_fee,// 订单总金额
'refund_fee' => $this->refund_fee,// 退款总金额
'op_user_id' => $this->operator_id,//操作员帐号, 默认为商户号
'refund_account' => $this->refund_account,// 退款账户类型
];
$this->retData = ArrayUtil::paraFilter($this->retData);
}
/**
* 检查参数
* @author helei
*/
protected function checkDataParam()
{
$refundNo = $this->refund_no;// 商户退款单号
$transactionId = $this->transaction_id;
$outTradeNo = $this->out_trade_no;
$totalFee = $this->total_fee;
$refundFee = $this->refund_fee;
$operatorId = $this->operator_id;
$refundAccount = $this->refund_account;// 退款账户
if (empty($refundNo)) {
throw new PayException('请设置退款单号 refund_no');
}
// 二者不能同时为空
if (empty($transactionId) && empty($outTradeNo)) {
throw new PayException('必须提供微信交易号或商户网站唯一订单号。建议使用微信交易号');
}
$this->total_fee = bcmul($totalFee, 100, 0);// 微信以分为单位
$this->refund_fee = bcmul($refundFee, 100, 0);
if (bccomp($refundFee, $totalFee, 2) === 1) {
throw new PayException('退款金额不能大于订单总金额');
}
if (! in_array($refundAccount, [WxConfig::REFUND_RECHARGE, WxConfig::REFUND_UNSETTLED])) {
$this->refund_account = WxConfig::REFUND_UNSETTLED;
}
// 该接口,微信配置文件,必须提供cert key 两个pem文件
$certPath = $this->appCertPem;
$keyPath = $this->appKeyPem;
if (empty($certPath)) {
throw new PayException('退款接口,必须提供 apiclient_cert.pem 证书');
}
if (empty($keyPath)) {
throw new PayException('退款接口,必须提供 apiclient_key.pem 证书');
}
if (empty($operatorId)) {
$this->operator_id = $this->mchId;
}
}
}
<?php
/**
* @author: helei
* @createTime: 2016-08-04 09:42
* @description:
*/
namespace Payment\Common\Weixin\Data;
use Payment\Common\PayException;
use Payment\Utils\ArrayUtil;
/**
* Class TransferData
*
* 微信当前也仅支持单笔付款,不支持批量
*
* @property string $trans_no 商户转账唯一订单号
* @property string $openid 商户appid下,某用户的openid
* @property string $check_name
* - NO_CHECK:不校验真实姓名
* - FORCE_CHECK:强校验真实姓名(未实名认证的用户会校验失败,无法转账)
* - OPTION_CHECK:针对已实名认证的用户才校验真实姓名(未实名认证用户不校验,可以转账成功)
*
* @property string $payer_real_name 收款用户真实姓名。
* @property string $amount 转账金额,单位:元。 只支持2位小数,小数点前最大支持13位,金额必须大于0。
* @property string $desc 企业付款操作说明信息。必填。
* @property string $client_ip 调用接口的机器Ip地址
*
* @package Payment\Common\Weixin\Data
* anthor helei
*/
class TransferData extends WxBaseData
{
protected function buildData()
{
$this->retData = [
'mch_appid' => $this->appId,
'mchid' => $this->mchId,
'device_info' => $this->terminal_id,
'nonce_str' => $this->nonceStr,
'partner_trade_no' => $this->trans_no,
'openid' => $this->openid,
'check_name' => $this->check_name,
're_user_name' => $this->payer_real_name,
'amount' => $this->amount,// 此处需要处理单位为分
'desc' => $this->desc,
// $_SERVER["REMOTE_ADDR"] 获取客户端接口。此处获取php所在机器的ip 如果无法获取,则使用该ip
'spbill_create_ip' => $this->client_ip,
];
$this->retData = ArrayUtil::paraFilter($this->retData);
}
/**
* 检查相关参数是否设置
* @author helei
*/
protected function checkDataParam()
{
$openId = $this->openid;
$transNo = $this->trans_no;
$checkName = $this->check_name;
$realName = $this->payer_real_name;
$amount = $this->amount;
$clientIp = $this->client_ip;
if (empty($openId)) {
throw new PayException('商户appid下,某用户的openid 必须传入');
}
if (empty($transNo)) {
throw new PayException('商户订单号,需保持唯一性');
}
if ($checkName !== 'NO_CHECK' && empty($realName)) {
throw new PayException('请传入收款人真实姓名');
}
// 微信使用的单位位分.此处进行转化
$this->amount = bcmul($amount, 100, 0);
if (empty($amount) || $amount < 0) {
throw new PayException('转账金额错误');
}
if (empty($clientIp)) {
$this->client_ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1';
}
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-28 18:05
* @description: 微信支付相关接口的数据基类
*/
namespace Payment\Common\Weixin\Data;
use Payment\Common\BaseData;
/**
* Class BaseData
*
* @property string $appId 微信分配的公众账号ID
* @property string $mchId 微信支付分配的商户号
* @property string $nonceStr 随机字符串,不长于32位
* @property string $notifyUrl 异步通知的url
* @property string $feeType 符合ISO 4217标准的三位字母代码 默认位人民币
* @property string $timeStart 交易开始时间 格式为yyyyMMddHHmmss
* @property string $md5Key 用于加密的md5Key
* @property string $appCertPem 从apiclient_cert.p12中导出证书部分的文件,为pem格式,
* @property string $appKeyPem 从apiclient_key.pem中导出密钥部分的文件,为pem格式
* @property string $tradeType 支付类型
* @property string $terminal_id 终端设备号(门店号或收银设备ID),默认请传"WEB"
*
* @package Payment\Common\Weixin\Dataa
*/
abstract class WxBaseData extends BaseData
{
/**
* 签名算法实现 便于后期扩展微信不同的加密方式
* @param string $signStr
* @return string
*/
protected function makeSign($signStr)
{
switch ($this->signType) {
case 'MD5':
$signStr .= '&key=' . $this->md5Key;
$sign = md5($signStr);
break;
case 'HMAC-SHA256':
$sign = base64_encode(hash_hmac('sha256', $signStr, $this->md5Key));
break;
default:
$sign = '';
}
return strtoupper($sign);
}
}
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/4/26
* Time: 下午5:39
*/
namespace Payment\Common\Weixin;
use Payment\Common\PayException;
use Payment\Common\Weixin\Data\WxBaseData;
use Payment\Common\WxConfig;
use Payment\Utils\Curl;
use Payment\Utils\DataParser;
class WechatHelper extends WxBaseData
{
public function getSandboxSignKey()
{
$this->setSign();
$xml = DataParser::toXml($this->getData());
$url = WxConfig::SANDBOX_URL;
$curl = new Curl();
$responseTxt = $curl->set([
'CURLOPT_HEADER' => 0
])->post($xml)->submit($url);
if ($responseTxt['error']) {
throw new PayException('网络发生错误,请稍后再试curl返回码:' . $responseTxt['message']);
}
// 格式化为数组
$retData = DataParser::toArray($responseTxt['body']);
if ($retData['return_code'] != 'SUCCESS') {
throw new PayException('微信返回错误提示:' . $retData['return_msg']);
}
return $retData['sandbox_signkey'];
}
protected function buildData()
{
$this->retData = [
'mch_id' => $this->mchId,
'nonce_str' => $this->nonceStr,
];
}
protected function checkDataParam()
{
// TODO: Implement checkDataParam() method.
}
}
\ No newline at end of file
<?php
/**
* @author: helei
* @createTime: 2016-07-28 18:04
* @description: 微信的策略基类
*/
namespace Payment\Common\Weixin;
use Payment\Common\BaseData;
use Payment\Common\BaseStrategy;
use Payment\Common\PayException;
use Payment\Common\WxConfig;
use Payment\Utils\ArrayUtil;
use Payment\Utils\Curl;
use Payment\Utils\DataParser;
/**
* Class WxBaseStrategy
* 微信策略基类
*
* @package Payment\Common\Weixin
* anthor helei
*/
abstract class WxBaseStrategy implements BaseStrategy
{
/**
* 支付宝的配置文件
* @var WxConfig $config
*/
protected $config;
/**
* 支付数据
* @var BaseData $reqData
*/
protected $reqData;
/**
* WxBaseStrategy constructor.
* @param array $config
* @throws PayException
*/
public function __construct(array $config)
{
/* 设置内部字符编码为 UTF-8 */
mb_internal_encoding("UTF-8");
try {
$this->config = new WxConfig($config);
} catch (PayException $e) {
throw $e;
}
}
/**
* 发送完了请求
* @param string $xml
* @return mixed
* @throws PayException
* @author helei
*/
protected function sendReq($xml)
{
$url = $this->getReqUrl();
if (is_null($url)) {
throw new PayException('目前不支持该接口。请联系开发者添加');
}
if ($this->config->useSandbox) {
$url = str_ireplace('{debug}',WxConfig::SANDBOX_PRE, $url);
} else {
$url = str_ireplace('{debug}/', '', $url);
}
$responseTxt = $this->curlPost($xml, $url);
if ($responseTxt['error']) {
throw new PayException('网络发生错误,请稍后再试curl返回码:' . $responseTxt['message']);
}
// 格式化为数组
$retData = DataParser::toArray($responseTxt['body']);
if ($retData['return_code'] != 'SUCCESS') {
throw new PayException('微信返回错误提示:' . $retData['return_msg']);
}
if ($retData['result_code'] != 'SUCCESS') {
$msg = $retData['err_code_des'] ? $retData['err_code_des'] : $retData['err_msg'];
throw new PayException('微信返回错误提示:' . $msg);
}
return $retData;
}
/**
* 父类仅提供基础的post请求,子类可根据需要进行重写
* @param string $xml
* @param string $url
* @return array
* @author helei
*/
protected function curlPost($xml, $url)
{
$curl = new Curl();
return $curl->set([
'CURLOPT_HEADER' => 0
])->post($xml)->submit($url);
}
/**
* 获取需要的url 默认返回下单的url
* @author helei
* @return string|null
*/
protected function getReqUrl()
{
return WxConfig::UNIFIED_URL;
}
/**
* @param array $data
* @author helei
* @throws PayException
* @return array|string
*/
public function handle(array $data)
{
$buildClass = $this->getBuildDataClass();
try {
$this->reqData = new $buildClass($this->config, $data);
} catch (PayException $e) {
throw $e;
}
$this->reqData->setSign();
$xml = DataParser::toXml($this->reqData->getData());
$ret = $this->sendReq($xml);
// 检查返回的数据是否被篡改
$flag = $this->verifySign($ret);
if (!$flag) {
throw new PayException('微信返回数据被篡改。请检查网络是否安全!');
}
return $this->retData($ret);
}
/**
* 处理微信的返回值并返回给客户端
* @param array $ret
* @return mixed
* @author helei
*/
protected function retData(array $ret)
{
return $ret;
}
/**
* 检查微信返回的数据是否被篡改过
* @param array $retData
* @return boolean
* @author helei
*/
protected function verifySign(array $retData)
{
$retSign = $retData['sign'];
$values = ArrayUtil::removeKeys($retData, ['sign', 'sign_type']);
$values = ArrayUtil::paraFilter($values);
$values = ArrayUtil::arraySort($values);
$signStr = ArrayUtil::createLinkstring($values);
$signStr .= '&key=' . $this->config->md5Key;
switch ($this->config->signType) {
case 'MD5':
$sign = md5($signStr);
break;
case 'HMAC-SHA256':
$sign = hash_hmac('sha256', $signStr, $this->config->md5Key);
break;
default:
$sign = '';
}
return strtoupper($sign) === $retSign;
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-15 14:56
* @description: 微信配置文件
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment\Common;
use Payment\Common\Weixin\WechatHelper;
use Payment\Utils\ArrayUtil;
use Payment\Utils\StrUtil;
final class WxConfig extends ConfigInterface
{
// 微信分配的公众账号ID
public $appId;
// 微信支付分配的商户号
public $mchId;
// 随机字符串,不长于32位
public $nonceStr;
// 符合ISO 4217标准的三位字母代码
public $feeType = 'CNY';
// 交易开始时间 格式为yyyyMMddHHmmss
public $timeStart;
// 用于加密的md5Key
public $md5Key;
// 安全证书的路径
public $cacertPath;
// cert证书路径或者内容
public $appCertPem;
// key文件路径或者内容
public $appKeyPem;
// 支付类型
public $tradeType;
// 指定回调页面
public $returnUrl;
// 统一下单url
const UNIFIED_URL = 'https://api.mch.weixin.qq.com/{debug}/pay/unifiedorder';
// 提交刷卡支付url
const MICROPAY_URL = 'https://api.mch.weixin.qq.com/{debug}/pay/micropay';
// 支付查询url
const CHARGE_QUERY_URL = 'https://api.mch.weixin.qq.com/{debug}/pay/orderquery';
// 查询退款url
const REFUDN_QUERY_URL = 'https://api.mch.weixin.qq.com/{debug}/pay/refundquery';
// 企业付款的查询
const TRANS_QUERY_URL = 'https://api.mch.weixin.qq.com/{debug}/mmpaymkttransfers/gettransferinfo';
// 申请退款url
const REFUND_URL = 'https://api.mch.weixin.qq.com/{debug}/secapi/pay/refund';
// 企业付款
const TRANSFERS_URL = 'https://api.mch.weixin.qq.com/{debug}/mmpaymkttransfers/promotion/transfers';
// 关闭订单url 尚未接入
const CLOSE_URL = 'https://api.mch.weixin.qq.com/{debug}/pay/closeorder';
// 短连接转化url 尚未接入
const SHORT_URL = 'https://api.mch.weixin.qq.com/{debug}/tools/shorturl';
// 退款账户
const REFUND_UNSETTLED = 'REFUND_SOURCE_UNSETTLED_FUNDS';// 未结算资金退款(默认使用未结算资金退款)
const REFUND_RECHARGE = 'REFUND_SOURCE_RECHARGE_FUNDS';// 可用余额退款(限非当日交易订单的退款)
// 沙箱测试相关
const SANDBOX_PRE = 'sandboxnew';
const SANDBOX_URL = 'https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey';
/**
* 初始化微信配置文件
* WxConfig constructor.
* @param array $config
* @throws PayException
*/
public function __construct(array $config)
{
try {
$this->initConfig($config);
} catch (PayException $e) {
throw $e;
}
$basePath = dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'CacertFile' . DIRECTORY_SEPARATOR;
$this->cacertPath = "{$basePath}wx_cacert.pem";
}
/**
* 初始化配置文件参数
* @param array $config
* @throws PayException
*/
private function initConfig(array $config)
{
$config = ArrayUtil::paraFilter($config);
// 检查 微信分配的公众账号ID
if (key_exists('app_id', $config) && !empty($config['app_id'])) {
$this->appId = $config['app_id'];
} else {
throw new PayException('必须提供微信分配的公众账号ID');
}
// 检查 微信支付分配的商户号
if (key_exists('mch_id', $config) && !empty($config['mch_id'])) {
$this->mchId = $config['mch_id'];
} else {
throw new PayException('必须提供微信支付分配的商户号');
}
// 检查 异步通知的url
if (key_exists('notify_url', $config) && !empty($config['notify_url'])) {
$this->notifyUrl = trim($config['notify_url']);
} else {
throw new PayException('异步通知的url必须提供.');
}
// 设置交易开始时间 格式为yyyyMMddHHmmss .再次之前一定要设置时区
$startTime = time();
$this->timeStart = date('YmdHis', $startTime);
// 初始 MD5 key
if (key_exists('md5_key', $config) && !empty($config['md5_key'])) {
$this->md5Key = $config['md5_key'];
} else {
throw new PayException('MD5 Key 不能为空,再微信商户后台可查看');
}
// 设置支付的货币类型
if (key_exists('fee_type', $config) && in_array($config['fee_type'], ['CNY'])) {
$this->feeType = $config['fee_type'];
}
// 设置禁止使用的支付方式
if (key_exists('limit_pay', $config) && !empty($config['limit_pay']) && $config['limit_pay'][0] === 'no_credit') {
$this->limitPay = $config['limit_pay'][0];
}
if (key_exists('return_raw', $config)) {
$this->returnRaw = filter_var($config['return_raw'], FILTER_VALIDATE_BOOLEAN);
}
if (key_exists('redirect_url', $config)) {
$this->returnUrl = $config['redirect_url'];
}
// 以下两个文件,如果是调用资金流向接口,必须提供
if (! empty($config['app_cert_pem'])) {
$this->appCertPem = $config['app_cert_pem'];
}
if (! empty($config['app_key_pem'])) {
$this->appKeyPem = $config['app_key_pem'];
}
if (key_exists('sign_type', $config) && in_array($config['sign_type'], ['MD5', 'HMAC-SHA256'])) {
$this->signType = $config['sign_type'];
} else {
$this->signType = 'MD5';
}
// 生成随机字符串
$this->nonceStr = StrUtil::getNonceStr();
if (isset($config['use_sandbox']) && $config['use_sandbox'] === true) {
$this->useSandbox = true;// 是沙箱模式 重新获取key
$helper = new WechatHelper($this, []);
$this->md5Key = $helper->getSandboxSignKey();
} else {
$this->useSandbox = false;// 不是沙箱模式
}
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-14 17:47
* @description: 支付相关的基础配置 无法被继承
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*
* @version 2.6.1
*/
namespace Payment;
final class Config
{
const VERSION = '3.1.1-dev';
//========================= ali相关接口 =======================//
// 支付相关常量
const ALI_CHANNEL_APP = 'ali_app';// 支付宝 手机app 支付
const ALI_CHANNEL_WAP = 'ali_wap';// 支付宝 手机网页 支付
const ALI_CHANNEL_WEB = 'ali_web';// 支付宝 PC 网页支付
const ALI_CHANNEL_QR = 'ali_qr';// 支付宝 扫码支付
const ALI_CHANNEL_BAR = 'ali_bar';// 支付宝 条码支付
// 其他操作常量
const ALI_CHARGE = 'ali_charge';// 支付
const ALI_REFUND = 'ali_refund';// 退款
const ALI_RED = 'ali_red';// 红包
const ALI_TRANSFER = 'ali_transfer';// 转账
//========================= 微信相关接口 =======================//
// 支付常量
const WX_CHANNEL_APP = 'wx_app';// 微信 APP 支付
const WX_CHANNEL_PUB = 'wx_pub';// 微信 公众账号 支付
const WX_CHANNEL_QR = 'wx_qr';// 微信 扫码支付 (可以使用app的帐号,也可以用公众的帐号完成)
const WX_CHANNEL_BAR = 'wx_bar';// 微信 刷卡支付,与支付宝的条码支付对应
const WX_CHANNEL_LITE = 'wx_lite';// 微信小程序支付
const WX_CHANNEL_WAP = 'wx_wap';// 微信wap支付,针对特定用户
// 其他相关常量
const WX_CHARGE = 'wx_charge';// 支付
const WX_REFUND = 'wx_refund';// 退款
const WX_RED = 'wx_red';// 红包
const WX_TRANSFER = 'wx_transfer';// 转账
//========================= 招商相关接口 =======================//
// 支付常量
const CMB_CHANNEL_APP = 'cmb_app';// 招商 app ,实际上招商并无该概念
const CMB_CHANNEL_WAP = 'cmb_wap';// 招商h5支付,其实app支付也是使用的h5
const CMB_BIND = 'cmb_bind';// 签约API
const CMB_PUB_KEY = 'cmb_pub_key';// 查询招商公钥
const CMB_CHARGE = 'cmb_charge';// 招商支付
const CMB_REFUND = 'cmb_refund';// 招商退款
//========================= 通联支付相关接口 =======================//
// 支付常量
const TL_CHANNEL_APP = 'tl_pub';// 通联支付微信公众号h5支付
const TL_CHANNEL_LITE = 'tl_lite';// 通联支付小程序支付
const TL_BIND = 'tl_bind';// 签约API
const TL_PUB_KEY = 'tl_pub_key';// 查询通联公钥
const TL_CHARGE = 'tl_charge';// 通联支付-统一下单
const TL_REFUND = 'tl_refund';// 通联退款
const TL_CANCEL = 'tl_cancel';//通联交易撤销,只能撤销当天的交易,全额退款,实时返回退款结果
const TL_QUERY = 'tl_query';
const TL_PAY ='tl';
//========================= 金额问题设置 =======================//
const PAY_MIN_FEE = '0.01';// 支付的最小金额
const TRANS_FEE = '50000';// 转账达到这个金额,需要添加额外信息
//======================= 交易状态常量定义 ======================//
const TRADE_STATUS_SUCC = 'success';// 交易成功
const TRADE_STATUS_FAILD = 'not_pay';// 交易未完成
//======================= 账户类型 ======================//
const WECHAT_PAY = 'wechat';
const ALI_PAY = 'ali';
const CMB_PAY = 'cmb';
}
<?php
namespace Payment\Helper\Cmb;
use Payment\Common\Cmb\CmbBaseStrategy;
use Payment\Common\Cmb\Data\BindCardData;
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/4/28
* Time: 下午12:01
*/
class BindCardHelper extends CmbBaseStrategy
{
public function getBuildDataClass()
{
$this->config->getewayUrl = 'https://mobile.cmbchina.com/mobilehtml/DebitCard/M_NetPay/OneNetRegister/NP_BindCard.aspx';
if ($this->config->useSandbox) {// 测试
$this->config->getewayUrl = 'http://121.15.180.66:801/mobilehtml/DebitCard/M_NetPay/OneNetRegister/NP_BindCard.aspx';
}
return BindCardData::class;
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/4/29
* Time: 上午9:50
*/
namespace Payment\Helper\Cmb;
use Payment\Common\Cmb\CmbBaseStrategy;
use Payment\Common\Cmb\Data\PubKeyData;
use Payment\Common\CmbConfig;
use Payment\Config;
/**
* 招商公钥获取
* Class PubKeyHelper
* @package Payment\Helper\Cmb
*/
class PubKeyHelper extends CmbBaseStrategy
{
public function getBuildDataClass()
{
$this->config->getewayUrl = 'https://b2b.cmbchina.com/CmbBank_B2B/UI/NetPay/DoBusiness.ashx';
if ($this->config->useSandbox) {// 测试
$this->config->getewayUrl = 'http://121.15.180.72/CmbBank_B2B/UI/NetPay/DoBusiness.ashx';
}
return PubKeyData::class;
}
protected function retData(array $ret)
{
$json = json_encode($ret, JSON_UNESCAPED_UNICODE);
$postData = CmbConfig::REQ_FILED_NAME . '=' . $json;
$retData = $this->sendReq($postData);
if ($this->config->returnRaw) {
$retData['channel'] = Config::CMB_PUB_KEY;
return $retData;
}
// 正确情况
$rData = [
'is_success' => 'T',
'response' => [
'pub_key' => $retData['fbPubKey'],
'channel' => Config::CMB_PUB_KEY,
'time' => date('Y-m-d H:i:s', strtotime($retData['dateTime'])),// Y-m-d H:i:s,
],
];
return $rData;
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/4/28
* Time: 上午10:43
*/
namespace Payment;
use Payment\Common\BaseStrategy;
use Payment\Common\PayException;
use Payment\Helper\Cmb\BindCardHelper;
use Payment\Helper\Cmb\PubKeyHelper;
/**
* 用于完成一些辅助操作,例如: 招商绑卡 操作
* Class HelperContext
* @package Payment
*/
class HelperContext
{
/**
* 转款渠道
* @var BaseStrategy
*/
protected $helper;
/**
* 设置对应的退款渠道
* @param string $way 对应的方式渠道
* - @see Config
*
* @param array $config 配置文件
* @throws PayException
* @author helei
*/
public function initHelper($way, array $config)
{
try {
switch ($way) {
case Config::CMB_BIND:
$this->helper = new BindCardHelper($config);
break;
case Config::CMB_PUB_KEY:
$this->helper = new PubKeyHelper($config);
break;
default:
throw new PayException('当前仅支持:CMB_BIND CMB_PUB_KEY 操作');
}
} catch (PayException $e) {
throw $e;
}
}
/**
* 获取帮助操作
*
* @param array $data
*
* @return array
* @throws PayException
* @author helei
*/
public function helper(array $data)
{
if (! $this->helper instanceof BaseStrategy) {
throw new PayException('请检查初始化是否正确');
}
try {
return $this->helper->handle($data);
} catch (PayException $e) {
throw $e;
}
}
}
\ No newline at end of file
<?php
/**
* @author: helei
* @createTime: 2016-07-20 16:21
* @description: 支付宝回调通知
*
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment\Notify;
use Payment\Common\AliConfig;
use Payment\Common\PayException;
use Payment\Config;
use Payment\Utils\ArrayUtil;
use Payment\Utils\Rsa2Encrypt;
use Payment\Utils\RsaEncrypt;
class AliNotify extends NotifyStrategy
{
/**
* AliNotify constructor.
* @param array $config
* @throws PayException
*/
public function __construct(array $config)
{
parent::__construct($config);
try {
$this->config = new AliConfig($config);
} catch (PayException $e) {
throw $e;
}
}
protected function getOldAliPublicKey()
{
$filePath = dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR .'CacertFile/old_alipay_public_key.pem';
return @file_get_contents($filePath);
}
/**
* 获取移除通知的数据 并进行简单处理(如:格式化为数组)
*
* 如果获取数据失败,返回false
*
* @return array|boolean
* @author helei
*/
public function getNotifyData()
{
$data = empty($_POST) ? $_GET : $_POST;
if (empty($data) || ! is_array($data)) {
return false;
}
return $data;
}
/**
* 检查异步通知的数据是否合法
*
* 如果检查失败,返回false
*
* @param array $data 由 $this->getNotifyData() 返回的数据
* @return boolean
* @author helei
*/
public function checkNotifyData(array $data)
{
$status = $this->getTradeStatus($data['trade_status']);
if ($status !== Config::TRADE_STATUS_SUCC) {
// 如果不是交易成功状态,直接返回错误,
return false;
}
// 主要是为了即时到账的签名
if (! isset($data['version'])) {
$this->config->rsaAliPubKey = $this->getOldAliPublicKey();
}
// 检查签名
$flag = $this->verifySign($data);
return $flag;
}
/**
* 向客户端返回必要的数据
* @param array $data 回调机构返回的回调通知数据
* @return array|false
* @author helei
*/
protected function getRetData(array $data)
{
if ($this->config->returnRaw) {
$data['channel'] = Config::ALI_CHARGE;
return $data;
}
if (! isset($data['version'])) {// 即时到账
$retData = [
'order_no' => $data['out_trade_no'],
'subject' => $data['subject'],
'transaction_id' => $data['trade_no'],
'trade_state' => $this->getTradeStatus($data['trade_status']),
'trade_create_time' => $data['gmt_create'],// 交易创建时间
'pay_time' => $data['gmt_payment'],// 交易付款时间
'seller_id' => $data['seller_id'],
'seller_email' => $data['seller_email'],
'buyer_id' => $data['buyer_id'],
'amount' => $data['total_fee'],
'channel' => Config::ALI_CHARGE,
'body' => $data['body'],
'discount' => $data['discount'],
'return_param' => $data['extra_common_param'],
'notify_time' => $data['notify_time'],
'notify_type' => $data['notify_type'],
];
} else {
$retData = [
'amount' => $data['total_amount'],
'buyer_id' => $data['buyer_id'],
'transaction_id' => $data['trade_no'],
'body' => $data['body'],
'notify_time' => $data['notify_time'],
'subject' => $data['subject'],
'buyer_account' => $data['buyer_logon_id'],
'auth_app_id' => $data['auth_app_id'],
'notify_type' => $data['notify_type'],
'invoice_amount' => $data['invoice_amount'],
'order_no' => $data['out_trade_no'],
'trade_state' => $this->getTradeStatus($data['trade_status']),
'pay_time' => $data['gmt_payment'],// 交易付款时间
'point_amount' => $data['point_amount'],// 使用集分宝支付的金额
'trade_create_time' => $data['gmt_create'],// 交易创建时间
'pay_amount' => $data['buyer_pay_amount'],// 用户在交易中支付的金额
'receipt_amount' => $data['receipt_amount'],// 商家在交易中实际收到的款项,单位为元
'fund_bill_list' => $data['fund_bill_list'],// 支付成功的各个渠道金额信息
'app_id' => $data['app_id'],
'seller_id' => $data['seller_id'],
'seller_email' => $data['seller_email'],
'channel' => Config::ALI_CHARGE,
];
}
// 检查是否存在用户自定义参数
if (isset($data['passback_params']) && ! empty($data['passback_params'])) {
$retData['return_param'] = $data['passback_params'];
}
return $retData;
}
/**
* 支付宝,成功返回 ‘success’ 失败,返回 ‘fail’
* @param boolean $flag 每次返回的bool值
* @param string $msg 错误原因 后期考虑记录日志
* @return string
* @author helei
*/
protected function replyNotify($flag, $msg = '')
{
if ($flag) {
return 'success';
} else {
return 'fail';
}
}
/**
* 返回统一的交易状态
* @param $status
* @return string
* @author helei
*/
protected function getTradeStatus($status)
{
if (in_array($status, ['TRADE_SUCCESS', 'TRADE_FINISHED'])) {
return Config::TRADE_STATUS_SUCC;
} else {
return Config::TRADE_STATUS_FAILD;
}
}
/**
* 检查支付宝数据 签名是否被篡改
* @param array $data
* @return boolean
* @author helei
*/
protected function verifySign(array $data)
{
$signType = strtoupper($data['sign_type']);
$sign = $data['sign'];
// 1. 剔除sign与sign_type参数
$values = ArrayUtil::removeKeys($data, ['sign', 'sign_type']);
// 2. 移除数组中的空值
$values = ArrayUtil::paraFilter($values);
// 3. 对待签名参数数组排序
$values = ArrayUtil::arraySort($values);
// 4. 将排序后的参数与其对应值,组合成“参数=参数值”的格式,用&字符连接起来
$preStr = ArrayUtil::createLinkstring($values);
if ($signType === 'RSA') {// 使用rsa方式
$rsa = new RsaEncrypt($this->config->rsaAliPubKey);
return $rsa->rsaVerify($preStr, $sign);
} elseif ($signType === 'RSA2') {
$rsa = new Rsa2Encrypt($this->config->rsaAliPubKey);
return $rsa->rsaVerify($preStr, $sign);
} else {
return false;
}
}
}
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/4/29
* Time: 上午10:36
*/
namespace Payment\Notify;
use Payment\Common\CmbConfig;
use Payment\Common\PayException;
use Payment\Config;
use Payment\Utils\ArrayUtil;
use Payment\Utils\RsaEncrypt;
/**
* 招商回调处理
* Class CmbNotify
* @package Payment\Notify
*/
class CmbNotify extends NotifyStrategy
{
/**
* CmbNotify constructor.
* @param array $config
* @throws PayException
*/
public function __construct(array $config)
{
parent::__construct($config);
try {
$this->config = new CmbConfig($config);
} catch (PayException $e) {
throw $e;
}
}
public function getNotifyData()
{
$data = empty($_POST) ? $_GET : $_POST;
if (empty($data) || ! is_array($data)) {
return false;
}
$retData = json_decode($data[CmbConfig::REQ_FILED_NAME], true);
return $retData;
}
/**
* 对返回信息验证签名
* @param array $data
* @return boolean
*/
public function checkNotifyData(array $data)
{
$signType = strtoupper($data['signType']);
$sign = $data['sign'];
// 1. 对待签名参数数组排序
$values = ArrayUtil::arraySort($data['noticeData']);
// 2. 将排序后的参数与其对应值,组合成“参数=参数值”的格式,用&字符连接起来
$preStr = ArrayUtil::createLinkstring($values);
if ($signType === 'RSA') {// 使用rsa方式
$rsa = new RsaEncrypt($this->config->rsaPubKey);
return $rsa->rsaVerify($preStr, $sign);
} else {
return false;
}
}
/**
* 向客户端返回相关数据
* @param array $data
* @return array
*/
protected function getRetData(array $data)
{
$noticeData = $data['noticeData'];
$noticeType = $noticeData['noticeType'];
if ($noticeType === CmbConfig::NOTICE_PAY) {
$channel = Config::CMB_CHARGE;
} elseif ($noticeType === CmbConfig::NOTICE_SIGN) {
$channel = Config::CMB_BIND;
} else {
$channel = 'other';
}
if (!$this->config->returnRaw) {
$data['channel'] = $channel;
return $data;
} elseif ($noticeType === CmbConfig::NOTICE_PAY) {
$retData = [
'amount' => $noticeData['amount'],
'channel' => $channel,
'date' => $noticeData['date'],
'order_no' => $noticeData['orderNo'],
'trade_state' => Config::TRADE_STATUS_SUCC,// 招商的订单只会成功
'transaction_id' => $noticeData['bankSerialNo'],
'time_end' => date('Y-m-d H:i:s', strtotime($noticeData['dateTime'])),// Y-m-d H:i:s
'discount_fee' => $noticeData['discountAmount'],// 优惠金额,格式:xxxx.xx 无优惠时返回0.00
'card_type' => $noticeData['cardType'],// 卡类型,02:一卡通;03:信用卡;07:他行卡
'return_param' => $noticeData['merchantPara'],
'discount_flag' => $noticeData['discountFlag'],
'notice_no' => $noticeData['noticeSerialNo'],
];
} elseif ($noticeType === CmbConfig::NOTICE_SIGN) {
$retData = [
'user_id' => $noticeData['userID'],
'no_pwd_pay' => $noticeData['noPwdPay'],
'notice_no' => $noticeData['noticeSerialNo'],
'agr_no' => $noticeData['agrNo'],
'rsp_msg' => $noticeData['rspMsg'],
'return_param' => $noticeData['noticePara'],
'user_pid_hash' => $noticeData['userPidHash'],
'user_pid_type' => $noticeData['userPidType'],
'channel' => $channel,
];
} else {
$retData = $noticeData;
}
return $retData;
}
/**
* 招商只需要返回状态码
* @param bool $flag
* @param string $msg
* @return string
*/
protected function replyNotify($flag, $msg = 'OK')
{
if ($flag) {
header('HTTP/1.1 200 OK');
return $msg;
} else {
header('HTTP/1.1 503 Service Unavailable');
return $msg;
}
}
}
\ No newline at end of file
<?php
/**
* @author: helei
* @createTime: 2016-07-14 17:51
* @description: 支付回调的策略接口
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment\Notify;
use Payment\Common\ConfigInterface;
abstract class NotifyStrategy
{
/**
* 配置信息
* @var ConfigInterface $config
*/
protected $config;
/**
* NotifyStrategy constructor.
* @param array $config
*/
public function __construct(array $config)
{
/* 设置内部字符编码为 UTF-8 */
mb_internal_encoding("UTF-8");
}
/**
* 主要任务,验证返回的数据是否正确
* @param PayNotifyInterface $notify
* @return mixed
* @author helei
*/
final public function handle(PayNotifyInterface $notify)
{
// 获取异步通知的数据
$notifyData = $this->getNotifyData();
if ($notifyData === false) {// 失败,就返回错误
return $this->replyNotify(false, '获取通知数据失败');
}
// 检查异步通知返回的数据是否有误
$checkRet = $this->checkNotifyData($notifyData);
if ($checkRet === false) {// 失败,就返回错误
return $this->replyNotify(false, '返回数据验签失败,可能数据被篡改');
}
// 回调商户的业务逻辑
$flag = $this->callback($notify, $notifyData);
if ($flag) {
$msg = 'OK';
} else {
$msg = '商户逻辑调用出错';
}
// 返回响应值
return $this->replyNotify($flag, $msg);
}
/**
* 回调商户的业务逻辑,根据返回的true 或者 false 向第三方返回数据
* @param PayNotifyInterface $notify
* @param array $notifyData
*
* @return boolean
* @author helei
*/
protected function callback(PayNotifyInterface $notify, array $notifyData)
{
$data = $this->getRetData($notifyData);
if ($data === false) {
return false;
}
return $notify->notifyProcess($data);
}
/**
* 获取移除通知的数据 并进行简单处理(如:格式化为数组)
*
* 如果获取数据失败,返回false
*
* @return array|false
* @author helei
*/
abstract public function getNotifyData();
/**
* 检查异步通知的数据是否合法
*
* 如果检查失败,返回false
*
* @param array $data 由 $this->getNotifyData() 返回的数据
* @return boolean
* @author helei
*/
abstract public function checkNotifyData(array $data);
/**
* 向客户端返回必要的数据
* @param array $data 回调机构返回的回调通知数据
* @return array|false
* @author helei
*/
abstract protected function getRetData(array $data);
/**
* 根据返回结果,回答支付机构。是否回调通知成功
* @param boolean $flag 每次返回的bool值
* @param string $msg 通知信息,错误原因
* @return mixed
* @author helei
*/
abstract protected function replyNotify($flag, $msg = 'OK');
}
<?php
/**
* @author: helei
* @createTime: 2016-07-20 16:12
* @description: 提供给客户端实现的 支付异步回调 接口
*
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment\Notify;
interface PayNotifyInterface
{
/**
* 异步回调检验完成后,回调客户端的业务逻辑
* 业务逻辑处理,必须实现该类。
*
* @param array $data 返回的数据
*
* @return boolean
* @author helei
*/
public function notifyProcess(array $data);
}
<?php
namespace Payment\Notify;
use Payment\Common\PayException;
use Payment\Common\TLConfig;
use Payment\Config;
use Payment\Utils\ArrayUtil;
use Payment\Utils\StrUtil;
/**
* Class TLNotify
* 微信回调处理
* @package Payment\Notify
* anthor yeran
*/
class TLNotify extends NotifyStrategy
{
/**
* TLNotify constructor.
* @param array $config
* @throws PayException
*/
public function __construct(array $config)
{
parent::__construct($config);
try {
$this->config = new TLConfig($config);
} catch (PayException $e) {
throw $e;
}
}
/**
* 获取返回的异步通知数据
* @return array|bool
* @author yeran
*/
public function getNotifyData()
{
// php://input 带来的内存压力更小
$data = @file_get_contents('php://input');// 等同于微信提供的:$GLOBALS['HTTP_RAW_POST_DATA']
// 将xml数据格式化为数组
$arrData = json_decode($data,true);
if (empty($arrData)) {
return false;
}
// 移除值中的空格 xml转化为数组时,CDATA 数据会被带入额外的空格。
$arrData = ArrayUtil::paraFilter($arrData);
return $arrData;
}
/**
* 检查异步通知的数据是否正确
* @param array $data
*
* @author yeran
* @return boolean
*/
public function checkNotifyData(array $data)
{
// 检查返回数据签名是否正确
return $this->verifySign($data);
}
/**
* 检查微信返回的数据是否被篡改过
* @param array $retData
* @return boolean
* @author yeran
*/
protected function verifySign(array $retData)
{
$retSign = $retData['sign'];
$values = ArrayUtil::removeKeys($retData, ['sign', 'sign_type']);
$values = ArrayUtil::paraFilter($values);
$values = ArrayUtil::arraySort($values);
$signStr = ArrayUtil::createLinkstring($values);
$signStr .= '&key=' . $this->config->md5Key;
$sign = md5(StrUtil::getBytes($signStr));
return strtoupper($sign) === $retSign;
}
/**
*
* 封装回调函数需要的数据格式
*
* @param array $data
*
* @return array
* @author yeran
*/
protected function getRetData(array $data)
{
if ($this->config->returnRaw) {
$data['channel'] = Config::TL_CHARGE;
return $data;
}
return $data;
}
/**
* 处理完后返回的数据格式
* @param bool $flag
* @param string $msg 通知信息,错误原因
* @author yeran
* @return string
*/
protected function replyNotify($flag, $msg = 'OK')
{
// 默认为成功
$return_code ='SUCCESS';
if (! $flag) {
$return_code ='FAIL';
}
return $return_code;
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-20 16:46
* @description:
*/
namespace Payment\Notify;
use Payment\Common\PayException;
use Payment\Common\WxConfig;
use Payment\Config;
use Payment\Utils\ArrayUtil;
use Payment\Utils\DataParser;
/**
* Class WxNotify
* 微信回调处理
* @package Payment\Notify
* anthor helei
*/
class WxNotify extends NotifyStrategy
{
/**
* WxNotify constructor.
* @param array $config
* @throws PayException
*/
public function __construct(array $config)
{
parent::__construct($config);
try {
$this->config = new WxConfig($config);
} catch (PayException $e) {
throw $e;
}
}
/**
* 获取微信返回的异步通知数据
* @return array|bool
* @author helei
*/
public function getNotifyData()
{
// php://input 带来的内存压力更小
$data = @file_get_contents('php://input');// 等同于微信提供的:$GLOBALS['HTTP_RAW_POST_DATA']
// 将xml数据格式化为数组
$arrData = DataParser::toArray($data);
if (empty($arrData)) {
return false;
}
// 移除值中的空格 xml转化为数组时,CDATA 数据会被带入额外的空格。
$arrData = ArrayUtil::paraFilter($arrData);
return $arrData;
}
/**
* 检查微信异步通知的数据是否正确
* @param array $data
*
* @author helei
* @return boolean
*/
public function checkNotifyData(array $data)
{
if ($data['return_code'] != 'SUCCESS' || $data['result_code'] != 'SUCCESS') {
// $arrData['return_msg'] 返回信息,如非空,为错误原因
// $data['result_code'] != 'SUCCESS' 表示业务失败
return false;
}
// 检查返回数据签名是否正确
return $this->verifySign($data);
}
/**
* 检查微信返回的数据是否被篡改过
* @param array $retData
* @return boolean
* @author helei
*/
protected function verifySign(array $retData)
{
$retSign = $retData['sign'];
$values = ArrayUtil::removeKeys($retData, ['sign', 'sign_type']);
$values = ArrayUtil::paraFilter($values);
$values = ArrayUtil::arraySort($values);
$signStr = ArrayUtil::createLinkstring($values);
$signStr .= '&key=' . $this->config->md5Key;
switch ($this->config->signType) {
case 'MD5':
$sign = md5($signStr);
break;
case 'HMAC-SHA256':
$sign = hash_hmac('sha256', $signStr, $this->config->md5Key);
break;
default:
$sign = '';
}
return strtoupper($sign) === $retSign;
}
/**
* 获取向客户端返回的数据
* @param array $data
*
* @return array
* @author helei
*/
protected function getRetData(array $data)
{
if ($this->config->returnRaw) {
$data['channel'] = Config::WX_CHARGE;
return $data;
}
// 将金额处理为元
$totalFee = bcdiv($data['total_fee'], 100, 2);
$cashFee = bcdiv($data['cash_fee'], 100, 2);
$retData = [
'bank_type' => $data['bank_type'],
'cash_fee' => $cashFee,
'device_info' => $data['device_info'],
'fee_type' => $data['fee_type'],
'is_subscribe' => $data['is_subscribe'],
'buyer_id' => $data['openid'],
'order_no' => $data['out_trade_no'],
'pay_time' => date('Y-m-d H:i:s', strtotime($data['time_end'])),// 支付完成时间
'amount' => $totalFee,
'trade_type' => $data['trade_type'],
'transaction_id' => $data['transaction_id'],
'trade_state' => strtolower($data['return_code']),
'channel' => Config::WX_CHARGE,
];
// 检查是否存在用户自定义参数
if (isset($data['attach']) && ! empty($data['attach'])) {
$retData['return_param'] = $data['attach'];
}
return $retData;
}
/**
* 处理完后返回的数据格式
* @param bool $flag
* @param string $msg 通知信息,错误原因
* @author helei
* @return string
*/
protected function replyNotify($flag, $msg = 'OK')
{
// 默认为成功
$result = [
'return_code' => 'SUCCESS',
'return_msg' => 'OK',
];
if (! $flag) {
// 失败
$result = [
'return_code' => 'FAIL',
'return_msg' => $msg,
];
}
return DataParser::toXml($result);
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-14 17:42
* @description: 暴露给客户端调用的接口
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment;
use Payment\Notify\AliNotify;
use Payment\Notify\CmbNotify;
use Payment\Notify\NotifyStrategy;
use Payment\Notify\PayNotifyInterface;
use Payment\Notify\TLNotify;
use Payment\Notify\WxNotify;
use Payment\Common\PayException;
class NotifyContext
{
/**
* 支付的渠道
* @var NotifyStrategy
*/
protected $notify;
/**
* 设置对应的通知渠道
* @param string $channel 通知渠道
* - @see Config
*
* @param array $config 配置文件
* @throws PayException
* @author helei
*/
public function initNotify($channel, array $config)
{
try {
switch ($channel) {
case Config::ALI_CHARGE:
$this->notify = new AliNotify($config);
break;
case Config::WX_CHARGE:
$this->notify = new WxNotify($config);
break;
case Config::CMB_CHARGE:
$this->notify = new CmbNotify($config);
break;
case Config::TL_CHARGE:
$this->notify = new TLNotify($config);
break;
default:
throw new PayException('当前仅支持:ALI_CHARGE WX_CHARGE CMB_CHARGE 常量');
}
} catch (PayException $e) {
throw $e;
}
}
/**
* 返回异步通知的数据
* @return array|false
*/
public function getNotifyData()
{
return $this->notify->getNotifyData();
}
/**
* 通过环境类调用支付异步通知
*
* @param PayNotifyInterface $notify
* @return array
* @throws PayException
* @author helei
*/
public function notify(PayNotifyInterface $notify)
{
if (! $this->notify instanceof NotifyStrategy) {
throw new PayException('请检查初始化是否正确');
}
return $this->notify->handle($notify);
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-28 17:23
* @description: 支付宝订单查询接口
*/
namespace Payment\Query\Ali;
use Payment\Common\Ali\AliBaseStrategy;
use Payment\Common\Ali\Data\Query\ChargeQueryData;
use Payment\Common\AliConfig;
use Payment\Common\PayException;
use Payment\Config;
class AliChargeQuery extends AliBaseStrategy
{
public function getBuildDataClass()
{
$this->config->method = AliConfig::TRADE_QUERY_METHOD;
return ChargeQueryData::class;
}
protected function retData(array $data)
{
$url = parent::retData($data); // TODO: Change the autogenerated stub
try {
$ret = $this->sendReq($url);
} catch (PayException $e) {
throw $e;
}
if ($this->config->returnRaw) {
$ret['channel'] = Config::ALI_CHARGE;
return $ret;
}
return $this->createBackData($ret);
}
/**
* 处理支付宝返回的数据,统一处理后返回
* @param array $data 支付宝返回的数据
* @return array
* @author helei
*/
protected function createBackData(array $data)
{
// 新版本
if ($data['code'] !== '10000') {
return $retData = [
'is_success' => 'F',
'error' => $data['sub_msg']
];
}
// 正确情况
$retData = [
'is_success' => 'T',
'response' => [
'amount' => $data['total_amount'],
'channel' => Config::ALI_CHARGE,
'order_no' => $data['out_trade_no'],
'buyer_id' => $data['buyer_user_id'],
'trade_state' => $this->getTradeStatus($data['trade_status']),
'transaction_id' => $data['trade_no'],
'time_end' => $data['send_pay_date'],
'receipt_amount' => $data['receipt_amount'],// 实收金额,单位为元,两位小数。
'pay_amount' => $data['buyer_pay_amount'],// 买家实付金额,单位为元
'point_amount' => $data['point_amount'],// 使用集分宝支付的金额
'invoice_amount' => $data['invoice_amount'],// 交易中用户支付的可开具发票的金额,单位为元,两位小数
'fund_bill_list' => empty($data['fund_bill_list']) ? '' : $data['fund_bill_list'],// 支付成功的各个渠道金额信息
'logon_id' => $data['buyer_logon_id'],// 买家支付宝账号
],
];
return $retData;
}
}
<?php
/**
* @author: helei
* @createTime: 2016-12-31 17:55
* @description:
*/
namespace Payment\Query\Ali;
use Payment\Common\Ali\AliBaseStrategy;
use Payment\Common\Ali\Data\Query\RefundQueryData;
use Payment\Common\AliConfig;
use Payment\Common\PayException;
use Payment\Config;
/**
*
* 支付宝退款订单查询
* Class AliRefundQuery
* @package Payment\Query
* anthor helei
*/
class AliRefundQuery extends AliBaseStrategy
{
public function getBuildDataClass()
{
$this->config->method = AliConfig::REFUND_QUERY_METHOD;
return RefundQueryData::class;
}
protected function retData(array $data)
{
$url = parent::retData($data); // TODO: Change the autogenerated stub
try {
$ret = $this->sendReq($url);
} catch (PayException $e) {
throw $e;
}
if ($this->config->returnRaw) {
$ret['channel'] = Config::ALI_REFUND;
return $ret;
}
return $this->createBackData($ret);
}
/**
* 返回数据给客户端 未完成,目前没有数据提供
* @param array $data
* @return array
* @author helei
*/
protected function createBackData(array $data)
{
// 新版本
if ($data['code'] !== '10000') {
return $retData = [
'is_success' => 'F',
'error' => $data['sub_msg'],
'channel' => Config::ALI_REFUND,
];
}
// 这里有个诡异情况。查询数据全部为空。仅返回一个成功的标记
if (empty($data['out_trade_no'])) {
return [
'is_success' => 'T',
'msg' => strtolower($data['msg']),
'channel' => Config::ALI_REFUND,
];
}
$retData = [
'is_success' => 'T',
'response' => [
'amount' => $data['total_amount'],// 订单总金额
'refund_amount' => $data['refund_amount'],// 退款金额
'order_no' => $data['out_trade_no'],// 商户订单号
'refund_no' => $data['out_request_no'],// 本笔退款对应的退款请求号
'transaction_id' => $data['trade_no'],// 微信订单号
'reason' => $data['refund_reason'],// 退款理由
'channel' => Config::ALI_REFUND,
]
];
return $retData;
}
}
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/3/7
* Time: 下午3:58
*/
namespace Payment\Query\Ali;
use Payment\Common\Ali\AliBaseStrategy;
use Payment\Common\Ali\Data\Query\TransferQueryData;
use Payment\Common\AliConfig;
use Payment\Common\PayException;
use Payment\Config;
/**
* 查询转账订单的情况
* Class AliTransferQuery
* @package Payment\Query\Ali
*/
class AliTransferQuery extends AliBaseStrategy
{
public function getBuildDataClass()
{
$this->config->method = AliConfig::TRANS_QUERY_METHOD;
return TransferQueryData::class;
}
protected function retData(array $data)
{
$url = parent::retData($data); // TODO: Change the autogenerated stub
try {
$ret = $this->sendReq($url);
} catch (PayException $e) {
throw $e;
}
if ($this->config->returnRaw) {
$ret['channel'] = Config::ALI_TRANSFER;
return $ret;
}
return $this->createBackData($ret);
}
/**
* 返回数据给客户端 未完成,目前没有数据提供
* @param array $data
* @return array
* @author helei
*/
protected function createBackData(array $data)
{
// 新版本
if ($data['code'] !== '10000') {
return $retData = [
'is_success' => 'F',
'error' => $data['sub_msg'],
'channel' => Config::ALI_TRANSFER,
];
}
$retData = [
'is_success' => 'T',
'response' => [
'transaction_id' => $data['order_id'],// 支付宝订单号
'amount' => $data['order_fee'],// 转账金额
'trans_no' => $data['out_biz_no'],// 商户转账订单号
'pay_date' => $data['pay_date'],// 转账日期
'status' => strtolower($data['status']),
'fail_reason' => $data['fail_reason'],// 查询到的订单状态为FAIL失败或REFUND退票时,返回具体的原因。
'arrival_time_end' => $data['arrival_time_end'],// 预计到账时间,转账到银行卡专用,格式为yyyy-MM-dd HH:mm:ss
'channel' => Config::ALI_TRANSFER,
]
];
return $retData;
}
}
\ No newline at end of file
<?php
namespace Payment\Query\Cmb;
use Payment\Common\Cmb\CmbBaseStrategy;
use Payment\Common\Cmb\Data\Query\ChargeQueryData;
use Payment\Common\CmbConfig;
use Payment\Config;
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/4/28
* Time: 下午12:51
*/
class CmbChargeQuery extends CmbBaseStrategy
{
public function getBuildDataClass()
{
$this->config->getewayUrl = 'https://payment.ebank.cmbchina.com/NetPayment/BaseHttp.dll?QuerySingleOrder';
if ($this->config->useSandbox) {// 测试
$this->config->getewayUrl = 'http://121.15.180.66:801/NetPayment_dl/BaseHttp.dll?QuerySingleOrder';
}
return ChargeQueryData::class;
}
protected function retData(array $ret)
{
$json = json_encode($ret, JSON_UNESCAPED_UNICODE);
$postData = CmbConfig::REQ_FILED_NAME . '=' . $json;
$ret = $this->sendReq($postData);
if ($this->config->returnRaw) {
$ret['channel'] = Config::CMB_CHARGE;
return $ret;
}
// 正确情况
$retData = [
'is_success' => 'T',
'response' => [
'amount' => $ret['orderAmount'],
'channel' => Config::CMB_CHARGE,
'order_no' => $ret['orderNo'],
'trade_state' => $this->getTradeStatus($ret['orderStatus']),
'transaction_id' => $ret['bankSerialNo'],
'time_end' => date('Y-m-d H:i:s', strtotime($ret['settleDate'] . $ret['settleTime'])),// Y-m-d H:i:s
'fee' => $ret['fee'],// 招商抽取的金额
'time_start' => date('Y-m-d H:i:s', strtotime($ret['bankDate'] . $ret['bankTime'])),
'discount_fee' => $ret['discountAmount'],// 优惠金额,格式:xxxx.xx 无优惠时返回0.00
'card_type' => $ret['cardType'],// 卡类型,02:一卡通;03:信用卡;07:他行卡
'return_param' => $ret['merchantPara'],
],
];
return $retData;
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/4/28
* Time: 下午5:22
*/
namespace Payment\Query\Cmb;
use Payment\Common\Cmb\CmbBaseStrategy;
use Payment\Common\Cmb\Data\Query\RefundQueryData;
use Payment\Common\CmbConfig;
use Payment\Config;
class CmbRefundQuery extends CmbBaseStrategy
{
public function getBuildDataClass()
{
$this->config->getewayUrl = 'https://payment.ebank.cmbchina.com/NetPayment/BaseHttp.dll?QuerySettledRefund';
if ($this->config->useSandbox) {// 测试
$this->config->getewayUrl = 'http://121.15.180.66:801/netpayment_dl/BaseHttp.dll?QuerySettledRefund';
}
return RefundQueryData::class;
}
protected function retData(array $ret)
{
$json = json_encode($ret, JSON_UNESCAPED_UNICODE);
$postData = CmbConfig::REQ_FILED_NAME . '=' . $json;
$retData = $this->sendReq($postData);
if ($this->config->returnRaw) {
$retData['channel'] = Config::CMB_REFUND;
return $retData;
}
$list = $retData['dataList'];
$list = str_replace('`', '', $list);
$list = explode(PHP_EOL, $list);
$header = array_shift($list);
$header = explode(',', $header);
foreach ($list as $key => $item) {
$item = explode(',', $item);
$list[$key] = array_combine($header, $item);
}
// 正确情况
$retData = [
'is_success' => 'T',
'response' => [
'channel' => Config::CMB_REFUND,
'refund_data' => $list,
],
];
return $retData;
}
}
\ No newline at end of file
<?php
/**
* Created by IntelliJ IDEA.
* User: yeran
* Date: 2018/4/23
* Time: 下午7:07
*/
namespace Payment\Query\TLpay;
use Payment\Common\BaseData;
use Payment\Common\TLConfig;
use Payment\Common\TLpay\Data\Query\TLQueryData;
use Payment\Common\TLpay\TLBaseStrategy;
class TLQuery extends TLBaseStrategy {
/**
* 获取支付对应的数据完成类
* @return BaseData
* @author helei
*/
public function getBuildDataClass()
{
// TODO: Implement getBuildDataClass() method.
return TLQueryData::class;
}
/**
* 返回查询的url
* @return string
* @author helei
*/
protected function getReqUrl()
{
return TLConfig::CHARGE_QUERY_URL;
}
public function retData(array $ret)
{
//cusid 商户号 平台分配的商户号 否 15
//appid 应用ID 平台分配的APPID 否 8
//trxid 交易单号 平台的交易流水号 否 20
//chnltrxid 支付渠道交易单号 如支付宝,微信平台的交易单号 是 50
//reqsn 商户订单号 商户的交易订单号 否 32
//trxcode 交易类型 交易类型 否 8 见3.2
//trxamt 交易金额 单位为分 否 16
//trxstatus 交易状态 交易的状态 是 4 如果trxstatus为空,则交易正在处理中,尚未完成
//acct 支付平台用户标识 "JS支付时使用 微信支付-用户的微信openid 支付宝支付-用户user_id " 是 32 如果为空,则默认填000000
//fintime 交易完成时间 yyyyMMddHHmmss 是 14
//randomstr 随机字符串 随机生成的字符串 否 32
//errmsg 错误原因 失败的原因说明 是 100
//sign 签名 否 32 详见1.5
return $ret;
}
}
\ No newline at end of file
<?php
/**
* @author: helei
* @createTime: 2016-07-28 17:25
* @description:
*/
namespace Payment\Query\Wx;
use Payment\Common\Weixin\Data\Query\ChargeQueryData;
use Payment\Common\Weixin\WxBaseStrategy;
use Payment\Common\WxConfig;
use Payment\Config;
use Payment\Utils\ArrayUtil;
class WxChargeQuery extends WxBaseStrategy
{
/**
* 返回查询订单的数据
* @author helei
*/
public function getBuildDataClass()
{
return ChargeQueryData::class;
}
/**
* 返回微信查询的url
* @return string
* @author helei
*/
protected function getReqUrl()
{
return WxConfig::CHARGE_QUERY_URL;
}
/**
* 处理通知的返回数据
* @param array $data
* @return mixed
* @author helei
*/
protected function retData(array $data)
{
if ($this->config->returnRaw) {
$data['channel'] = Config::WX_CHARGE;
return $data;
}
// 请求失败,可能是网络
if ($data['return_code'] != 'SUCCESS') {
return $retData = [
'is_success' => 'F',
'error' => $data['return_msg'],
'channel' => Config::WX_CHARGE,// 支付查询
];
}
// 业务失败
if ($data['result_code'] != 'SUCCESS') {
return $retData = [
'is_success' => 'F',
'error' => $data['err_code_des'],
'channel' => Config::WX_CHARGE,// 支付查询
];
}
// 正确
return $this->createBackData($data);
}
/**
* 返回数据给客户端
* @param array $data
* @return array
* @author helei
*/
protected function createBackData(array $data)
{
// 将金额处理为元
$totalFee = bcdiv($data['total_fee'], 100, 2);
$retData = [
'is_success' => 'T',
'response' => [
'amount' => $totalFee,
'channel' => Config::WX_CHARGE,// 支付查询
'order_no' => $data['out_trade_no'],
'buyer_id' => $data['openid'],
'trade_state' => strtolower($data['trade_state']),
'transaction_id' => $data['transaction_id'],
'time_end' => date('Y-m-d H:i:s', strtotime($data['time_end'])),
'return_param' => $data['attach'],
'terminal_id' => $data['device_info'],
'trade_type' => $data['trade_type'],
'bank_type' => $data['bank_type'],
'trade_state_desc' => isset($data['trade_state_desc']) ? $data['trade_state_desc'] : '交易成功',
],
];
return ArrayUtil::paraFilter($retData);
}
}
<?php
/**
* @author: helei
* @createTime: 2016-08-02 17:55
* @description:
*/
namespace Payment\Query\Wx;
use Payment\Common\Weixin\Data\Query\RefundQueryData;
use Payment\Common\Weixin\WxBaseStrategy;
use Payment\Common\WxConfig;
use Payment\Config;
/**
*
* 微信退款订单查询
* Class WxRefudnQuery
* @package Payment\Query
* anthor helei
*/
class WxRefundQuery extends WxBaseStrategy
{
public function getBuildDataClass()
{
return RefundQueryData::class;
}
protected function getReqUrl()
{
return WxConfig::REFUDN_QUERY_URL;// 查询退款url
}
/**
* 处理通知的返回数据
* @param array $data
* @return mixed
* @author helei
*/
protected function retData(array $data)
{
if ($this->config->returnRaw) {
$data['channel'] = Config::WX_REFUND;
return $data;
}
// 请求失败,可能是网络
if ($data['return_code'] != 'SUCCESS') {
return $retData = [
'is_success' => 'F',
'error' => $data['return_msg'],
'channel' => Config::WX_REFUND,
];
}
// 业务失败
if ($data['result_code'] != 'SUCCESS') {
return $retData = [
'is_success' => 'F',
'error' => $data['err_code_des'],
'channel' => Config::WX_REFUND,
];
}
// 正确
return $this->createBackData($data);
}
/**
* 返回数据给客户端
* @param array $data
* @return array
* @author helei
*/
protected function createBackData(array $data)
{
$refund_count = $data['refund_count'];// 退款的笔数
// 将金额处理为元
$totalFee = bcdiv($data['total_fee'], 100, 2);
$refundFee = bcdiv($data['refund_fee'], 100, 2);
// 获取退款笔数
$refundData = [];
for ($i = 0; $i<$refund_count; $i++) {
$refund_no = 'out_refund_no_' . $i;// 商户退款单号
$refund_id = 'refund_id_' . $i;// 微信退款单号
$refund_channel = 'refund_channel_' . $i;// 退款渠道
$refund_fee = 'refund_fee_' . $i;// 申请退款金额
//$settlement_refund_fee = 'settlement_refund_fee_' . $i;// 实际退款金额
$refund_status = 'refund_status_' . $i;// 退款状态
$recv_accout = 'refund_recv_accout_' . $i;// 退款入账账户
$fee = bcdiv($refund_fee, 100, 2);
// 一笔订单可能被分为多笔,进行退款
$refundData[] = [
'refund_no' => $data[$refund_no],
'refund_id' => $data[$refund_id],
'refund_channel' => $data[$refund_channel],
'refund_fee' => $fee,
//'settlement_refund_fee' => $data[$settlement_refund_fee],
'refund_status' => strtolower($data[$refund_status]),
'recv_accout' => $data[$recv_accout],
];
}
$retData = [
'is_success' => 'T',
'response' => [
'amount' => $totalFee,// 订单总金额
'order_no' => $data['out_trade_no'],// 商户订单号
'transaction_id' => $data['transaction_id'],// 微信订单号
'refund_count' => $data['refund_count'],// 退款总笔数
'refund_fee' => $refundFee,// 退款总金额
'refund_data' => $refundData,// 退款信息
'channel' => Config::WX_REFUND,
],
];
return $retData;
}
}
<?php
/**
* @author: helei
* @createTime: 2016-08-04 10:30
* @description:
*/
namespace Payment\Query\Wx;
use Payment\Common\PayException;
use Payment\Common\Weixin\Data\Query\TransferQueryData;
use Payment\Common\Weixin\WxBaseStrategy;
use Payment\Common\WxConfig;
use Payment\Config;
use Payment\Utils\Curl;
use Payment\Utils\DataParser;
/**
* Class WxTransferQuery
* @package Payment\Query
* anthor helei
*/
class WxTransferQuery extends WxBaseStrategy
{
public function getBuildDataClass()
{
return TransferQueryData::class;
}
/**
* 使用证书方式进行查询
* @param string $xml
* @param string $url
* @return array
* @author helei
*/
protected function curlPost($xml, $url)
{
$curl = new Curl();
$responseTxt = $curl->set([
'CURLOPT_HEADER' => 0,
'CURLOPT_SSL_VERIFYHOST' => false,
'CURLOPT_SSLCERTTYPE' => 'PEM', //默认支持的证书的类型,可以注释
'CURLOPT_SSLCERT' => $this->config->appCertPem,
'CURLOPT_SSLKEY' => $this->config->appKeyPem,
'CURLOPT_CAINFO' => $this->config->cacertPath,
])->post($xml)->submit($url);
return $responseTxt;
}
/**
* 返回付款查询url
* @return string
* @author helei
*/
protected function getReqUrl()
{
return WxConfig::TRANS_QUERY_URL;
}
/**
* 处理通知的返回数据
* @param array $data
* @return mixed
* @author helei
*/
protected function retData(array $data)
{
if ($this->config->returnRaw) {
$data['channel'] = Config::WX_TRANSFER;
return $data;
}
// 请求失败,可能是网络
if ($data['return_code'] != 'SUCCESS') {
return $retData = [
'is_success' => 'F',
'error' => $data['return_msg'],
'channel' => Config::WX_TRANSFER,
];
}
// 业务失败
if ($data['result_code'] != 'SUCCESS') {
return $retData = [
'is_success' => 'F',
'error' => $data['err_code_des'],
'channel' => Config::WX_TRANSFER,
];
}
// 正确
return $this->createBackData($data);
}
/**
* 返回数据给客户端
* @param array $data
* @return array
* @author helei
*/
protected function createBackData(array $data)
{
// 将金额处理为元
$amount = bcdiv($data['payment_amount'], 100, 2);
$retData = [
'is_success' => 'T',
'response' => [
'trans_no' => $data['partner_trade_no'],// 商户单号
'transaction_id' => $data['detail_id'],// 付款单号
'status' => strtolower($data['status']),// 转账状态
'reason' => $data['reason'],// 失败原因
'openid' => $data['openid'],
'payee_name' => $data['transfer_name'],// 收款用户姓名
'amount' => $amount,
'pay_date' => $data['transfer_time'],
'desc' => $data['desc'],// 付款描述
'channel' => Config::WX_TRANSFER,
],
];
return $retData;
}
/**
* @param array $data
* @author helei
* @throws PayException
* @return array|string
*/
public function handle(array $data)
{
$buildClass = $this->getBuildDataClass();
try {
$this->reqData = new $buildClass($this->config, $data);
} catch (PayException $e) {
throw $e;
}
$this->reqData->setSign();
$xml = DataParser::toXml($this->reqData->getData());
$ret = $this->sendReq($xml);
return $this->retData($ret);
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-28 17:24
* @description:
*/
namespace Payment;
use Payment\Common\BaseStrategy;
use Payment\Common\PayException;
use Payment\Query\Ali\AliChargeQuery;
use Payment\Query\Ali\AliRefundQuery;
use Payment\Query\Ali\AliTransferQuery;
use Payment\Query\Cmb\CmbChargeQuery;
use Payment\Query\Cmb\CmbRefundQuery;
use Payment\Query\TLpay\TLQuery;
use Payment\Query\Wx\WxChargeQuery;
use Payment\Query\Wx\WxRefundQuery;
use Payment\Query\Wx\WxTransferQuery;
class QueryContext
{
/**
* 查询的渠道
* @var BaseStrategy
*/
protected $query;
/**
* 设置对应的查询渠道
* @param string $channel 查询渠道
* - @see Config
*
* @param array $config 配置文件
* @throws PayException
* @author helei
*/
public function initQuery($channel, array $config)
{
try {
switch ($channel) {
case Config::ALI_CHARGE:
$this->query = new AliChargeQuery($config);
break;
case Config::ALI_REFUND:// 支付宝退款订单查询
$this->query = new AliRefundQuery($config);
break;
case Config::ALI_TRANSFER:
$this->query = new AliTransferQuery($config);
break;
case Config::WX_CHARGE:// 微信支付订单查询
$this->query = new WxChargeQuery($config);
break;
case Config::WX_REFUND:// 微信退款订单查询
$this->query = new WxRefundQuery($config);
break;
case Config::WX_TRANSFER:// 微信转款订单查询
$this->query = new WxTransferQuery($config);
break;
case Config::CMB_CHARGE:// 招商支付查询
$this->query = new CmbChargeQuery($config);
break;
case Config::CMB_REFUND:// 招商退款查询
$this->query = new CmbRefundQuery($config);
break;
case Config::TL_QUERY:// 招商退款查询
$this->query = new TLQuery($config);
break;
default:
throw new PayException('当前仅支持:ALI_CHARGE ALI_REFUND WX_CHARGE WX_REFUND WX_TRANSFER CMB_CHARGE CMB_REFUND TLPAY');
}
} catch (PayException $e) {
throw $e;
}
}
/**
* 通过环境类调用支付异步通知
*
* @param array $data
* // 二者设置一个即可
* $data => [
* 'transaction_id' => '原付款支付宝交易号',
* 'order_no' => '商户订单号',
* ];
*
* @return array
* @throws PayException
* @author helei
*/
public function query(array $data)
{
if (! $this->query instanceof BaseStrategy) {
throw new PayException('请检查初始化是否正确');
}
try {
return $this->query->handle($data);
} catch (PayException $e) {
throw $e;
}
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-27 10:36
* @description:
*/
namespace Payment\Refund;
use Payment\Common\Ali\AliBaseStrategy;
use Payment\Common\Ali\Data\RefundData;
use Payment\Common\AliConfig;
use Payment\Common\PayException;
use Payment\Config;
class AliRefund extends AliBaseStrategy
{
public function getBuildDataClass()
{
$this->config->method = AliConfig::TRADE_REFUND_METHOD;
return RefundData::class;
}
/**
* 返回数据
* @param array $data
* @return array|string
* @throws PayException
*/
protected function retData(array $data)
{
$url = parent::retData($data);
try {
$rsqData = $this->sendReq($url);
} catch (PayException $e) {
throw $e;
}
$content = json_decode($data['biz_content'], true);
$refundNo = $content['out_request_no'];
if ($this->config->returnRaw) {
$rsqData['channel'] = Config::ALI_REFUND;
return $rsqData;
}
if ($rsqData['code'] !== '10000') {
return $retData = [
'is_success' => 'F',
'error' => $rsqData['sub_msg']
];
}
$retData = [
'is_success' => 'T',
'response' => [
'transaction_id' => $rsqData['trade_no'],
'order_no' => $rsqData['out_trade_no'],
'logon_id' => $rsqData['buyer_logon_id'],
'buyer_id' => $rsqData['buyer_user_id'],
'refund_no' => $refundNo,
'fund_change' => $rsqData['fund_change'],// 本次退款是否发生了资金变化
'refund_fee' => $rsqData['refund_fee'],// 返回的总金额,这里支付宝会累计
'refund_time'=> $rsqData['gmt_refund_pay'],
'channel' => Config::ALI_REFUND,
],
];
return $retData;
}
}
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/4/28
* Time: 下午3:14
*/
namespace Payment\Refund;
use Payment\Common\Cmb\CmbBaseStrategy;
use Payment\Common\Cmb\Data\RefundData;
use Payment\Common\CmbConfig;
use Payment\Config;
/**
* 招商退款
* Class CmbRefund
* @package Payment\Refund
*/
class CmbRefund extends CmbBaseStrategy
{
public function getBuildDataClass()
{
$this->config->getewayUrl = 'https://payment.ebank.cmbchina.com/NetPayment/BaseHttp.dll?DoRefund';
if ($this->config->useSandbox) {// 测试
$this->config->getewayUrl = 'http://121.15.180.66:801/NetPayment_dl/BaseHttp.dll?DoRefund';
}
return RefundData::class;
}
protected function retData(array $ret)
{
$json = json_encode($ret, JSON_UNESCAPED_UNICODE);
$postData = CmbConfig::REQ_FILED_NAME . '=' . $json;
$rsqData = $this->sendReq($postData);
if ($this->config->returnRaw) {
$rsqData['channel'] = Config::CMB_REFUND;
return $rsqData;
}
// 正确情况
$retData = [
'is_success' => 'T',
'response' => [
'transaction_id' => $rsqData['bankSerialNo'],// 银行的退款流水号
'order_no' => $ret['reqData']['orderNo'],
'date' => $ret['reqData']['date'],
'refund_no' => trim($rsqData['refundSerialNo']),//退款流水号,商户生成
'refund_id' => $rsqData['refundRefNo'],// 银行的退款参考号
'currency' => $rsqData['currency'],
'refund_fee' => $rsqData['amount'],
'channel' => Config::CMB_REFUND,
'refund_time' => date('Y-m-d H:i:s', strtotime($rsqData['bankDate'] . $rsqData['bankTime'])),
],
];
return $retData;
}
}
\ No newline at end of file
<?php
/**
* @author: yeran
* @createTime: 2018-04-22 19:01
* @description:
*/
namespace Payment\Refund;
use Payment\Common\TLConfig;
use Payment\Common\TLpay\Data\Cancel\RefundData;
use Payment\Config;
use Payment\Common\TLpay\TLBaseStrategy;
/**
* Class TLRefund
* 微信退款操作
* @package Payment\Refund
* anthor yeran
*/
class TLRefund extends TLBaseStrategy
{
public function getBuildDataClass()
{
return RefundData::class;
}
/**
* 返回退款的url
* @return null|string
* @author helei
*/
protected function getReqUrl()
{
return TLConfig::REFUND_URL;
}
protected function retData(array $ret)
{
//cusid 商户号 平台分配的商户号 否 15
//appid 应用ID 平台分配的APPID 否 8
//trxid 交易单号 收银宝平台的退款交易流水号 否 20
//reqsn 商户订单号 商户的退款交易订单号 否 32
//trxstatus 交易状态 交易的状态 否 4 见附录3.1
//fintime 交易完成时间 yyyyMMddHHmmss 是 14
//errmsg 错误原因 失败的原因说明 是 100
//randomstr 随机字符串 随机生成的字符串 否 32
//sign 签名 否 32 详见1.5
//返回数组结构
return $ret;
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-27 10:51
* @description:
*/
namespace Payment\Refund;
use Payment\Common\Weixin\Data\RefundData;
use Payment\Common\Weixin\WxBaseStrategy;
use Payment\Common\WxConfig;
use Payment\Config;
use Payment\Utils\Curl;
/**
* Class WxRefund
* 微信退款操作
* @package Payment\Refund
* anthor helei
*/
class WxRefund extends WxBaseStrategy
{
public function getBuildDataClass()
{
return RefundData::class;
}
/**
* 微信退款接口,需要用到相关加密文件及证书,需要重新进行curl的设置
* @param string $xml
* @param string $url
* @return array
* @author helei
*/
protected function curlPost($xml, $url)
{
$curl = new Curl();
$responseTxt = $curl->set([
'CURLOPT_HEADER' => 0,
'CURLOPT_SSL_VERIFYHOST' => false,
'CURLOPT_SSLCERTTYPE' => 'PEM', //默认支持的证书的类型,可以注释
'CURLOPT_SSLCERT' => $this->config->appCertPem,
'CURLOPT_SSLKEY' => $this->config->appKeyPem,
'CURLOPT_CAINFO' => $this->config->cacertPath,
])->post($xml)->submit($url);
return $responseTxt;
}
/**
* 处理退款的返回数据
* @param array $ret
* @return mixed
* @author helei
*/
protected function retData(array $ret)
{
if ($this->config->returnRaw) {
$ret['channel'] = Config::WX_REFUND;
return $ret;
}
// 请求失败,可能是网络
if ($ret['return_code'] != 'SUCCESS') {
return $retData = [
'is_success' => 'F',
'error' => $ret['return_msg']
];
}
// 业务失败
if ($ret['result_code'] != 'SUCCESS') {
return $retData = [
'is_success' => 'F',
'error' => $ret['err_code_des']
];
}
return $this->createBackData($ret);
}
/**
* 处理返回的数据
* @param array $data
* @return array
* @author helei
*/
protected function createBackData(array $data)
{
// 将订单总金额金额处理为元
$total_fee = bcdiv($data['total_fee'], 100, 2);
// 将订单退款金额处理为元
$refund_fee = bcdiv($data['refund_fee'], 100, 2);
$retData = [
'is_success' => 'T',
'response' => [
'transaction_id' => $data['transaction_id'],
'order_no' => $data['out_trade_no'],
'refund_no' => $data['out_refund_no'],
'refund_id' => $data['refund_id'],
'refund_fee' => $refund_fee,
'refund_channel' => $data['refund_channel'],
'amount' => $total_fee,
'channel' => Config::WX_REFUND,
'coupon_refund_fee' => bcdiv($data['coupon_refund_fee'], 100, 2),
'coupon_refund_count' => $data['coupon_refund_count'],
'cash_fee' => bcdiv($data['cash_fee'], 100, 2),
'cash_refund_fee' => bcdiv($data['cash_refund_fee'], 100, 2),
],
];
return $retData;
}
/**
* 返回退款的url
* @return null|string
* @author helei
*/
protected function getReqUrl()
{
return WxConfig::REFUND_URL;
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-27 17:42
* @description: 退款统一接口
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment;
use Payment\Common\BaseStrategy;
use Payment\Common\PayException;
use Payment\Refund\AliRefund;
use Payment\Refund\CmbRefund;
use Payment\Refund\TLRefund;
use Payment\Refund\WxRefund;
class RefundContext
{
/**
* 退款的渠道
* @var BaseStrategy
*/
protected $refund;
/**
* 设置对应的退款渠道
* @param string $channel 退款渠道
* - @see Config
*
* @param array $config 配置文件
* @throws PayException
* @author helei
*/
public function initRefund($channel, array $config)
{
try {
switch ($channel) {
case Config::ALI_REFUND:
$this->refund = new AliRefund($config);
break;
case Config::WX_REFUND:
$this->refund = new WxRefund($config);
break;
case Config::CMB_REFUND:
$this->refund = new CmbRefund($config);
break;
case Config::TL_REFUND:
$this->refund = new TLRefund($config);
break;
default:
throw new PayException('当前仅支持:ALI WEIXIN CMB');
}
} catch (PayException $e) {
throw $e;
}
}
/**
* 通过环境类调用支付退款操作
*
* @param array $data
*
* @return array
* @throws PayException
* @author helei
*/
public function refund(array $data)
{
if (! $this->refund instanceof BaseStrategy) {
throw new PayException('请检查初始化是否正确');
}
try {
return $this->refund->handle($data);
} catch (PayException $e) {
throw $e;
}
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-27 15:28
* @description: 支付宝批量付款接口
*/
namespace Payment\Trans;
use Payment\Common\Ali\AliBaseStrategy;
use Payment\Common\Ali\Data\TransData;
use Payment\Common\AliConfig;
use Payment\Common\PayException;
use Payment\Config;
class AliTransfer extends AliBaseStrategy
{
public function getBuildDataClass()
{
$this->config->method = AliConfig::TRANS_TOACCOUNT_METHOD;
return TransData::class;
}
protected function retData(array $data)
{
$url = parent::retData($data);
try {
$data = $this->sendReq($url);
} catch (PayException $e) {
throw $e;
}
if ($this->config->returnRaw) {
$data['channel'] = Config::ALI_TRANSFER;
return $data;
}
return $this->createBackData($data);
}
/**
* 处理返回的数据
* @param array $data
* @return array
* @author helei
*/
protected function createBackData(array $data)
{
// 新版本
if ($data['code'] !== '10000') {
return $retData = [
'is_success' => 'F',
'error' => $data['sub_msg'],
'channel' => Config::ALI_TRANSFER,
];
}
$retData = [
'is_success' => 'T',
'response' => [
'transaction_id' => $data['order_id'],
'trans_no' => $data['out_biz_no'],
'pay_date' => $data['pay_date'],
'channel' => Config::ALI_TRANSFER,
],
];
return $retData;
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-27 15:43
* @description:
*/
namespace Payment\Trans;
use Payment\Common\Weixin\Data\TransferData;
use Payment\Common\Weixin\WxBaseStrategy;
use Payment\Common\WxConfig;
use Payment\Config;
use Payment\Utils\Curl;
/**
* 微信企业付款接口
* Class WxTransfer
* @package Payment\Trans
* anthor helei
*/
class WxTransfer extends WxBaseStrategy
{
public function getBuildDataClass()
{
return TransferData::class;
}
/*
* 返回转款的url
*/
protected function getReqUrl()
{
return WxConfig::TRANSFERS_URL;
}
/**
* 微信退款接口,需要用到相关加密文件及证书,需要重新进行curl的设置
* @param string $xml
* @param string $url
* @return array
* @author helei
*/
protected function curlPost($xml, $url)
{
$curl = new Curl();
$responseTxt = $curl->set([
'CURLOPT_HEADER' => 0,
'CURLOPT_SSL_VERIFYHOST' => false,
'CURLOPT_SSLCERTTYPE' => 'PEM', //默认支持的证书的类型,可以注释
'CURLOPT_SSLCERT' => $this->config->appCertPem,
'CURLOPT_SSLKEY' => $this->config->appKeyPem,
'CURLOPT_CAINFO' => $this->config->cacertPath,
])->post($xml)->submit($url);
return $responseTxt;
}
/**
* 转款的返回数据
* @param array $ret
* @return mixed
*/
protected function retData(array $ret)
{
if ($this->config->returnRaw) {
$ret['channel'] = Config::WX_TRANSFER;
return $ret;
}
// 请求失败,可能是网络
if ($ret['return_code'] != 'SUCCESS') {
return $retData = [
'is_success' => 'F',
'error' => $ret['return_msg'],
'channel' => Config::WX_TRANSFER,
];
}
// 业务失败
if ($ret['result_code'] != 'SUCCESS') {
return $retData = [
'is_success' => 'F',
'error' => $ret['err_code_des'],
'channel' => Config::WX_TRANSFER,
];
}
return $this->createBackData($ret);
}
/**
* 返回数据
* @param array $data
* @return array
*/
protected function createBackData(array $data)
{
$retData = [
'is_success' => 'T',
'response' => [
'trans_no' => $data['partner_trade_no'],
'transaction_id' => $data['payment_no'],
'pay_date' => $data['payment_time'],// 企业付款成功时间 2015-05-19 15:26:59
'device_info' => $data['device_info'],
'channel' => Config::WX_TRANSFER,
],
];
return $retData;
}
/**
* 企业转账,不需要签名,使用返回true
* @param array $retData
* @return bool
*/
protected function verifySign(array $retData)
{
return true;
}
}
<?php
/**
* @author: helei
* @createTime: 2016-07-27 15:40
* @description:
*/
namespace Payment;
use Payment\Common\BaseStrategy;
use Payment\Common\PayException;
use Payment\Trans\AliTransfer;
use Payment\Trans\WxTransfer;
class TransferContext
{
/**
* 转款渠道
* @var BaseStrategy
*/
protected $transfer;
/**
* 设置对应的退款渠道
* @param string $channel 退款渠道
* - @see Config
*
* @param array $config 配置文件
* @throws PayException
* @author helei
*/
public function initTransfer($channel, array $config)
{
try {
switch ($channel) {
case Config::ALI_TRANSFER:
$this->transfer = new AliTransfer($config);
break;
case Config::WX_TRANSFER:
$this->transfer = new WxTransfer($config);
break;
default:
throw new PayException('当前仅支持:ALI WEIXIN两个常量');
}
} catch (PayException $e) {
throw $e;
}
}
/**
* 通过环境类调用支付转款操作
*
* @param array $data
*
* @return array
* @throws PayException
* @author helei
*/
public function transfer(array $data)
{
if (! $this->transfer instanceof BaseStrategy) {
throw new PayException('请检查初始化是否正确');
}
try {
return $this->transfer->handle($data);
} catch (PayException $e) {
throw $e;
}
}
}
<?php
/**
* @author: helei
* @createTime: 2016-06-07 21:01
* @description: 常用的数组处理工具
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment\Utils;
class ArrayUtil
{
/**
* 移除空值的key
* @param $para
* @return array
* @author helei
*/
public static function paraFilter($para)
{
$paraFilter = [];
while (list($key, $val) = each($para)) {
if ($val === '' || $val === null) {
continue;
} else {
if (! is_array($para[$key])) {
$para[$key] = is_bool($para[$key]) ? $para[$key] : trim($para[$key]);
}
$paraFilter[$key] = $para[$key];
}
}
return $paraFilter;
}
/**
* 删除一位数组中,指定的key与对应的值
* @param array $inputs 要操作的数组
* @param array|string $keys 需要删除的key的数组,或者用(,)链接的字符串
* @return array
*/
public static function removeKeys(array $inputs, $keys)
{
if (! is_array($keys)) {// 如果不是数组,需要进行转换
$keys = explode(',', $keys);
}
if (empty($keys) || ! is_array($keys)) {
return $inputs;
}
$flag = true;
foreach ($keys as $key) {
if (array_key_exists($key, $inputs)) {
if (is_int($key)) {
$flag = false;
}
unset($inputs[$key]);
}
}
if (! $flag) {
$inputs = array_values($inputs);
}
return $inputs;
}
/**
* 对输入的数组进行字典排序
* @param array $param 需要排序的数组
* @return array
* @author helei
*/
public static function arraySort(array $param)
{
ksort($param);
reset($param);
return $param;
}
/**
* 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
* @param array $para 需要拼接的数组
* @return string
* @throws \Exception
*/
public static function createLinkstring($para)
{
if (! is_array($para)) {
throw new \Exception('必须传入数组参数');
}
reset($para);
$arg = '';
while (list($key, $val) = each($para)) {
if (is_array($val)) {
continue;
}
$arg.=$key.'='.urldecode($val).'&';
}
//去掉最后一个&字符
$arg && $arg = substr($arg, 0, -1);
//如果存在转义字符,那么去掉转义
if (get_magic_quotes_gpc()) {
$arg = stripslashes($arg);
}
return $arg;
}
/**
* 将参数数组签名
*/
public static function SignArray(array $array,$appkey){
$array['key'] = $appkey;// 将key放到数组中一起进行排序和组装
ksort($array);
$blankStr = self::ToUrlParams($array);
$sign = md5($blankStr);
return $sign;
}
public static function ToUrlParams(array $array)
{
$buff = "";
foreach ($array as $k => $v)
{
if($v != "" && !is_array($v)){
$buff .= $k . "=" . $v . "&";
}
}
$buff = trim($buff, "&");
return $buff;
}
/**
* 校验签名
* @param array 参数
* @param unknown_type appkey
* @return bool
*/
public static function ValidSign(array $array,$appkey){
$sign = $array['sign'];
unset($array['sign']);
$array['key'] = $appkey;
$mySign = self::SignArray($array, $appkey);
return strtolower($sign) == strtolower($mySign);
}
}
<?php
/**
* @author: helei
* @createTime: 2016-06-07 19:38
* @description: 一个轻量级的网络操作类,实现GET、POST、UPLOAD、DOWNLOAD常用操作,支持链式写法。
* @link https://github.com/helei112g/PHP-Curl
* @link https://helei112g.github.io/
*/
namespace Payment\Utils;
class Curl
{
private $post;
private $retry;
private $option;
private $default;
private $download;
private static $instance;
public function __construct()
{
$this->retry = 0;
$this->default = array(
'CURLOPT_TIMEOUT' => 30,
'CURLOPT_ENCODING' => '',
'CURLOPT_IPRESOLVE' => 1,
'CURLOPT_RETURNTRANSFER' => true,
'CURLOPT_SSL_VERIFYPEER' => false,
'CURLOPT_CONNECTTIMEOUT' => 10,
'CURLOPT_HEADER' => 0
);
}
/**
* 静态实例化
* @return Curl
*/
public static function init()
{
if (static::$instance === null) {
static::$instance = new static;
}
return static::$instance;
}
//发送请求操作仅供参考,不为最佳实践
function request($url,$params){
$ch = curl_init();
$this_header = array("content-type: application/x-www-form-urlencoded;charset=UTF-8");
curl_setopt($ch,CURLOPT_HTTPHEADER,$this_header);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)');
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);//如果不加验证,就设false,商户自行处理
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
$output = curl_exec($ch);
curl_close($ch);
return $output;
}
/**
* 提交GET请求
* @param string $url
* @return array
*/
public function get($url)
{
return $this->set('CURLOPT_URL', $url)->exec();
}
/**
* 设置POST信息
* @param array|string $data
* @param string $value
* @return $this
*/
public function post($data, $value = '')
{
if (is_array($data)) {
foreach ($data as $key => $value) {
$this->post[$key] = $value;
}
} elseif ($value) {
$this->post[$data] = $value;
} else {
$this->post = $data;
}
return $this;
}
/**
* 设置文件上传
* @param string $field
* @param string $path
* @param string $type
* @param string $name
* @return $this
*/
public function upload($field, $path, $type, $name)
{
$name = basename($name);
if (class_exists('CURLFile')) {
$this->set('CURLOPT_SAFE_UPLOAD', true);
$file = curl_file_create($path, $type, $name);
} else {
$file = "@{$path};type={$type};filename={$name}";
}
return $this->post($field, $file);
}
/**
* 提交POST请求
* @param string $url
* @return array
*/
public function submit($url)
{
if (! $this->post) {
return array(
'error' => 1,
'message' => '未设置POST信息'
);
}
return $this->set('CURLOPT_URL', $url)->exec();
}
/**
* 设置下载地址
* @param string $url
* @return $this
*/
public function download($url)
{
$this->download = true;
return $this->set('CURLOPT_URL', $url);
}
/**
* 下载保存文件
* @param string $path
* @return array
*/
public function save($path)
{
if (! $this->download) {
return array(
'error' => 1,
'message' => '未设置下载地址'
);
}
$result = $this->exec();
if ($result['error'] === 0) {
$fp = @fopen($path, 'w');
fwrite($fp, $result['body']);
fclose($fp);
}
return $result;
}
/**
* 配置Curl操作
* @param array|string $item
* @param string $value
* @return $this
*/
public function set($item, $value = '')
{
if (is_array($item)) {
foreach ($item as $key => &$value) {
$this->option[$key] = $value;
}
} else {
$this->option[$item] = $value;
}
return $this;
}
/**
* 出错自动重试
* @param int $times
* @return $this
*/
public function retry($times = 0)
{
$this->retry = $times;
return $this;
}
/**
* 执行Curl操作
* @param int $retry
* @return array
*/
private function exec($retry = 0)
{
// 初始化句柄
$ch = curl_init();
// 配置选项
$options = array_merge($this->default, $this->option);
foreach ($options as $key => $val) {
if (is_string($key)) {
$key = constant(strtoupper($key));
}
curl_setopt($ch, $key, $val);
}
// POST选项
if ($this->post) {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $this->postFieldsBuild($this->post));
}
// 运行句柄
$body = curl_exec($ch);
$info = curl_getinfo($ch);
// 检查错误
$errno = curl_errno($ch);
if ($errno === 0 && $info['http_code'] >= 400) {
$errno = $info['http_code'];
}
// 注销句柄
curl_close($ch);
// 自动重试
if ($errno && $retry < $this->retry) {
$this->exec($retry + 1);
}
// 注销配置
$this->post = null;
$this->retry = null;
$this->option = null;
$this->download = null;
// 返回结果
return array(
'error' => $errno ? 1 : 0,
'message' => $errno,
'body' => $body,
'info' => $info
);
}
/**
* 一维化POST信息
* @param array $input
* @param string $pre
* @return array
*/
private function postFieldsBuild($input, $pre = null)
{
if (is_array($input)) {
$output = array();
foreach ($input as $key => $value) {
$index = is_null($pre) ? $key : "{$pre}[{$key}]";
if (is_array($value)) {
$output = array_merge($output, $this->postFieldsBuild($value, $index));
} else {
$output[$index] = $value;
}
}
return $output;
}
return $input;
}
}
<?php
/**
* @author: helei
* @createTime: 2016-06-08 13:02
* @description: 数据解析的工具类,主要处理,xml到数组 数组到xml的转化
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment\Utils;
class DataParser
{
/**
* 输出xml字符
* @param array $values
* @return string|bool
**/
public static function toXml($values)
{
if (!is_array($values) || count($values) <= 0) {
return false;
}
$xml = "<xml>";
foreach ($values as $key => $val) {
if (is_numeric($val)) {
$xml.="<".$key.">".$val."</".$key.">";
} else {
$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
}
}
$xml.="</xml>";
return $xml;
}
/**
* 将xml转为array
* @param string $xml
* @return array|false
*/
public static function toArray($xml)
{
if (!$xml) {
return false;
}
// 检查xml是否合法
$xml_parser = xml_parser_create();
if (!xml_parse($xml_parser, $xml, true)) {
xml_parser_free($xml_parser);
return false;
}
//将XML转为array
//禁止引用外部xml实体
libxml_disable_entity_loader(true);
$data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $data;
}
/**
* google api 二维码生成【QRcode可以存储最多4296个字母数字类型的任意文本,具体可以查看二维码数据格式】
* @param string $text 二维码包含的信息,可以是数字、字符、二进制信息、汉字。不能混合数据类型,数据必须经过UTF-8 URL-encoded
* @param string $widthHeight 生成二维码的尺寸设置
* @param string $ecLevel 可选纠错级别,QR码支持四个等级纠错,用来恢复丢失的、读错的、模糊的、数据。
* L-默认:可以识别已损失的7%的数据
* M-可以识别已损失15%的数据
* Q-可以识别已损失25%的数据
* H-可以识别已损失30%的数据
*
* @param string $margin 生成的二维码离图片边框的距离
*
* @return string
*/
public static function toQRimg($text, $widthHeight = '150', $ecLevel = 'L', $margin = '0')
{
$chl = urlencode($text);
return "http://chart.apis.google.com/chart?chs={$widthHeight}x{$widthHeight}&cht=qr&chld={$ecLevel}|{$margin}&chl={$chl}";
}
}
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 2017/4/28
* Time: 下午3:25
*/
namespace Payment\Utils;
class Rc4Encrypt
{
protected $key;
public function __construct($key)
{
$this->key = $key;
}
/**
* 设置key
* @param $key
* @author helei
*/
public function setKey($key)
{
$this->key = $key;
}
/**
* rc4加密算法
* @param $data
* @return string
* @throws \Exception
*/
public function encrypt($data)
{
$cipher = $box[] = $key[] = '';
$pwd_length = strlen($this->key);
$data_length = strlen($data);
for ($i = 0; $i < 256; $i++) {
$key[$i] = ord($this->key[$i % $pwd_length]);
$box[$i] = $i;
}
for ($j = $i = 0; $i < 256; $i++) {
$j = ($j + $box[$i] + $key[$i]) % 256;
$tmp = $box[$i];
$box[$i] = $box[$j];
$box[$j] = $tmp;
}
for ($a = $j = $i = 0; $i < $data_length; $i++) {
$a = ($a + 1) % 256;
$j = ($j + $box[$a]) % 256;
$tmp = $box[$a];
$box[$a] = $box[$j];
$box[$j] = $tmp;
$k = $box[(($box[$a] + $box[$j]) % 256)];
$cipher .= chr(ord($data[$i]) ^ $k);
}
return strtoupper(StrUtil::String2Hex($cipher));
}
}
\ No newline at end of file
<?php
/**
* @author: helei
* @createTime: 2017-03-07 09:29
* @description: rsa2加密算法
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment\Utils;
class Rsa2Encrypt
{
protected $key;
public function __construct($key)
{
$this->key = $key;
}
/**
* 设置key
* @param $key
* @author helei
*/
public function setKey($key)
{
$this->key = $key;
}
/**
* RSA2签名, 此处秘钥是私有秘钥
* @param string $data 签名的数组
* @throws \Exception
* @return string
* @author helei
*/
public function encrypt($data)
{
if ($this->key === false) {
return '';
}
$res = openssl_get_privatekey($this->key);
if (empty($res)) {
throw new \Exception('您使用的私钥格式错误,请检查RSA私钥配置');
}
openssl_sign($data, $sign, $res, OPENSSL_ALGO_SHA256);
openssl_free_key($res);
//base64编码
$sign = base64_encode($sign);
return $sign;
}
/**
* RSA2解密 此处秘钥是用户私有秘钥
* @param string $content 需要解密的内容,密文
* @throws \Exception
* @return string
* @author helei
*/
public function decrypt($content)
{
if ($this->key === false) {
return '';
}
$res = openssl_get_privatekey($this->key);
if (empty($res)) {
throw new \Exception('您使用的私钥格式错误,请检查RSA私钥配置');
}
//用base64将内容还原成二进制
$decodes = base64_decode($content);
$str = '';
$dcyCont = '';
foreach ($decodes as $n => $decode) {
if (!openssl_private_decrypt($decode, $dcyCont, $res)) {
echo "<br/>" . openssl_error_string() . "<br/>";
}
$str .= $dcyCont;
}
openssl_free_key($res);
return $str;
}
/**
* RSA2验签 ,此处的秘钥,是第三方公钥
* @param string $data 待签名数据
* @param string $sign 要校对的的签名结果
* @throws \Exception
* @return bool
* @author helei
*/
public function rsaVerify($data, $sign)
{
// 初始时,使用公钥key
$res = openssl_get_publickey($this->key);
if (empty($res)) {
throw new \Exception('支付宝RSA公钥错误。请检查公钥文件格式是否正确');
}
$result = (bool)openssl_verify($data, base64_decode($sign), $res, OPENSSL_ALGO_SHA256);
openssl_free_key($res);
return $result;
}
}
<?php
/**
* @author: helei
* @createTime: 2016-06-08 16:29
* @description: rsa加密算法
* @link https://github.com/helei112g/payment/tree/paymentv2
* @link https://helei112g.github.io/
*/
namespace Payment\Utils;
class RsaEncrypt
{
protected $key;
public function __construct($key)
{
$this->key = $key;
}
/**
* 设置key
* @param $key
* @author helei
*/
public function setKey($key)
{
$this->key = $key;
}
/**
* RSA签名, 此处秘钥是私有秘钥
* @param string $data 签名的数组
* @throws \Exception
* @return string
* @author helei
*/
public function encrypt($data)
{
if ($this->key === false) {
return '';
}
$res = openssl_get_privatekey($this->key);
if (empty($res)) {
throw new \Exception('您使用的私钥格式错误,请检查RSA私钥配置');
}
openssl_sign($data, $sign, $res);
openssl_free_key($res);
//base64编码
$sign = base64_encode($sign);
return $sign;
}
/**
* RSA解密 此处秘钥是用户私有秘钥
* @param string $content 需要解密的内容,密文
* @throws \Exception
* @return string
* @author helei
*/
public function decrypt($content)
{
if ($this->key === false) {
return '';
}
$res = openssl_get_privatekey($this->key);
if (empty($res)) {
throw new \Exception('您使用的私钥格式错误,请检查RSA私钥配置');
}
//用base64将内容还原成二进制
$content = base64_decode($content);
//把需要解密的内容,按128位拆开解密
$result = '';
for ($i = 0; $i < strlen($content)/128; $i++) {
$data = substr($content, $i * 128, 128);
openssl_private_decrypt($data, $decrypt, $res);
$result .= $decrypt;
}
openssl_free_key($res);
return $result;
}
/**
* RSA验签 ,此处的秘钥,是第三方公钥
* @param string $data 待签名数据
* @param string $sign 要校对的的签名结果
* @throws \Exception
* @return bool
* @author helei
*/
public function rsaVerify($data, $sign)
{
// 初始时,使用公钥key
$res = openssl_get_publickey($this->key);
if (empty($res)) {
throw new \Exception('支付宝RSA公钥错误。请检查公钥文件格式是否正确');
}
$result = (bool)openssl_verify($data, base64_decode($sign), $res);
openssl_free_key($res);
return $result;
}
}
<?php
/**
* Created by PhpStorm.
* User: helei
* Date: 16/7/31
* Time: 上午8:12
*/
namespace Payment\Utils;
/**
* Class StrUtil
* @dec 字符串处理类
* @package Payment\Utils
*/
class StrUtil
{
/**
* 产生随机字符串,不长于32位
* @param int $length
* @return string 产生的随机字符串
*/
public static function getNonceStr($length = 32)
{
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
$str = "";
for ($i = 0; $i < $length; $i++) {
$str .= substr($chars, mt_rand(0, strlen($chars)-1), 1);
}
return $str;
}
/**
* 转码字符集转码 仅支持 转码 到 UTF-8
* @param string $str
* @param string $targetCharset
* @return mixed|string
*/
public static function characet($str, $targetCharset)
{
if (empty($str)) {
return $str;
}
if (strcasecmp('UTF-8', $targetCharset) != 0) {
$str = mb_convert_encoding($str, $targetCharset, 'UTF-8');
}
return $str;
}
/**
* 转成16进制
* @param string $string
* @return string
*/
public static function String2Hex($string)
{
$hex = '';
$len = strlen($string);
for ($i=0; $i < $len; $i++) {
$hex .= dechex(ord($string[$i]));
}
return $hex;
}
/**
* 获取rsa密钥内容
* @param string $key 传入的密钥信息, 可能是文件或者字符串
* @param string $type
*
* @return string
*/
public static function getRsaKeyValue($key, $type = 'private')
{
if (is_file($key)) {// 是文件
$keyStr = @file_get_contents($key);
} else {
$keyStr = $key;
}
$keyStr = str_replace(PHP_EOL, '', $keyStr);
// 为了解决用户传入的密钥格式,这里进行统一处理
if ($type === 'private') {
$beginStr = ['-----BEGIN RSA PRIVATE KEY-----', '-----BEGIN PRIVATE KEY-----'];
$endStr = ['-----END RSA PRIVATE KEY-----', '-----END PRIVATE KEY-----'];
} else {
$beginStr = ['-----BEGIN PUBLIC KEY-----', ''];
$endStr = ['-----END PUBLIC KEY-----', ''];
}
$keyStr = str_replace($beginStr, ['', ''], $keyStr);
$keyStr = str_replace($endStr, ['', ''], $keyStr);
$rsaKey = $beginStr[0] . PHP_EOL . wordwrap($keyStr, 64, PHP_EOL, true) . PHP_EOL . $endStr[0];
return $rsaKey;
}
public static function getBytes($str){
return implode(null, array_map('ord', str_split($str)));
}
}
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!