first commit

This commit is contained in:
2025-11-28 10:08:12 +08:00
commit 09a14bf0a3
1088 changed files with 145132 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
deny from all
+152
View File
@@ -0,0 +1,152 @@
<?php
webscan_error();
//引用配置文件
require_once('webscan_cache.php');
//get拦截规则
$getfilter = "\\<.+javascript:window\\[.{1}\\\\x|<.*=(&#\\d+?;?)+?>|<.*(data|src)=data:text\\/html.*>|\\b(alert\\(|confirm\\(|expression\\(|prompt\\(|benchmark\s*?\(.*\)|sleep\s*?\(.*\)|\\b(group_)?concat[\\s\\/\\*]*?\\([^\\)]+?\\)|\bcase[\s\/\*]*?when[\s\/\*]*?\([^\)]+?\)|load_file\s*?\\()|<[a-z]+?\\b[^>]*?\\bon([a-z]{4,})\s*?=|^\\+\\/v(8|9)|\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.*\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)|UPDATE\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE)@{0,2}(\\(.+\\)|\\s+?.+?\\s+?|(`|'|\").*?(`|'|\"))FROM(\\(.+\\)|\\s+?.+?|(`|'|\").*?(`|'|\"))|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)|<.*(iframe|frame|style|embed|object|frameset|meta|xml)";
//post拦截规则
$postfilter = "<.*=(&#\\d+?;?)+?>|<.*data=data:text\\/html.*>|\\b(alert\\(|confirm\\(|expression\\(|prompt\\(|benchmark\s*?\(.*\)|sleep\s*?\(.*\)|\\b(group_)?concat[\\s\\/\\*]*?\\([^\\)]+?\\)|\bcase[\s\/\*]*?when[\s\/\*]*?\([^\)]+?\)|load_file\s*?\\()|<[^>]*?\\b(onerror|onmousemove|onload|onclick|onmouseover)\\b|\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.*\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)|UPDATE\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE)(\\(.+\\)|\\s+?.+?\\s+?|(`|'|\").*?(`|'|\"))FROM(\\(.+\\)|\\s+?.+?|(`|'|\").*?(`|'|\"))|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)|<.*(iframe|frame|style|embed|object|frameset|meta|xml)";
//cookie拦截规则
$cookiefilter = "benchmark\s*?\(.*\)|sleep\s*?\(.*\)|load_file\s*?\\(|\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.*\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)|UPDATE\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE)@{0,2}(\\(.+\\)|\\s+?.+?\\s+?|(`|'|\").*?(`|'|\"))FROM(\\(.+\\)|\\s+?.+?|(`|'|\").*?(`|'|\"))|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
//referer获取
$webscan_referer = empty($_SERVER['HTTP_REFERER']) ? array() : array('HTTP_REFERER'=>$_SERVER['HTTP_REFERER']);
/**
* 关闭用户错误提示
*/
function webscan_error() {
if (ini_get('display_errors')) {
ini_set('display_errors', '0');
}
}
/**
* 数据统计回传
*/
function webscan_slog($logs) {
//日志记录
return true;
}
/**
* 参数拆分
*/
function webscan_arr_foreach($arr) {
static $str;
static $keystr;
if (!is_array($arr)) {
return $arr;
}
foreach ($arr as $key => $val ) {
$keystr=$keystr.$key;
if (is_array($val)) {
webscan_arr_foreach($val);
} else {
$str[] = $val.$keystr;
}
}
return implode($str);
}
/**
* 防护提示页
*/
function webscan_pape(){
$pape=<<<HTML
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8"/>
<title>输入内容存在危险字符,安全起见,已被本站拦截</title>
<style>
body, h1, h2, p,dl,dd,dt{margin: 0;padding: 0;font: 12px/1.5 微软雅黑,tahoma,arial;}
body{background:#efefef;}
h1, h2, h3, h4, h5, h6 {font-size: 100%;cursor:default;}
ul, ol {list-style: none outside none;}
a {text-decoration: none;color:#447BC4}
a:hover {text-decoration: underline;}
.ip-attack{width:600px; margin:200px auto 0;}
.ip-attack dl{ background:#fff; padding:30px; border-radius:10px;border: 1px solid #CDCDCD;-webkit-box-shadow: 0 0 8px #CDCDCD;-moz-box-shadow: 0 0 8px #cdcdcd;box-shadow: 0 0 8px #CDCDCD;}
.ip-attack dt{text-align:center;}
.ip-attack dd{font-size:16px; color:#333; text-align:center;}
.tips{text-align:center; font-size:14px; line-height:50px; color:#999;}
</style>
</head>
<body>
<div class="ip-attack">
<dl>
<dt><img src='http://p2.qhimg.com/t016dd70ac04d942b1b.png' /></dt>
<dt><a href="javascript:history.go(-1)">返回上一页</a></dt>
</dl>
</div>
</body>
</html>
HTML;
echo $pape;
}
/**
* 攻击检查拦截
*/
function webscan_StopAttack($StrFiltKey,$StrFiltValue,$ArrFiltReq,$method) {
$StrFiltValue=webscan_arr_foreach($StrFiltValue);
if (preg_match("/".$ArrFiltReq."/is",$StrFiltValue)==1){
webscan_slog(array('ip' => $_SERVER["REMOTE_ADDR"],'time'=>strftime("%Y-%m-%d %H:%M:%S"),'page'=>$_SERVER["PHP_SELF"],'method'=>$method,'rkey'=>$StrFiltKey,'rdata'=>$StrFiltValue,'user_agent'=>$_SERVER['HTTP_USER_AGENT'],'request_url'=>$_SERVER["REQUEST_URI"]));
exit(webscan_pape());
}
if (preg_match("/".$ArrFiltReq."/is",$StrFiltKey)==1){
webscan_slog(array('ip' => $_SERVER["REMOTE_ADDR"],'time'=>strftime("%Y-%m-%d %H:%M:%S"),'page'=>$_SERVER["PHP_SELF"],'method'=>$method,'rkey'=>$StrFiltKey,'rdata'=>$StrFiltKey,'user_agent'=>$_SERVER['HTTP_USER_AGENT'],'request_url'=>$_SERVER["REQUEST_URI"]));
exit(webscan_pape());
}
}
/**
* 拦截目录白名单
*/
function webscan_white($webscan_white_name,$webscan_white_url=array()) {
$url_path=$_SERVER['SCRIPT_NAME'];
$url_var=$_SERVER['QUERY_STRING'];
if (preg_match("/".$webscan_white_name."/is",$url_path)==1&&!empty($webscan_white_name)) {
return false;
}
foreach ($webscan_white_url as $key => $value) {
if(!empty($url_var)&&!empty($value)){
if (stristr($url_path,$key)&&stristr($url_var,$value)) {
return false;
}
}
elseif (empty($url_var)&&empty($value)) {
if (stristr($url_path,$key)) {
return false;
}
}
}
return true;
}
if ($webscan_switch&&webscan_white($webscan_white_directory,$webscan_white_url)) {
if ($webscan_get) {
foreach($_GET as $key=>$value) {
webscan_StopAttack($key,$value,$getfilter,"GET");
}
}
if ($webscan_post) {
foreach($_POST as $key=>$value) {
webscan_StopAttack($key,$value,$postfilter,"POST");
}
}
if ($webscan_cookie) {
foreach($_COOKIE as $key=>$value) {
webscan_StopAttack($key,$value,$cookiefilter,"COOKIE");
}
}
if ($webscan_referre) {
foreach($webscan_referer as $key=>$value) {
webscan_StopAttack($key,$value,$postfilter,"REFERRER");
}
}
}
?>
+14
View File
@@ -0,0 +1,14 @@
<?php
//拦截开关(1为开启,0关闭)
$webscan_switch=1;
//提交方式拦截(1开启拦截,0关闭拦截,post,get,cookie,referre选择需要拦截的方式)
$webscan_post=1;
$webscan_get=1;
$webscan_cookie=1;
$webscan_referre=1;
//后台白名单,后台操作将不会拦截,添加"|"隔开白名单目录下面默认是网址带 admin /dede/ 放行
$webscan_white_directory='';
//url白名单,可以自定义添加url白名单,默认是对phpcms的后台url放行
//写法:比如phpcms 后台操作url index.php?m=admin php168的文章提交链接post.php?job=postnew&step=post ,dedecms 空间设置edit_space_info.php
$webscan_white_url = array('index.php' => 'mod=admin-set');
?>
+75
View File
@@ -0,0 +1,75 @@
<?php
//验证码类
class ValidateCode {
private $charset = 'abcdefghkmnprstuvwxyzABCDEFGHKMNPRSTUVWXYZ23456789'; //随机因子
private $code; //验证码
private $codelen = 4; //验证码长度
private $width = 130; //宽度
private $height = 50; //高度
private $img; //图形资源句柄
private $font; //指定的字体
private $fontsize = 20; //指定字体大小
private $fontcolor; //指定字体颜色
//构造方法初始化
public function __construct() {
$this->font = ROOT_PATH.'/../assets/font/elephant.ttf';
}
//生成随机码
private function createCode() {
$_len = strlen($this->charset)-1;
for ($i=0;$i<$this->codelen;$i++) {
$this->code .= $this->charset[mt_rand(0,$_len)];
}
}
//生成背景
private function createBg() {
$this->img = imagecreatetruecolor($this->width, $this->height);
$color = imagecolorallocate($this->img, mt_rand(157,255), mt_rand(157,255), mt_rand(157,255));
imagefilledrectangle($this->img,0,$this->height,$this->width,0,$color);
}
//生成文字
private function createFont() {
$_x = $this->width / $this->codelen;
for ($i=0;$i<$this->codelen;$i++) {
$this->fontcolor = imagecolorallocate($this->img,mt_rand(0,156),mt_rand(0,156),mt_rand(0,156));
imagettftext($this->img,$this->fontsize,mt_rand(-30,30),$_x*$i+mt_rand(1,5),$this->height / 1.4,$this->fontcolor,$this->font,$this->code[$i]);
}
}
//生成线条、雪花
private function createLine() {
for ($i=0;$i<6;$i++) {
$color = imagecolorallocate($this->img,mt_rand(0,156),mt_rand(0,156),mt_rand(0,156));
imageline($this->img,mt_rand(0,$this->width),mt_rand(0,$this->height),mt_rand(0,$this->width),mt_rand(0,$this->height),$color);
}
for ($i=0;$i<100;$i++) {
$color = imagecolorallocate($this->img,mt_rand(200,255),mt_rand(200,255),mt_rand(200,255));
imagestring($this->img,mt_rand(1,5),mt_rand(0,$this->width),mt_rand(0,$this->height),'*',$color);
}
}
//输出
private function outPut() {
header('Content-type:image/png');
imagepng($this->img);
imagedestroy($this->img);
}
//对外生成
public function doimg() {
$this->createBg();
$this->createCode();
$this->createLine();
$this->createFont();
$this->outPut();
}
//获取验证码
public function getCode() {
return strtolower($this->code);
}
}
+4
View File
@@ -0,0 +1,4 @@
<?php
define('authcode','057c43e3a2ff4968e146db470313ed3f');
?>
+33
View File
@@ -0,0 +1,33 @@
<?php
/**
*
* 自动载入函数
*/
class Autoloader
{
/**
* 向PHP注册在自动载入函数
*/
public static function register()
{
spl_autoload_register(array(new self, 'autoload'));
}
/**
* 根据类名载入所在文件
*/
public static function autoload($className)
{
// DIRECTORY_SEPARATOR:目录分隔符,linux上就是’/ windows上是’\
$filePath = __DIR__ . DIRECTORY_SEPARATOR . $className;
$filePath = str_replace('\\', DIRECTORY_SEPARATOR, $filePath) . '.php';
if (file_exists($filePath)) {
require_once $filePath;
return true;
} else {
return false;
}
}
}
+110
View File
@@ -0,0 +1,110 @@
<?php
//error_reporting(0);
error_reporting(E_ERROR | E_PARSE | E_COMPILE_ERROR);
if(defined('IN_CRONLITE'))return;
define('VERSION', '3085');
define('DB_VERSION', '2045');
define('IN_CRONLITE', true);
define('SYSTEM_ROOT', dirname(__FILE__).'/');
define('ROOT', dirname(SYSTEM_ROOT).'/');
define('PAYPAGE_ROOT', SYSTEM_ROOT.'pages/');
define('TEMPLATE_ROOT', ROOT.'template/');
define('PLUGIN_ROOT', ROOT.'plugins/');
date_default_timezone_set('Asia/Shanghai');
$date = date("Y-m-d H:i:s");
if(!isset($nosession) || !$nosession)session_start();
if(!function_exists("is_https")){
function is_https() {
if(isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443){
return true;
}elseif(isset($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS']) == 'on' || $_SERVER['HTTPS'] == '1')){
return true;
}elseif(isset($_SERVER['HTTP_X_CLIENT_SCHEME']) && $_SERVER['HTTP_X_CLIENT_SCHEME'] == 'https'){
return true;
}elseif(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https'){
return true;
}elseif(isset($_SERVER['REQUEST_SCHEME']) && $_SERVER['REQUEST_SCHEME'] == 'https'){
return true;
}elseif(isset($_SERVER['HTTP_EWS_CUSTOME_SCHEME']) && $_SERVER['HTTP_EWS_CUSTOME_SCHEME'] == 'https'){
return true;
}
return false;
}
}
$siteurl = (is_https() ? 'https://' : 'http://').$_SERVER['HTTP_HOST'].'/';
include_once(SYSTEM_ROOT."autoloader.php");
Autoloader::register();
if($is_defend){
include_once(SYSTEM_ROOT."txprotect.php");
}
require ROOT.'config.php';
define('DBQZ', $dbconfig['dbqz']);
if(!$dbconfig['user']||!$dbconfig['pwd']||!$dbconfig['dbname'])//检测安装1
{
header('Content-type:text/html;charset=utf-8');
echo '你还没安装!<a href="/install/">点此安装</a>';
exit();
}
$DB = new \lib\PdoHelper($dbconfig);
if($DB->query("select * from pre_config where 1")==FALSE)//检测安装2
{
header('Content-type:text/html;charset=utf-8');
echo '你还没安装!<a href="/install/">点此安装</a>';
exit();
}
$CACHE=new \lib\Cache();
$conf=$CACHE->pre_fetch();
define('SYS_KEY', $conf['syskey']);
if(!$conf['localurl'])$conf['localurl'] = $siteurl;
$password_hash='!@#%!s!0';
if ($conf['version'] < DB_VERSION) {
if (!$install) {
header('Content-type:text/html;charset=utf-8');
echo '请先完成网站升级!<a href="/install/update.php"><font color=red>点此升级</font></a>';
exit;
}
}
include_once(SYSTEM_ROOT."functions.php");
include_once(SYSTEM_ROOT."member.php");
require_once SYSTEM_ROOT."vendor/autoload.php";
if (!file_exists(ROOT.'install/install.lock') && file_exists(ROOT.'install/index.php')) {
sysmsg('<h2>检测到无 install.lock 文件</h2><ul><li><font size="4">如果您尚未安装本程序,请<a href="/install/">前往安装</a></font></li><li><font size="4">如果您已经安装本程序,请手动放置一个空的 install.lock 文件到 /install 文件夹下,<b>为了您站点安全,在您完成它之前我们不会工作。</b></font></li></ul><br/><h4>为什么必须建立 install.lock 文件?</h4>它是安装保护文件,如果检测不到它,就会认为站点还没安装,此时任何人都可以安装/重装你的网站。<br/><br/>');exit;
}
if($conf['cdnpublic']==1){
$cdnpublic = '//lib.baomitu.com/';
}elseif($conf['cdnpublic']==2){
$cdnpublic = 'https://s4.zstatic.net/ajax/libs/';
}elseif($conf['cdnpublic']==4){
$cdnpublic = '//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/';
}else{
$cdnpublic = '//mirrors.sustech.edu.cn/cdnjs/ajax/libs/';
}
if(empty($conf['public_key'])){
$key_pair = generate_key_pair();
if($key_pair){
$conf['public_key'] = $key_pair['public_key'];
$conf['private_key'] = $key_pair['private_key'];
saveSetting('public_key', $conf['public_key']);
saveSetting('private_key', $conf['private_key']);
$CACHE->clear();
unset($key_pair);
}
}
?>
+8
View File
@@ -0,0 +1,8 @@
{
"require": {
"cccyun/alipay-sdk": "^1.1",
"cccyun/wechatpay-sdk": "^1.0",
"cccyun/qqpay-sdk": "^1.0",
"lpilp/guomi": "^1.0"
}
}
File diff suppressed because it is too large Load Diff
+116
View File
@@ -0,0 +1,116 @@
<?php
namespace lib;
class AliyunCertify {
private $AccessKeyId;
private $AccessKeySecret;
private $Endpoint = 'saf.cn-shanghai.aliyuncs.com'; //API接入域名
private $Version = '2017-03-31'; //API版本号
private $SceneId;
function __construct($AccessKeyId, $AccessKeySecret, $SceneId){
$this->AccessKeyId = $AccessKeyId;
$this->AccessKeySecret = $AccessKeySecret;
$this->SceneId = $SceneId;
}
//身份认证初始化服务
public function initialize($outer_order_no, $cert_name, $cert_no, $return_url) {
$params = [
'method' => 'init',
'sceneId' => $this->SceneId,
'outerOrderNo' => $outer_order_no,
'bizCode' => 'FACE_SDK',
'identityType' => 'CERT_INFO',
'certType' => 'IDENTITY_CARD',
'certNo' => $cert_no,
'certName' => $cert_name,
'returnUrl' => $return_url
];
$ServiceParameters = json_encode($params);
return $this->ExecuteRequest($ServiceParameters);
}
//身份认证记录查询
public function query($certify_id) {
$params = [
'method' => 'query',
'certifyId' => $certify_id,
'sceneId' => $this->SceneId
];
$ServiceParameters = json_encode($params);
return $this->ExecuteRequest($ServiceParameters);
}
//执行请求
private function ExecuteRequest($ServiceParameters){
$param = ['Action' => 'ExecuteRequest', 'Service' => 'fin_face_verify', 'ServiceParameters' => $ServiceParameters];
return $this->request($param, true);
}
//签名方法
private function aliyunSignature($parameters, $accessKeySecret, $method)
{
ksort($parameters);
$canonicalizedQueryString = '';
foreach ($parameters as $key => $value) {
if($value === null) continue;
$canonicalizedQueryString .= '&' . $this->percentEncode($key) . '=' . $this->percentEncode($value);
}
$stringToSign = $method . '&%2F&' . $this->percentencode(substr($canonicalizedQueryString, 1));
$signature = base64_encode(hash_hmac("sha1", $stringToSign, $accessKeySecret . "&", true));
return $signature;
}
private function percentEncode($str)
{
$search = ['+', '*', '%7E'];
$replace = ['%20', '%2A', '~'];
return str_replace($search, $replace, urlencode($str));
}
//请求方法(当需要返回列表等数据时,returnData=true
private function request($param, $returnData=false){
if(empty($this->AccessKeyId)||empty($this->AccessKeySecret))return false;
$url='https://'.$this->Endpoint.'/';
$data=array(
'Format' => 'JSON',
'Version' => $this->Version,
'AccessKeyId' => $this->AccessKeyId,
'SignatureMethod' => 'HMAC-SHA1',
'Timestamp' => gmdate('Y-m-d\TH:i:s\Z'),
'SignatureVersion' => '1.0',
'SignatureNonce' => $this->random(8));
$data=array_merge($data, $param);
$data['Signature'] = $this->aliyunSignature($data, $this->AccessKeySecret, 'POST');
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
$json=curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$arr=json_decode($json,true);
if($returnData==true){
return $arr;
}else{
if($httpCode==200){
return true;
}else{
return $arr['Message'];
}
}
}
private function random($length, $numeric = 0) {
$seed = base_convert(md5(microtime().$_SERVER['DOCUMENT_ROOT']), 16, $numeric ? 10 : 35);
$seed = $numeric ? (str_replace('0', '', $seed).'012340567890') : ($seed.'zZ'.strtoupper($seed));
$hash = '';
$max = strlen($seed) - 1;
for($i = 0; $i < $length; $i++) {
$hash .= $seed[mt_rand(0, $max)];
}
return $hash;
}
}
+127
View File
@@ -0,0 +1,127 @@
<?php
namespace lib;
use Exception;
class AliyunRecognize
{
private $AccessKeyId;
private $AccessKeySecret;
private $Endpoint = 'ocr-api.cn-hangzhou.aliyuncs.com'; //API接入域名
private $Version = '2021-07-07'; //API版本号
function __construct($AccessKeyId, $AccessKeySecret){
$this->AccessKeyId = $AccessKeyId;
$this->AccessKeySecret = $AccessKeySecret;
}
//身份证识别
public function RecognizeIdcard($file_path){
$arr = $this->request(__FUNCTION__, file_get_contents($file_path), true);
return json_decode($arr['Data'],true);
}
//护照识别
public function RecognizePassport($file_path){
$arr = $this->request(__FUNCTION__, file_get_contents($file_path), true);
return json_decode($arr['Data'],true);
}
//银行卡识别
public function RecognizeBankCard($file_path){
$arr = $this->request(__FUNCTION__, file_get_contents($file_path), true);
return json_decode($arr['Data'],true);
}
//营业执照识别
public function RecognizeBusinessLicense($file_path){
$arr = $this->request(__FUNCTION__, file_get_contents($file_path), true);
return json_decode($arr['Data'],true);
}
//银行开户许可证识别
public function RecognizeBankAccountLicense($file_path){
$arr = $this->request(__FUNCTION__, file_get_contents($file_path), true);
return json_decode($arr['Data'],true);
}
//签名方法
private function aliyunSignature($parameters, $accessKeySecret, $method)
{
ksort($parameters);
$canonicalizedQueryString = '';
foreach ($parameters as $key => $value) {
if($value === null || $value instanceof \CURLFile) continue;
$canonicalizedQueryString .= '&' . $this->percentEncode($key) . '=' . $this->percentEncode($value);
}
$stringToSign = $method . '&%2F&' . $this->percentencode(substr($canonicalizedQueryString, 1));
$signature = base64_encode(hash_hmac("sha1", $stringToSign, $accessKeySecret . "&", true));
return $signature;
}
private function percentEncode($str)
{
$search = ['+', '*', '%7E'];
$replace = ['%20', '%2A', '~'];
return str_replace($search, $replace, urlencode($str));
}
//请求方法(当需要返回列表等数据时,returnData=true
private function request($action, $file = null, $returnData=false){
if(empty($this->AccessKeyId)||empty($this->AccessKeySecret))return false;
$url='https://'.$this->Endpoint.'/';
$data=array(
'Action' => $action,
'Format' => 'JSON',
'Version' => $this->Version,
'AccessKeyId' => $this->AccessKeyId,
'SignatureMethod' => 'HMAC-SHA1',
'Timestamp' => gmdate('Y-m-d\TH:i:s\Z'),
'SignatureVersion' => '1.0',
'SignatureNonce' => $this->random(8));
$data['Signature'] = $this->aliyunSignature($data, $this->AccessKeySecret, 'POST');
$url.='?'.http_build_query($data);
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
if($file !== null){
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/octet-stream']);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $file);
}
$json=curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if($httpCode>=200 && $httpCode<300){
if($returnData==true){
$arr = json_decode($json,true);
if(!$arr) throw new Exception('无法解析返回数据');
return $arr;
}else{
return true;
}
}else{
$arr=json_decode($json,true);
if(isset($arr['Code']) && isset($arr['Message'])){
if(strpos($arr['Message'],' server string to sign is:')!==false){
$arr['Message'] = substr($arr['Message'],0,strpos($arr['Message'],' server string to sign is:')-1);
}
throw new Exception('['.$arr['Code'].'] '.$arr['Message']);
}else{
throw new Exception('无法解析返回数据');
}
}
}
private function random($length, $numeric = 0) {
$seed = base_convert(md5(microtime().$_SERVER['DOCUMENT_ROOT']), 16, $numeric ? 10 : 35);
$seed = $numeric ? (str_replace('0', '', $seed).'012340567890') : ($seed.'zZ'.strtoupper($seed));
$hash = '';
$max = strlen($seed) - 1;
for($i = 0; $i < $length; $i++) {
$hash .= $seed[mt_rand(0, $max)];
}
return $hash;
}
}
+77
View File
@@ -0,0 +1,77 @@
<?php
namespace lib;
use Exception;
class ApiHelper
{
//无需签名验证的接口
private static $exclude_list = [
'pay/submit',
'pay/create',
'complain/image'
];
public static function load_api($s){
if(preg_match('/^(.[a-zA-Z0-9\_]+)\/(.[a-zA-Z0-9\_]+)$/',$s, $matchs)){
$class = $matchs[1];
$func = $matchs[2];
$classname = '\\lib\\api\\'.ucfirst($class).'';
if (class_exists($classname) && method_exists($classname, $func)) {
try{
if(in_array($class.'/'.$func, self::$exclude_list)){
$classname::$func();
}else{
self::verify();
$result = $classname::$func();
$result['timestamp'] = time().'';
$result['sign_type'] = 'RSA';
$result['sign'] = \lib\Payment::makeSign($result, null);
echojson($result);
}
}catch(Exception $e){
$code = $e->getCode();
echojsonmsg($e->getMessage(), $code != 0 ? $code : -1);
}
}else{
echojsonmsg('接口方法不存在', -5);
}
}else{
echojsonmsg('URL Error!', -5);
}
}
private static function verify(){
global $DB, $conf, $userrow, $queryArr;
if(isset($_POST['pid'])){
$queryArr=$_POST;
}else{
throw new Exception('未传入任何参数', -4);
}
$pid=intval($queryArr['pid']);
if(empty($pid))throw new Exception('商户ID不能为空');
$userrow=$DB->getRow("SELECT `uid`,`gid`,`key`,`money`,`channelinfo`,`keytype`,`publickey`,`status`,`pay`,`settle`,`refund`,`transfer` FROM `pre_user` WHERE `uid`='{$pid}' LIMIT 1");
if(!$userrow)throw new Exception('商户不存在!');
if($userrow['status']==0)throw new Exception('商户已被封禁');
try{
self::api_verify($userrow, $queryArr);
}catch(Exception $e){
throw new Exception($e->getMessage(), -3);
}
}
//API签名校验
static public function api_verify($userrow, $queryArr, $forceRsa = false){
if($forceRsa && $queryArr['sign_type'] != 'RSA')throw new Exception('该接口只能使用RSA签名类型');
if($userrow['keytype'] == 1 && $queryArr['sign_type'] != 'RSA')throw new Exception('该商户只能使用RSA签名类型');
if(defined('API_INIT') || $forceRsa){
if(empty($queryArr['timestamp']))throw new Exception('时间戳(timestamp)字段不能为空');
if(abs(time() - $queryArr['timestamp']) > 300)throw new Exception('时间戳字段不正确,请检查服务器时间');
}
$sign_type = $queryArr['sign_type'] ? $queryArr['sign_type'] : 'MD5';
if(!\lib\Payment::verifySign($queryArr, $userrow['key'], $userrow['publickey']))throw new Exception($sign_type.'签名校验失败');
}
}
+52
View File
@@ -0,0 +1,52 @@
<?php
namespace lib;
class Cache {
public function get($key) {
global $_CACHE;
return $_CACHE[$key];
}
public function read($key = 'config') {
global $DB;
$value = $DB->getColumn("SELECT v FROM pre_cache WHERE k=:key LIMIT 1", [':key'=>$key]);
return $value;
}
public function save($key ,$value, $expire=0) {
if (is_array($value)) $value = serialize($value);
global $DB;
if($expire) $expire = time() + $expire;
return $DB->exec("REPLACE INTO pre_cache VALUES (:key, :value, :expire)", [':key'=>$key, ':value'=>$value, ':expire'=>$expire]);
}
public function pre_fetch(){
global $_CACHE;
$_CACHE=array();
$cache = $this->read('config');
$_CACHE = @unserialize($cache);
if(empty($_CACHE['version']))$_CACHE = $this->update();
return $_CACHE;
}
public function update() {
global $DB;
$cache = array();
$result = $DB->getAll("SELECT * FROM pre_config");
foreach($result as $row){
$cache[ $row['k'] ] = $row['v'];
}
$this->save('config', $cache);
return $cache;
}
public function clear($key = 'config') {
global $DB;
return $DB->exec("UPDATE pre_cache SET v='' WHERE k=:key", [':key'=>$key]);
}
public function delete($key) {
global $DB;
return $DB->exec("DELETE FROM pre_cache WHERE k=:key", [':key'=>$key]);
}
public function clean() {
global $DB;
$DB->exec("DELETE FROM pre_cache WHERE expire>0 AND expire<'".time()."'");
//$DB->exec("OPTIMIZE TABLE pre_cache");
return true;
}
}
+341
View File
@@ -0,0 +1,341 @@
<?php
namespace lib;
class Channel {
static public function get($id, $channelinfo=null){
global $DB;
$value=$DB->getRow("SELECT * FROM pre_channel WHERE id='$id' LIMIT 1");
if(!$value) return null;
$channel = ['id'=>$value['id'], 'name'=>$value['name'], 'mode'=>$value['mode'], 'type'=>$value['type'], 'plugin'=>$value['plugin'], 'apptype'=>$value['apptype'], 'appwxmp'=>$value['appwxmp'], 'appwxa'=>$value['appwxa'], 'costrate'=>$value['costrate'], 'daytop'=>$value['daytop']];
$config = json_decode($value['config'], true);
if(!empty($channelinfo) && !empty($config)){
$arr = json_decode($channelinfo, true);
foreach($config as $configkey => $configrow){
if($configrow && substr($configrow, 0, 1) == '['){
$key = substr($configrow,1,-1);
$config[$configkey] = $arr[$key];
}
}
}
if(!empty($config)){
$channel = array_merge($channel, $config);
}
return $channel;
}
static public function getSub($id){
global $DB;
$value=$DB->getRow("SELECT A.*,B.info,B.id subid FROM pre_subchannel B INNER JOIN pre_channel A ON B.channel=A.id WHERE B.id='$id'");
if(!$value) return null;
$channel = ['id'=>$value['id'], 'subid'=>$value['subid'], 'name'=>$value['name'], 'mode'=>$value['mode'], 'type'=>$value['type'], 'plugin'=>$value['plugin'], 'apptype'=>$value['apptype'], 'appwxmp'=>$value['appwxmp'], 'appwxa'=>$value['appwxa'], 'costrate'=>$value['costrate'], 'daytop'=>$value['daytop']];
$config = json_decode($value['config'], true);
if(!empty($value['info']) && !empty($config)){
$arr = json_decode($value['info'], true);
foreach($config as $configkey => $configrow){
if($configrow && substr($configrow, 0, 1) == '['){
$key = substr($configrow,1,-1);
$config[$configkey] = $arr[$key];
}
}
if(isset($arr['apptype']) && !empty($arr['apptype'])){
$channel['apptype'] = $arr['apptype'];
}
if(isset($arr['appwxmp']) && $arr['appwxmp']>0){
$channel['appwxmp'] = $arr['appwxmp'];
$channel['subappwxmp'] = 1;
}
if(isset($arr['appwxa']) && $arr['appwxa']>0){
$channel['appwxa'] = $arr['appwxa'];
$channel['subappwxa'] = 1;
}
}
if(!empty($config)){
$channel = array_merge($channel, $config);
}
return $channel;
}
static public function getGroup($gid){
global $DB;
$group=$DB->getRow("SELECT * FROM pre_group WHERE gid='{$gid}' LIMIT 1");
if(!$group)$group=$DB->getRow("SELECT * FROM pre_group WHERE gid=0 LIMIT 1");
$info = json_decode($group['info'],true);
$rows = $DB->getAll("SELECT * FROM pre_type WHERE status=1 ORDER BY id ASC");
$paytype = [];
foreach($rows as $row){
$paytype[$row['id']] = $row['name'];
}
$subchannel_type = [];
foreach($info as $id=>$row){
if(!isset($paytype[$id]))continue;
if($row['channel'] == -2){
$subchannel_type[] = $paytype[$id];
}
}
$group['subchannel_type'] = $subchannel_type;
return $group;
}
static public function info($id, $gid = 0){
global $DB;
$value=$DB->getRow("SELECT id,plugin,type,rate,apptype,mode,paymin,paymax FROM pre_channel WHERE id='$id' LIMIT 1");
$money_rate = $value['rate'];
if($gid>0)$groupinfo=$DB->getColumn("SELECT info FROM pre_group WHERE gid='$gid' LIMIT 1");
if(!$groupinfo)$groupinfo=$DB->getColumn("SELECT info FROM pre_group WHERE gid=0 LIMIT 1");
if($groupinfo){
$info = json_decode($groupinfo,true);
$groupinfo = $info[$value['type']];
if(is_array($groupinfo) && !empty($groupinfo['rate'])){
$money_rate = $groupinfo['rate'];
}
}
return ['typeid'=>$value['type'], 'plugin'=>$value['plugin'], 'channel'=>$value['id'], 'rate'=>$money_rate, 'apptype'=>$value['apptype'], 'mode'=>$value['mode'], 'paymin'=>$value['paymin'], 'paymax'=>$value['paymax']];
}
static public function getWeixin($id){
global $DB;
$value=$DB->getRow("SELECT * FROM pre_weixin WHERE id='$id' LIMIT 1");
return $value;
}
// 支付提交处理(输入支付方式名称)
static public function submit($type, $uid=0, $gid=0, $money=0, $device=null){
global $DB;
if($device == 'mobile' || $device == 'qq' || $device == 'wechat' || $device == 'alipay' || checkmobile()==true){
$sqls = " AND (device=0 OR device=2)";
}else{
$sqls = " AND (device=0 OR device=1)";
}
$paytype=$DB->getRow("SELECT id,name,status FROM pre_type WHERE name=:type{$sqls} LIMIT 1", [':type'=>$type]);
if(!$paytype || $paytype['status']==0)sysmsg('支付方式(type)不存在');
$typeid = $paytype['id'];
$typename = $paytype['name'];
return self::getSubmitInfo($typeid, $typename, $uid, $gid, $money);
}
// 支付提交处理2(输入支付方式ID)
static public function submit2($typeid, $uid=0, $gid=0, $money=0){
global $DB;
$paytype=$DB->getRow("SELECT id,name,status FROM pre_type WHERE id='$typeid' LIMIT 1");
if(!$paytype || $paytype['status']==0)sysmsg('支付方式(type)不存在');
$typename = $paytype['name'];
return self::getSubmitInfo($typeid, $typename, $uid, $gid, $money);
}
//获取通道、插件、费率信息
static public function getSubmitInfo($typeid, $typename, $uid, $gid, $money){
global $DB;
if($gid>0)$groupinfo=$DB->getColumn("SELECT info FROM pre_group WHERE gid='$gid' LIMIT 1");
if(!$groupinfo)$groupinfo=$DB->getColumn("SELECT info FROM pre_group WHERE gid=0 LIMIT 1");
if($groupinfo){
$info = json_decode($groupinfo,true);
$groupinfo = $info[$typeid];
if(is_array($groupinfo)){
$channel = $groupinfo['channel'];
$money_rate = $groupinfo['rate'];
}
else{
$channel = -1;
$money_rate = null;
}
if($channel==0){ //当前商户关闭该通道
return false;
}
elseif($channel==-1){ //随机可用通道
$rows=$DB->getAll("SELECT id,plugin,status,rate,apptype,mode,paymin,paymax FROM pre_channel WHERE type='$typeid' AND status=1 AND daystatus=0");
if(count($rows)>0){
$newrows = [];
foreach($rows as $row){
if($money>0 && !empty($row['paymin']) && $row['paymin']>0 && $money<$row['paymin'])continue;
if($money>0 && !empty($row['paymax']) && $row['paymax']>0 && $money>$row['paymax'])continue;
$newrows[] = $row;
}
if(count($newrows)>0){
$row = $newrows[array_rand($newrows)];
}else{
$row = $rows[array_rand($rows)];
}
if(empty($money_rate))$money_rate = $row['rate'];
return ['typeid'=>$typeid, 'typename'=>$typename, 'plugin'=>$row['plugin'], 'channel'=>$row['id'], 'subchannel'=>0, 'rate'=>$money_rate, 'apptype'=>$row['apptype'], 'mode'=>$row['mode'], 'paymin'=>$row['paymin'], 'paymax'=>$row['paymax']];
}
}
elseif($channel==-2){ //用户自定义子通道
$rows=$DB->getAll("SELECT A.id,plugin,A.status,rate,apptype,mode,paymin,paymax,B.id subid FROM pre_subchannel B INNER JOIN pre_channel A ON B.channel=A.id WHERE B.uid='$uid' AND A.type='$typeid' AND A.status=1 AND B.status=1 AND daystatus=0 ORDER BY B.usetime ASC");
if(count($rows)>0){
$newrows = [];
foreach($rows as $row){
if($money>0 && !empty($row['paymin']) && $row['paymin']>0 && $money<$row['paymin'])continue;
if($money>0 && !empty($row['paymax']) && $row['paymax']>0 && $money>$row['paymax'])continue;
$newrows[] = $row;
}
if(count($newrows)>0){
$row = $newrows[0];
}else{
$row = $rows[0];
}
if(empty($money_rate))$money_rate = $row['rate'];
$DB->exec("UPDATE pre_subchannel SET usetime=NOW() WHERE id='{$row['subid']}'");
return ['typeid'=>$typeid, 'typename'=>$typename, 'plugin'=>$row['plugin'], 'channel'=>$row['id'], 'subchannel'=>$row['subid'], 'rate'=>$money_rate, 'apptype'=>$row['apptype'], 'mode'=>$row['mode'], 'paymin'=>$row['paymin'], 'paymax'=>$row['paymax']];
}
}
else{
if($groupinfo['type']=='roll'){ //解析轮询组
$channel = self::getChannelFromRoll($channel, $money);
if(!$channel || $channel==0){ //当前轮询组未开启
return false;
}
}
//获取轮询组对应通道
$row=$DB->getRow("SELECT plugin,status,rate,apptype,mode,paymin,paymax FROM pre_channel WHERE id='$channel' LIMIT 1");
if($row['status']==1 && $row['daystatus']==0){
if(empty($money_rate))$money_rate = $row['rate'];
return ['typeid'=>$typeid, 'typename'=>$typename, 'plugin'=>$row['plugin'], 'channel'=>$channel, 'subchannel'=>0, 'rate'=>$money_rate, 'apptype'=>$row['apptype'], 'mode'=>$row['mode'], 'paymin'=>$row['paymin'], 'paymax'=>$row['paymax']];
}
}
}else{
//未设置用户组
$row=$DB->getRow("SELECT id,plugin,status,rate,apptype,mode,paymin,paymax FROM pre_channel WHERE type='$typeid' AND status=1 AND daystatus=0 ORDER BY rand() LIMIT 1");
if($row){
return ['typeid'=>$typeid, 'typename'=>$typename, 'plugin'=>$row['plugin'], 'channel'=>$row['id'], 'subchannel'=>0, 'rate'=>$row['rate'], 'apptype'=>$row['apptype'], 'mode'=>$row['mode'], 'paymin'=>$row['paymin'], 'paymax'=>$row['paymax']];
}
}
return false;
}
// 获取当前商户可用支付方式
static public function getTypes($uid, $gid=0){
global $DB;
if(checkmobile()==true){
$sqls = " AND (device=0 OR device=2)";
}else{
$sqls = " AND (device=0 OR device=1)";
}
$rows = $DB->getAll("SELECT * FROM pre_type WHERE status=1{$sqls} ORDER BY id ASC");
$paytype = [];
foreach($rows as $row){
$paytype[$row['id']] = $row;
}
if($gid>0)$groupinfo=$DB->getColumn("SELECT info FROM pre_group WHERE gid='$gid' LIMIT 1");
if(!$groupinfo)$groupinfo=$DB->getColumn("SELECT info FROM pre_group WHERE gid=0 LIMIT 1");
if($groupinfo){
$info = json_decode($groupinfo,true);
foreach($info as $id=>$row){
if(!isset($paytype[$id]))continue;
if($row['channel']==0){
unset($paytype[$id]);
}elseif($row['channel']==-1){
$channel=$DB->getRow("SELECT rate,status FROM pre_channel WHERE type='$id' AND status=1 LIMIT 1");
if(!$channel){
unset($paytype[$id]);
}elseif(empty($row['rate'])){
$paytype[$id]['rate']=$channel['rate'];
}else{
$paytype[$id]['rate']=$row['rate'];
}
}elseif($row['channel']==-2){
$channel=$DB->getRow("SELECT A.id,A.status,rate FROM pre_subchannel B INNER JOIN pre_channel A ON B.channel=A.id WHERE B.uid='$uid' AND A.type='$id' AND A.status=1 AND B.status=1 LIMIT 1");
if(!$channel){
unset($paytype[$id]);
}elseif(empty($row['rate'])){
$paytype[$id]['rate']=$channel['rate'];
}else{
$paytype[$id]['rate']=$row['rate'];
}
}else{
if($row['type']=='roll'){
$status=$DB->getColumn("SELECT status FROM pre_roll WHERE id='{$row['channel']}' LIMIT 1");
}else{
$status=$DB->getColumn("SELECT status FROM pre_channel WHERE id='{$row['channel']}' LIMIT 1");
}
if(!$status || $status==0)unset($paytype[$id]);
else $paytype[$id]['rate']=$row['rate'];
}
}
}else{
foreach($paytype as $id=>$row){
$status=$DB->getColumn("SELECT status FROM pre_channel WHERE type='$id' AND status=1 limit 1");
if(!$status || $status==0)unset($paytype[$id]);
else{
$paytype[$id]['rate']=$DB->getColumn("SELECT rate FROM pre_channel WHERE type='$id' AND status=1 limit 1");
}
}
}
return $paytype;
}
//根据轮询组ID获取支付通道ID
static private function getChannelFromRoll($channel, $money){
global $DB;
$row=$DB->getRow("SELECT * FROM pre_roll WHERE id='$channel' LIMIT 1");
if($row['status']==1){
$info = self::rollinfo_decode($row['info'],true);
//先根据支付金额与限额过滤可用支付通道
$channelids = [];
foreach($info as $inforow){
$channelids[] = $inforow['name'];
}
$channelids = implode(',',$channelids);
$rows=$DB->getAll("SELECT id,paymin,paymax FROM pre_channel WHERE id IN ($channelids) AND status=1 AND daystatus=0");
$newids = [];
foreach($rows as $channelrow){
if($money>0 && !empty($channelrow['paymin']) && $channelrow['paymin']>0 && $money<$channelrow['paymin'])continue;
if($money>0 && !empty($channelrow['paymax']) && $channelrow['paymax']>0 && $money>$channelrow['paymax'])continue;
$newids[] = $channelrow['id'];
}
if(count($newids)==0)return false;
$newinfo = [];
foreach($info as $inforow){
if(in_array($inforow['name'], $newids))$newinfo[]=$inforow;
}
if($row['kind']==2){
return $newids[0];
}elseif($row['kind']==1){
$channel = self::random_weight($newinfo);
}else{
$channel = $newinfo[$row['index']]['name'];
$index = ($row['index'] + 1) % count($newinfo);
$DB->exec("UPDATE pre_roll SET `index`='$index' WHERE id='{$row['id']}'");
}
return $channel;
}
return false;
}
//解析轮询组info
static private function rollinfo_decode($content){
$result = [];
$arr = explode(',',$content);
foreach($arr as $row){
$a = explode(':',$row);
$result[] = ['name'=>$a[0], 'weight'=>$a[1]];
}
return $result;
}
//加权随机
static private function random_weight($arr){
$weightSum = 0;
foreach ($arr as $value) {
$weightSum += intval($value['weight']);
}
if($weightSum<=0)return false;
$randNum = mt_rand(1, $weightSum);
foreach ($arr as $v) {
if ($randNum <= $v['weight']) {
return $v['name'];
}
$randNum -=$v['weight'];
}
}
}
+194
View File
@@ -0,0 +1,194 @@
<?php
namespace lib;
/**
* 极验3.0 lib
*/
class GeetestLib
{
const SDK_VERSION = 'php_3.0.0';
const JSON_FORMAT = "1";
private $geetest_id;
private $geetest_key;
public function __construct($geetest_id, $geetest_key) {
$this->geetest_id = $geetest_id;
$this->geetest_key = $geetest_key;
}
//验证初始化
public function pre_process($params) {
if(!empty($this->geetest_id) && !empty($this->geetest_key)){
return $this->pre_process_api($params);
}else{
return $this->pre_process_demo($params);
}
}
private function pre_process_api($params) {
$public_params = [
'digestmod' => 'md5',
'gt' => $this->geetest_id,
'sdk' => self::SDK_VERSION,
'json_format' => self::JSON_FORMAT
];
$params = array_merge($params, $public_params);
$url = 'http://api.geetest.com/register.php?' . http_build_query($params);
$res = get_curl($url);
$arr = json_decode($res, true);
if($arr && isset($arr['challenge'])){
return $this->success_process($arr['challenge']);
}else{
return $this->failback_process();
}
}
private function success_process($challenge) {
$challenge = md5($challenge . $this->geetest_key);
$result = array(
'success' => 1,
'gt' => $this->geetest_id,
'challenge' => $challenge,
'new_captcha'=>true
);
return $result;
}
private function failback_process() {
$challenge = md5(uniqid(mt_rand(), true) . microtime());
$result = array(
'success' => 0,
'gt' => !empty($this->geetest_id) ? $this->geetest_id : 'e10adc3949ba59abbe56e057f20f883e',
'challenge' => $challenge,
'new_captcha'=>true
);
return $result;
}
private function pre_process_demo($params) {
$url = 'https://www.geetest.com/demo/gt/register-fullpage?t=' . time() . "123";
$referer = 'https://www.geetest.com/demo/slide-popup.html';
$data = get_curl($url, 0, $referer);
$arr = json_decode($data, true);
if($arr && isset($arr['challenge'])){
return $arr;
}else{
return $this->failback_process();
}
}
//正常流程下(即验证初始化成功),二次验证
public function success_validate($challenge, $validate, $seccode, $params) {
if(!empty($this->geetest_id) && !empty($this->geetest_key)){
return $this->success_validate_api($challenge, $validate, $seccode, $params);
}else{
return $this->success_validate_demo($challenge, $validate, $seccode);
}
}
private function success_validate_api($challenge, $validate, $seccode, $params) {
if (!$this->check_validate($challenge, $validate)) {
return false;
}
$public_params = [
'seccode' => $seccode,
'challenge' => $challenge,
'captchaid' => $this->geetest_id,
'sdk' => self::SDK_VERSION,
'json_format' => self::JSON_FORMAT
];
$params = array_merge($params, $public_params);
$url = 'http://api.geetest.com/validate.php';
$res = get_curl($url, http_build_query($params));
$arr = json_decode($res, true);
if($arr && isset($arr['seccode'])){
if($arr['seccode'] == md5($seccode)){
return true;
}
}
return false;
}
private function check_validate($challenge, $validate) {
if (strlen($validate) != 32) {
return false;
}
if (md5($this->geetest_key . 'geetest' . $challenge) != $validate) {
return false;
}
return true;
}
private function success_validate_demo($challenge, $validate, $seccode) {
$params = [
'geetest_challenge' => $challenge,
'geetest_validate' => $validate,
'geetest_seccode' => $seccode
];
$url = 'https://www.geetest.com/demo/gt/validate-fullpage';
$referer = 'https://www.geetest.com/demo/slide-popup.html';
$data = get_curl($url, http_build_query($params), $referer);
$arr = json_decode($data, true);
if($arr && $arr['status'] == 'success'){
return true;
}
return false;
}
//异常流程下(即验证初始化失败,宕机模式),二次验证
public function fail_validate($challenge, $validate, $seccode) {
if(md5($challenge) == $validate){
return true;
}else{
return false;
}
}
public function gt4_validate($captcha_id, $lot_number, $pass_token, $gen_time, $captcha_output) {
if(!empty($this->geetest_id) && !empty($this->geetest_key)){
return $this->gt4_validate_api($captcha_id, $lot_number, $pass_token, $gen_time, $captcha_output);
}else{
return $this->gt4_validate_demo($captcha_id, $lot_number, $pass_token, $gen_time, $captcha_output);
}
}
private function gt4_validate_api($captcha_id, $lot_number, $pass_token, $gen_time, $captcha_output){
$url = 'http://gcaptcha4.geetest.com/validate?captcha_id='.$captcha_id;
$param = [
'lot_number' => $lot_number,
'pass_token' => $pass_token,
'gen_time' => $gen_time,
'captcha_output' => $captcha_output
];
$param['sign_token'] = hash_hmac('sha256', $param['lot_number'], $this->geetest_key);
$data = get_curl($url, http_build_query($param));
$arr = json_decode($data, true);
if(isset($arr['status']) && $arr['status']=='success'){
if(isset($arr['result']) && $arr['result'] == 'success'){
return true;
}
}
return false;
}
private function gt4_validate_demo($captcha_id, $lot_number, $pass_token, $gen_time, $captcha_output){
global $clientip;
$url = 'http://gt4.geetest.com/demov4/demo/login';
$param = [
'captcha_id' => $captcha_id,
'lot_number' => $lot_number,
'pass_token' => $pass_token,
'gen_time' => $gen_time,
'captcha_output' => $captcha_output
];
$referer = 'http://gt4.geetest.com/demov4/invisible-bind-zh.html';
$httpheader[] = "X-Real-IP: ".$clientip;
$httpheader[] = "X-Forwarded-For: ".$clientip;
$data = get_curl($url.'?'.http_build_query($param),0,$referer,0,0,0,0,$httpheader);
$arr = json_decode($data, true);
if(isset($arr['result']) && $arr['result'] == 'success'){
return true;
}
return false;
}
}
+172
View File
@@ -0,0 +1,172 @@
<?php
namespace lib;
use Exception;
class MsgNotice
{
public static function send($scene, $uid, $param){
global $DB, $conf;
$scene_all = ['complain', 'mchrisk'];
if($uid == 0){
if(in_array($scene, $scene_all)){
$switch = self::getMessageSwitch($scene.'_all');
}else{
$switch = self::getMessageSwitch($scene);
}
if($switch == 1){
$receiver = $conf['mail_recv']?$conf['mail_recv']:$conf['mail_name'];
return self::send_mail_msg($scene, $receiver, $param);
}
}else{
$userrow = $DB->find('user', 'phone,email,wx_uid,msgconfig', ['uid'=>$uid]);
$userrow['msgconfig'] = unserialize($userrow['msgconfig']);
if($scene == 'order' && $userrow['msgconfig']['order_money']>0 && $param['money']<$userrow['msgconfig']['order_money']) return false;
if($scene == 'balance') $param['msgmoney'] = $userrow['msgconfig']['balance_money'];
if($userrow['msgconfig'][$scene] == 1 && !empty($userrow['wx_uid'])){
return self::send_wechat_tplmsg($scene, $userrow['wx_uid'], $param);
}elseif($userrow['msgconfig'][$scene] == 2 && !empty($userrow['email']) && self::getMessageSwitch($scene) == 1){
return self::send_mail_msg($scene, $userrow['email'], $param);
}elseif($userrow['msgconfig'][$scene] == 3 && !empty($userrow['phone'])){
if($scene == 'balance'){
$tpl_code = $conf['sms_tpl_balance'];
$tpl_param = ['code'=>$param['msgmoney']];
}elseif($scene == 'complain'){
$tpl_code = $conf['sms_tpl_complain'];
$tpl_param = ['code'=>$param['trade_no']];
}
if(!empty($tpl_code)){
return send_sms_common($userrow['phone'], $tpl_code, $tpl_param);
}
}
if(in_array($scene, $scene_all)){
$switch = self::getMessageSwitch($scene.'_all');
if($switch == 1){
$receiver = $conf['mail_recv']?$conf['mail_recv']:$conf['mail_name'];
return self::send_mail_msg($scene, $receiver, $param);
}
}
}
return false;
}
public static function send_wechat_tplmsg($scene, $openid, $param){
global $conf, $siteurl, $CACHE;
$wid = $conf['login_wx'];
if($scene == 'order'){
$template_id = $conf['wxnotice_tpl_order'];
if(strlen($param['out_trade_no']) > 32) $param['out_trade_no'] = substr($param['out_trade_no'], 0, 32);
if(mb_strlen($param['name']) > 20) $param['name'] = mb_substr($param['name'], 0, 20);
$data = [];
if($conf['wxnotice_tpl_order_no']) $data[$conf['wxnotice_tpl_order_no']] = ['value'=>$param['trade_no']];
if($conf['wxnotice_tpl_order_name']) $data[$conf['wxnotice_tpl_order_name']] = ['value'=>$param['name']];
if($conf['wxnotice_tpl_order_money']) $data[$conf['wxnotice_tpl_order_money']] = ['value'=>'¥'.$param['money']];
if($conf['wxnotice_tpl_order_time']) $data[$conf['wxnotice_tpl_order_time']] = ['value'=>$param['time']];
if($conf['wxnotice_tpl_order_outno']) $data[$conf['wxnotice_tpl_order_outno']] = ['value'=>$param['out_trade_no']];
$jumpurl = $siteurl.'user/order.php';
}elseif($scene == 'settle'){
$template_id = $conf['wxnotice_tpl_settle'];
$data = [];
if($conf['wxnotice_tpl_settle_type']) $data[$conf['wxnotice_tpl_settle_type']] = ['value'=>'结算成功'];
if($conf['wxnotice_tpl_settle_account']) $data[$conf['wxnotice_tpl_settle_account']] = ['value'=>$param['account']];
if($conf['wxnotice_tpl_settle_money']) $data[$conf['wxnotice_tpl_settle_money']] = ['value'=>'¥'.$param['money']];
if($conf['wxnotice_tpl_settle_realmoney']) $data[$conf['wxnotice_tpl_settle_realmoney']] = ['value'=>'¥'.$param['realmoney']];
if($conf['wxnotice_tpl_settle_time']) $data[$conf['wxnotice_tpl_settle_time']] = ['value'=>$param['time']];
$jumpurl = isset($param['jumpurl']) ? $param['jumpurl'] : $siteurl.'user/settle.php';
}elseif($scene == 'login'){
$template_id = $conf['wxnotice_tpl_login'];
$data = [];
if($conf['wxnotice_tpl_login_user']) $data[$conf['wxnotice_tpl_login_user']] = ['value'=>$param['user']];
if($conf['wxnotice_tpl_login_time']) $data[$conf['wxnotice_tpl_login_time']] = ['value'=>$param['time']];
if($conf['wxnotice_tpl_login_name']) $data[$conf['wxnotice_tpl_login_name']] = ['value'=>$conf['sitename']];
if($conf['wxnotice_tpl_login_ip']) $data[$conf['wxnotice_tpl_login_ip']] = ['value'=>$param['clientip']];
if($conf['wxnotice_tpl_login_iploc']) $data[$conf['wxnotice_tpl_login_iploc']] = ['value'=>$param['ipinfo']];
$jumpurl = $siteurl.'user/';
}elseif($scene == 'complain'){
$template_id = $conf['wxnotice_tpl_complain'];
$data = [];
if(mb_strlen($param['name']) > 20) $param['name'] = mb_substr($param['name'], 0, 20);
if(mb_strlen($param['reason']) > 20) $param['reason'] = mb_substr($param['reason'], 0, 20);
if($conf['wxnotice_tpl_complain_order_no']) $data[$conf['wxnotice_tpl_complain_order_no']] = ['value'=>$param['trade_no']];
if($conf['wxnotice_tpl_complain_time']) $data[$conf['wxnotice_tpl_complain_time']] = ['value'=>$param['time']];
if($conf['wxnotice_tpl_complain_reason']) $data[$conf['wxnotice_tpl_complain_reason']] = ['value'=>$param['content']];
if($conf['wxnotice_tpl_complain_type']) $data[$conf['wxnotice_tpl_complain_type']] = ['value'=>$param['type']];
if($conf['wxnotice_tpl_complain_name']) $data[$conf['wxnotice_tpl_complain_name']] = ['value'=>$param['name']];
$jumpurl = $siteurl.'user/';
}elseif($scene == 'balance'){
$template_id = $conf['wxnotice_tpl_balance'];
$data = [];
if($conf['wxnotice_tpl_balance_user']) $data[$conf['wxnotice_tpl_balance_user']] = ['value'=>$param['user']];
if($conf['wxnotice_tpl_balance_time']) $data[$conf['wxnotice_tpl_balance_time']] = ['value'=>$param['time']];
if($conf['wxnotice_tpl_balance_money']) $data[$conf['wxnotice_tpl_balance_money']] = ['value'=>$param['money']];
if($conf['wxnotice_tpl_balance_msg']) $data[$conf['wxnotice_tpl_balance_msg']] = ['value'=>'为避免造成订单失败,请及时充值'];
$jumpurl = $siteurl.'user/';
}
if(empty($template_id) || empty($wid)) return false;
$wechat = new \lib\wechat\WechatAPI($wid);
try{
return $wechat->sendTemplateMessage($openid, $template_id, $jumpurl, $data);
}catch(Exception $e){
$errmsg = $e->getMessage();
$CACHE->save('wxtplerrmsg', ['errmsg'=>$errmsg, 'time'=>date('Y-m-d H:i:s')], 86400);
//echo $errmsg;
return false;
}
}
private static function send_mail_msg($scene, $receiver, $param){
global $conf, $CACHE;
[$title, $content] = self::get_msg_tpl($scene, $param);
if(empty($content)) return;
$result = send_mail($receiver, $title, $content);
if($result === true) return true;
if(!empty($result)){
$CACHE->save('mailerrmsg', ['errmsg'=>$result, 'time'=>date('Y-m-d H:i:s')], 86400);
}
return false;
}
private static function get_msg_tpl($scene, $param){
global $conf;
if($scene == 'regaudit'){
$title = '新注册商户待审核提醒';
$content = '尊敬的'.$conf['sitename'].'管理员,网站有新注册的商户待审核,请及时前往用户列表审核处理。<br/>商户ID'.$param['uid'].'<br/>注册账号:'.$param['account'].'<br/>注册时间:'.date('Y-m-d H:i:s');
}elseif($scene == 'apply'){
$title = '新的提现待处理提醒';
$content = '尊敬的'.$conf['sitename'].'管理员,商户'.$param['uid'].'发起了手动提现申请,请及时处理。<br/>商户ID'.$param['uid'].'<br/>提现方式:'.$param['type'].'<br/>提现金额:'.$param['realmoney'].'<br/>提交时间:'.date('Y-m-d H:i:s');
}elseif($scene == 'domain'){
$title = '新的授权支付域名待审核提醒';
$content = '尊敬的'.$conf['sitename'].'管理员,商户'.$param['uid'].'提交了新的授权支付域名,请及时审核处理。<br/>商户ID'.$param['uid'].'<br/>授权域名:'.$param['domain'].'<br/>提交时间:'.date('Y-m-d H:i:s');
}elseif($scene == 'order'){
$title = '新订单通知 - '.$conf['sitename'];
$content = '尊敬的商户,您有一条新订单通知。<br/>商品名称:'.$param['name'].'<br/>订单金额:¥'.$param['money'].'<br/>支付方式:'.$param['type'].'<br/>商户订单号:'.$param['out_trade_no'].'<br/>系统订单号:'.$param['trade_no'].'<br/>支付完成时间:'.$param['time'];
}elseif($scene == 'settle'){
$title = '结算完成通知 - '.$conf['sitename'];
$content = '尊敬的商户,今日结算已完成,请查收。<br/>结算金额:¥'.$param['money'].'<br/>实际到账:¥'.$param['realmoney'].'<br/>结算账号:'.$param['account'].'<br/>结算完成时间:'.$param['time'];
}elseif($scene == 'login'){
$title = '账号登录通知 - '.$conf['sitename'];
$content = '尊敬的商户,您的账号<b>'.$param['user'].'</b>已于'.$param['time'].'成功登录到商户平台。<br/>登录IP'.$param['clientip'].'<br/>登录时间:'.$param['time'];
}elseif($scene == 'complain'){
$title = '支付交易投诉通知 - '.$conf['sitename'];
$content = '尊敬的商户,'.$param['type'].'<br/>系统订单号:'.$param['trade_no'].'<br/>投诉原因:'.$param['title'].'<br/>投诉详情:'.$param['content'].'<br/>商品名称:'.$param['ordername'].'<br/>订单金额:¥'.$param['money'].'<br/>投诉时间:'.$param['time'];
}elseif($scene == 'mchrisk'){
$title = '渠道商户违规处置通知 - '.$conf['sitename'];
$content = '尊敬的商户,您有新的渠道商户违规处置记录!<br/>渠道子商户号:'.$param['mchid'].'<br/>商户名称:'.$param['mchname'].'<br/>风险类型:'.$param['risk_desc'].'<br/>处罚方案:'.$param['punish_type'].''.$param['punish_desc'].'<br/>记录时间:'.$param['punish_time'];
}elseif($scene == 'balance'){
$title = '商户余额不足提醒 - '.$conf['sitename'];
$content = '尊敬的商户,您的手续费余额不足'.$param['msgmoney'].'元,为避免造成订单失败请及时充值。<br/>当前余额:'.$param['money'].'元';
}
return [$title, $content];
}
private static function getMessageSwitch($scene){
global $conf;
if(isset($conf['msgconfig_'.$scene])){
return $conf['msgconfig_'.$scene];
}
return false;
}
}
+78
View File
@@ -0,0 +1,78 @@
<?php
namespace lib;
/*
* 快捷登录接口
*/
class Oauth{
private $apiurl;
private $appid;
private $appkey;
private $callback;
function __construct($config){
$this->apiurl = $config['apiurl'].'connect.php';
$this->appid = $config['appid'];
$this->appkey = $config['appkey'];
$this->callback = $config['callback'];
}
//获取登录跳转url
public function login($type){
//-------生成唯一随机串防CSRF攻击
$state = md5(uniqid(rand(), TRUE));
$_SESSION['Oauth_state'] = $state;
//-------构造请求参数列表
$keysArr = array(
"act" => "login",
"appid" => $this->appid,
"appkey" => $this->appkey,
"type" => $type,
"redirect_uri" => $this->callback,
"state" => $state
);
$login_url = $this->apiurl.'?'.http_build_query($keysArr);
$response = get_curl($login_url);
$arr = json_decode($response,true);
return $arr;
}
//登录成功返回网站
public function callback(){
//-------请求参数列表
$keysArr = array(
"act" => "callback",
"appid" => $this->appid,
"appkey" => $this->appkey,
"code" => $_GET['code']
);
//------构造请求access_token的url
$token_url = $this->apiurl.'?'.http_build_query($keysArr);
$response = get_curl($token_url);
$arr = json_decode($response,true);
return $arr;
}
//查询用户信息
public function query($type, $social_uid){
//-------请求参数列表
$keysArr = array(
"act" => "query",
"appid" => $this->appid,
"appkey" => $this->appkey,
"type" => $type,
"social_uid" => $social_uid
);
//------构造请求access_token的url
$token_url = $this->apiurl.'?'.http_build_query($keysArr);
$response = get_curl($token_url);
$arr = json_decode($response,true);
return $arr;
}
}
+128
View File
@@ -0,0 +1,128 @@
<?php
namespace lib;
class Order
{
public static function freeze($trade_no){
global $DB;
$row = $DB->find('order', 'uid,getmoney,status,channel', ['trade_no'=>$trade_no]);
if(!$row)
return ['code'=>-1, 'msg'=>'当前订单不存在!'];
if($row['status']!=1)
return ['code'=>-1, 'msg'=>'只支持冻结已支付状态的订单'];
$channel = \lib\Channel::get($row['channel']);
if($channel['mode']==1)
return ['code'=>-1, 'msg'=>'当前支付通道为商户直清,不支持冻结'];
if($row['getmoney']>0){
changeUserMoney($row['uid'], $row['getmoney'], false, '订单冻结', $trade_no);
$DB->exec("update pre_order set status='3' where trade_no='$trade_no'");
}
return ['code'=>0, 'msg'=>'已成功从UID:'.$row['uid'].'冻结'.$row['getmoney'].'元余额'];
}
public static function unfreeze($trade_no){
global $DB;
$row = $DB->find('order', 'uid,getmoney,status,channel', ['trade_no'=>$trade_no]);
if(!$row)
return ['code'=>-1, 'msg'=>'当前订单不存在!'];
if($row['status']!=3)
return ['code'=>-1, 'msg'=>'只支持解冻已冻结状态的订单'];
$channel = \lib\Channel::get($row['channel']);
if($channel['mode']==1)
return ['code'=>-1, 'msg'=>'当前支付通道为商户直清,不支持冻结'];
if($row['getmoney']>0){
changeUserMoney($row['uid'], $row['getmoney'], true, '订单解冻', $trade_no);
$DB->exec("update pre_order set status='1' where trade_no='$trade_no'");
}
return ['code'=>0, 'msg'=>'已成功为UID:'.$row['uid'].'恢复'.$row['getmoney'].'元余额'];
}
public static function refund_info($trade_no, $api = 0, $uid = 0){
global $DB;
$where = ['trade_no'=>$trade_no];
if($uid > 0) $where['uid'] = $uid;
$order = $DB->find('order', '*', $where);
if(!$order)
return ['code'=>-1, 'msg'=>'当前订单不存在!'];
if(!in_array($order['status'], [1,2,3]))
return ['code'=>-1, 'msg'=>'该订单状态不支持退款!'];
if($order['status'] == 2 && empty($order['refundmoney'])) return ['code'=>-1, 'msg'=>'该订单已退款!'];
if($order['refundmoney'] > 0 && $order['refundmoney'] >= $order['realmoney']) return ['code'=>-1, 'msg'=>'该订单已全额退款!'];
$money = !empty($order['refundmoney']) ? round($order['realmoney'] - $order['refundmoney'], 2) : $order['realmoney'];
if($api==1){
if(!$order['api_trade_no']) return ['code'=>-1, 'msg'=>'接口订单号不存在'];
$channel = \lib\Channel::get($order['channel']);
if(!$channel) return ['code'=>-1, 'msg'=>'当前支付通道信息不存在'];
if(\lib\Plugin::isrefund($channel['plugin'])==false){
return ['code'=>-1, 'msg'=>'当前支付通道不支持API退款'];
}
}
return ['code'=>0, 'money'=>$money];
}
public static function refund($refund_no, $trade_no, $money, $api = 0, $uid = 0, $out_refund_no = null){
global $DB, $order, $conf;
$where = ['trade_no'=>$trade_no];
if($uid > 0) $where['uid'] = $uid;
$order = $DB->find('order', '*', $where);
if(!$order)
return ['code'=>-1, 'msg'=>'当前订单不存在!'];
if(!in_array($order['status'], [1,2,3]))
return ['code'=>-1, 'msg'=>'该订单状态不支持退款!'];
if($money>$order['realmoney']) return ['code'=>-1, 'msg'=>'退款金额不能大于订单金额'];
if(!$order['api_trade_no']) return ['code'=>-1, 'msg'=>'接口订单号不存在'];
if($order['status'] == 2 && empty($order['refundmoney'])) return ['code'=>-1, 'msg'=>'该订单已退款!'];
if($order['refundmoney'] > 0 && $order['refundmoney'] >= $order['realmoney']) return ['code'=>-1, 'msg'=>'该订单已全额退款!'];
if($order['refundmoney'] > 0 && $money > round($order['realmoney'] - $order['refundmoney'], 2)) return ['code'=>-1, 'msg'=>'退款金额不能超过该订单剩余可退款金额!'];
$refunded = $order['refundmoney'];
if(!$out_refund_no) $out_refund_no = $refund_no;
$mode = $DB->findColumn('channel', 'mode', ['id'=>$order['channel']]);
if($order['status'] == 3 || $mode == 1){
$reducemoney = 0;
}elseif($conf['refund_fee_type']==1 && $money == $order['realmoney']){
$reducemoney = $order['realmoney'];
}elseif(!$conf['refund_fee_type'] && ($money == $order['realmoney'] || $money >= $order['getmoney'])){
$reducemoney = $order['getmoney'];
}else{
$reducemoney = $money;
}
if($uid > 0 && $reducemoney > 0){
$usermoney = $DB->findColumn('user', 'money', ['uid'=>$uid]);
if($reducemoney > $usermoney){
return ['code'=>-1, 'msg'=>'商户余额不足,请先充值'];
}
}
if($api == 1){
$message = null;
if(!\lib\Plugin::refund($refund_no, $trade_no, $money, $message)){
return ['code'=>-1, 'msg'=>'退款失败:'.$message];
}
}
if($reducemoney > 0){
if($order['tid'] == 2){
$param = json_decode($order['param'], true);
if(isset($param['uid'])){
$order['uid'] = $param['uid'];
}
}
changeUserMoney($order['uid'], $reducemoney, false, '订单退款', $trade_no);
}
if($api == 1){
$refundmoney = !empty($refunded) ? round($refunded + $money, 2) : $money;
$DB->update('order', ['status'=>2, 'refundmoney'=>$refundmoney], ['trade_no'=>$trade_no]);
$DB->insert('refundorder', ['refund_no'=>$refund_no, 'out_refund_no'=>$out_refund_no, 'trade_no'=>$trade_no, 'uid'=>$order['uid'], 'money'=>$money, 'reducemoney'=>$reducemoney, 'addtime'=>date('Y-m-d H:i:s'), 'endtime'=>date('Y-m-d H:i:s'), 'status'=>1]);
}else{
$DB->update('order', ['status'=>2], ['trade_no'=>$trade_no]);
}
return ['code'=>0, 'refund_no'=>$refund_no, 'out_refund_no'=>$out_refund_no, 'trade_no'=>$trade_no, 'out_trade_no'=>$order['out_trade_no'], 'uid'=>$order['uid'], 'money'=>$money, 'reducemoney'=>$reducemoney];
}
}
+552
View File
@@ -0,0 +1,552 @@
<?php
namespace lib;
use Exception;
class Payment {
//生成待签名字符串
static private function getSignContent($data){
ksort($data);
$signStr = '';
foreach ($data as $k => $v) {
if(is_array($v) || isEmpty($v) || $k == 'sign' || $k == 'sign_type') continue;
$signStr .= $k . '=' . $v . '&';
}
$signStr = substr($signStr, 0, -1);
return $signStr;
}
//生成签名
static public function makeSign($data, $md5key) {
$sign_type = $data['sign_type'] ? $data['sign_type'] : 'MD5';
$signStr = self::getSignContent($data);
if($sign_type == 'RSA'){
global $conf;
$private_key = base64ToPem($conf['private_key'], 'PRIVATE KEY');
$pkey = openssl_pkey_get_private($private_key);
if(!$pkey) return false;
openssl_sign($signStr, $sign, $pkey, OPENSSL_ALGO_SHA256);
return base64_encode($sign);
}else{
$sign = md5($signStr . $md5key);
return $sign;
}
}
//验证签名
static public function verifySign($data, $md5key, $publicKey) {
if(!isset($data['sign'])) throw new Exception('缺少签名参数');
$sign_type = $data['sign_type'] ? $data['sign_type'] : 'MD5';
if($sign_type == 'RSA'){
$public_key = base64ToPem($publicKey, 'PUBLIC KEY');
$pkey = openssl_pkey_get_public($public_key);
if(!$pkey) throw new Exception('签名校验失败,商户公钥错误');
$signStr = self::getSignContent($data);
$result = openssl_verify($signStr, base64_decode($data['sign']), $pkey, OPENSSL_ALGO_SHA256);
return $result === 1;
}else{
$sign = self::makeSign($data, $md5key);
return $sign === $data['sign'];
}
}
// 页面支付返回信息
static public function echoDefault($result){
global $cdnpublic,$order,$conf,$sitename,$ordername,$siteurl;
$type = $result['type'];
if(!$type) return false;
switch($type){
case 'jump': //跳转
$html_text = '<script>window.location.replace(\''.$result['url'].'\');</script>';
if(isset($result['submit']) && $result['submit']){
submitTemplate($html_text);
}else{
echo $html_text;
}
break;
case 'html': //显示html
$html_text = $result['data'];
if(isset($result['submit']) && $result['submit'] && substr($html_text, 0, 6) == '<form '){
submitTemplate($html_text);
}else{
echo $html_text;
}
break;
case 'json': //显示JSON
echo json_encode($result['data']);
break;
case 'page': //显示指定页面
include_once SYSTEM_ROOT.'txprotect.php';
if(isset($result['data'])) extract($result['data']);
if($conf['pageordername']==1)$order['name']=$ordername?$ordername:'onlinepay';
include PAYPAGE_ROOT.$result['page'].'.php';
break;
case 'qrcode': //扫码页面
if($result['page'] == 'alipay_qrcode' && !empty($conf['alipay_qrcode_url'])){
if(strpos($result['url'], $siteurl)===0){
$result['url'] = $conf['alipay_qrcode_url'].substr($result['url'], strlen($siteurl));
}elseif(!empty($conf['localurl_alipay']) && strpos($result['url'], $conf['localurl_alipay'])===0){
$result['url'] = $conf['alipay_qrcode_url'].substr($result['url'], strlen($conf['localurl_alipay']));
}
}
case 'scheme': //跳转urlscheme页面
if($result['page'] == 'wxpay_mini') $result['page'] = 'wxpay_h5';
include_once SYSTEM_ROOT.'txprotect.php';
$code_url = $result['url'];
if($conf['pageordername']==1)$order['name']=$ordername?$ordername:'onlinepay';
if($conf['wework_payopen'] == 1 && ($result['page'] == 'wxpay_wap' && strpos($_SERVER['HTTP_USER_AGENT'], 'MicroMessenger')===false || $result['page'] == 'wxpay_qrcode' && checkmobile())){
$code_url_wxkf = self::getWxkfPayUrl($code_url);
if($code_url_wxkf){
$code_url = $code_url_wxkf;
include PAYPAGE_ROOT.'wxpay_h5.php';
break;
}
}
include PAYPAGE_ROOT.$result['page'].'.php';
break;
case 'return': //同步回调
returnTemplate($result['url']);
break;
case 'error': //错误提示
sysmsg($result['msg']);
break;
default:break;
}
}
// API支付返回信息
static public function echoJson($result){
global $order,$siteurl;
if(!$result) return false;
$type = $result['type'];
if(!$type) return false;
if(defined('API_INIT')){
$json = ['code'=>0, 'trade_no'=>TRADE_NO];
switch($type){
case 'jump': //跳转URL
$json['pay_type'] = 'jump';
$json['pay_info'] = $result['url'];
break;
case 'html': //显示html跳转
$json['pay_type'] = 'html';
$json['pay_info'] = $result['data'];
break;
case 'qrcode': //扫码支付
$json['pay_type'] = 'qrcode';
$json['pay_info'] = $result['url'];
break;
case 'scheme': //小程序H5跳转
$json['pay_type'] = 'urlscheme';
$json['pay_info'] = $result['url'];
break;
case 'jsapi': //JSAPI支付
$json['pay_type'] = 'jsapi';
$json['pay_info'] = $result['data'];
break;
case 'app': //APP支付
$json['pay_type'] = 'app';
$json['pay_info'] = $result['data'];
break;
case 'scan': //付款码支付
$json['pay_type'] = 'scan';
$json['pay_info'] = $result['data'];
break;
case 'wxplugin': //微信小程序插件支付
$json['pay_type'] = 'wxplugin';
$json['pay_info'] = $result['data'];
break;
case 'wxapp': //跳转微信小程序支付
$json['pay_type'] = 'wxapp';
$json['pay_info'] = $result['data'];
break;
case 'error':
$json['code'] = -2;
$json['msg'] = $result['msg'];
break;
default:
$json['pay_type'] = 'jump';
$json['pay_info'] = $siteurl.'pay/submit/'.TRADE_NO.'/';
break;
}
if($json['code'] == 0){
if(is_array($json['pay_info'])) $json['pay_info'] = json_encode($json['pay_info']);
$json['timestamp'] = time().'';
$json['sign_type'] = 'RSA';
$json['sign'] = self::makeSign($json, null);
}
exit(json_encode($json));
}else{
$json = ['code'=>1, 'trade_no'=>TRADE_NO];
switch($type){
case 'jump': //跳转URL
$json['payurl'] = $result['url'];
break;
case 'html': //显示html跳转
$json['html'] = $result['data'];
break;
case 'qrcode': //扫码支付
$json['qrcode'] = $result['url'];
break;
case 'scheme': //小程序H5跳转
$json['urlscheme'] = $result['url'];
break;
case 'error':
$json['code'] = -2;
$json['msg'] = $result['msg'];
break;
default:
$json['payurl'] = $siteurl.'pay/submit/'.TRADE_NO.'/';
break;
}
exit(json_encode($json));
}
}
// 订单回调处理
static public function processOrder($isnotify, $order, $api_trade_no, $buyer = null, $bill_trade_no = null){
global $DB,$conf,$siteurl;
if($order['status']==0 || $order['status']==4){
if($DB->exec("UPDATE `pre_order` SET `status`=1 WHERE `trade_no`='".$order['trade_no']."'")){
$data = ['endtime'=>'NOW()', 'date'=>'CURDATE()'];
if(!empty($api_trade_no)) $data['api_trade_no'] = $api_trade_no;
if(!empty($buyer)) $data['buyer'] = $buyer;
if(!empty($bill_trade_no)) $data['bill_trade_no'] = $bill_trade_no;
if($order['settle']>0) $data['settle'] = $order['settle'];
$DB->update('order', $data, ['trade_no'=>$order['trade_no']]);
$order['api_trade_no'] = $api_trade_no;
processOrder($order, $isnotify);
}
}elseif(empty($order['api_trade_no']) && !empty($api_trade_no)){
$data = ['api_trade_no'=>$api_trade_no];
if(!empty($buyer)) $data['buyer'] = $buyer;
if(!empty($bill_trade_no)) $data['bill_trade_no'] = $bill_trade_no;
$DB->update('order', $data, ['trade_no'=>$order['trade_no']]);
}elseif(empty($order['buyer']) && !empty($buyer)){
$data['buyer'] = $buyer;
$DB->update('order', $data, ['trade_no'=>$order['trade_no']]);
}
if($isnotify && $order['settle']>0){
$DB->update('order', ['settle'=>$order['settle']], ['trade_no'=>$order['trade_no']]);
}
if(!$isnotify){
include_once SYSTEM_ROOT.'txprotect.php';
if($order['status'] == 2){
$jumpurl = '/payerr.html';
returnTemplate($jumpurl);
return;
}
// 支付完成5分钟后禁止跳转回网站
if(!empty($order['endtime']) && time() - strtotime($order['endtime']) > 300){
$jumpurl = '/payok.html';
}else{
$url=creat_callback($order);
$jumpurl = $url['return'];
}
returnTemplate($jumpurl);
}
}
// 更新订单信息
static public function updateOrder($trade_no, $api_trade_no, $buyer = null, $status = null){
global $DB;
$data = ['api_trade_no'=>$api_trade_no];
if(!empty($buyer)) $data['buyer'] = $buyer;
if($status) $data['status'] = $status;
$DB->update('order', $data, ['trade_no'=>$trade_no]);
}
// 更新订单扩展信息
static public function updateOrderExt($trade_no, $data){
global $DB;
$DB->update('order', ['ext'=>serialize($data)], ['trade_no'=>$trade_no]);
}
// 更新合单状态
static public function updateOrderCombine($trade_no, $sub_orders = null){
global $DB;
$DB->update('order', ['combine'=>1], ['trade_no'=>$trade_no]);
if(!empty($sub_orders)){
$DB->delete('suborder', ['trade_no'=>$trade_no]);
foreach($sub_orders as $data){
$data['trade_no'] = $trade_no;
$data['status'] = 0;
$DB->insert('suborder', $data);
}
}
}
// 更新订单分账接收人
static public function updateOrderProfits($order, $plugin){
global $DB;
$support_plugins = \lib\ProfitSharing\CommUtil::$plugins;
if(in_array($plugin, $support_plugins)){
$psreceiver = null;
if($order['subchannel'] > 0){
$psreceiver = $DB->getRow("SELECT * FROM `pre_psreceiver` WHERE `channel`='{$order['channel']}' AND `uid`='{$order['uid']}' AND `subchannel`='{$order['subchannel']}' AND `status`=1 ORDER BY id ASC LIMIT 1");
}
if(!$psreceiver) $psreceiver = $DB->getRow("SELECT * FROM `pre_psreceiver` WHERE `channel`='{$order['channel']}' AND `uid`='{$order['uid']}' AND `status`=1 ORDER BY id ASC LIMIT 1");
if(!$psreceiver) $psreceiver = $DB->getRow("SELECT * FROM `pre_psreceiver` WHERE `channel`='{$order['channel']}' AND `uid` IS NULL AND `status`=1 ORDER BY id ASC LIMIT 1");
if($psreceiver){
if(!$psreceiver['minmoney'] || $order['realmoney']>=$psreceiver['minmoney']){
$DB->update('order', ['profits'=>$psreceiver['id']], ['trade_no'=>$order['trade_no']]);
return intval($psreceiver['id']);
}
}
}
return 0;
}
// 更新订单分账接收人2
static public function updateOrderProfits2($order, $plugin){
return 0;
}
//支付宝直付通确认结算
public static function alipaydSettle($channel, $order){
$alipay_config = require(PLUGIN_ROOT.'alipayd/inc/config.php');
$alipaySevice = new \Alipay\AlipayTradeService($alipay_config);
if($order['combine'] == 1){
$sub_orders = self::getSubOrders($order['trade_no']);
if(empty($sub_orders)) throw new Exception('子订单数据不存在');
$failnum = 0;
$errmsg = '';
foreach($sub_orders as $sub_order){
if($sub_order['settle'] == 0){
$settle = 0;
try{
$alipaySevice->settle_confirm($sub_order['api_trade_no'], $sub_order['money']);
$settle = 1;
}catch(Exception $e){
if(strpos($e->getMessage(), 'ALREADY_CONFIRM_SETTLE')!==false){
$settle = 1;
}else{
$failnum++;
$errmsg .= $e->getMessage().',';
}
}
if($settle == 1) self::updateSubOrderSettle($sub_order['sub_trade_no'], 1);
}
}
if($failnum > 0) throw new Exception('部分子单结算失败,失败数量:'.$failnum.',失败原因:'.$errmsg);
return true;
}
try{
$alipaySevice->settle_confirm($order['api_trade_no'], $order['realmoney']);
return true;
}catch(Exception $e){
if(strpos($e->getMessage(), 'ALREADY_CONFIRM_SETTLE')!==false){
return true;
}else{
throw $e;
}
}
}
//微信收付通确认结算
public static function wxpaynpSettle($channel, $order){
$wechatpay_config = require(PLUGIN_ROOT.'/wxpaynp/inc/config.php');
if($wechatpay_config['ecommerce']){
if(!$order['profits']){
$client = new \WeChatPay\V3\ProfitsharingService($wechatpay_config);
return $client->unfreeze($order['trade_no'], $order['api_trade_no']);
}else{
throw new Exception('当前订单需要分账,请进入分账订单页面确认分账');
}
}else{
throw new Exception('非电商收付通订单');
}
}
//支付宝预授权资金支付
public static function alipayPreAuthPay($channel, $order){
global $conf;
$alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php');
$alipaySevice = new \Alipay\AlipayTradeService($alipay_config);
$trade_no = $order['trade_no'];
$bizContent = [
'out_order_no' => $trade_no,
'out_request_no' => $trade_no,
];
$result = $alipaySevice->preAuthQuery($bizContent);
//print_r($result);exit;
if(!isset($result['auth_no'])) throw new Exception('预授权订单查询失败');
if($result['rest_amount'] == 0) throw new Exception('剩余冻结金额为0');
if($result['order_status'] == 'AUTHORIZED'){
$auth_no = $result['auth_no'];
$ordername = !empty($conf['ordername'])?ordername_replace($conf['ordername'],$order['name'],$order['uid'],$trade_no):$order['name'];
$bizContent = [
'out_trade_no' => $trade_no,
'total_amount' => $result['rest_amount'],
'subject' => $ordername,
'product_code' => 'PREAUTH_PAY',
'auth_no' => $auth_no,
'auth_confirm_mode' => 'COMPLETE'
];
return $alipaySevice->scanPay($bizContent);
}else{
throw new Exception('该笔订单非已授权状态,无需支付');
}
}
//支付宝预授权资金解冻
public static function alipayUnfreeze($channel, $order){
$alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php');
$alipaySevice = new \Alipay\AlipayTradeService($alipay_config);
$trade_no = $order['trade_no'];
$bizContent = [
'out_order_no' => $trade_no,
'out_request_no' => $trade_no,
];
$result = $alipaySevice->preAuthQuery($bizContent);
//print_r($result);exit;
if(!isset($result['auth_no'])) throw new Exception('预授权订单查询失败');
if($result['rest_amount'] == 0) throw new Exception('剩余冻结金额为0');
if($result['order_status'] == 'AUTHORIZED'){
$auth_no = $result['auth_no'];
$bizContent = [
'auth_no' => $auth_no,
'out_request_no' => date("YmdHis").rand(11111,99999),
'amount' => $result['rest_amount'],
'remark' => '解冻资金'
];
return $alipaySevice->preAuthUnfreeze($bizContent);
}else{
throw new Exception('该笔订单非已授权状态,无需解冻');
}
}
//支付宝红包转账
public static function alipayRedPacketTransfer($channel, $payee_user_id, $money, $order_id){
$out_biz_no = date("YmdHis").rand(11111,99999);
$alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php');
$alipaySevice = new \Alipay\AlipayTransferService($alipay_config);
$alipaySevice->redPacketTansfer($out_biz_no, $money, $payee_user_id, $conf['sitename'], $order_id);
}
//支付宝红包资金退回
public static function alipayRedPacketRefund($channel, $trade_no, $money){
$out_biz_no = date("YmdHis").rand(11111,99999);
$alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php');
$alipaySevice = new \Alipay\AlipayTransferService($alipay_config);
$alipaySevice->redPacketRefund($out_biz_no, $trade_no, $money);
}
//加锁设置订单扩展数据
public static function lockPayData($trade_no, $func){
global $DB;
$DB->beginTransaction();
$data = $DB->getColumn("SELECT ext FROM pre_order WHERE trade_no=:trade_no FOR UPDATE", [':trade_no'=>$trade_no]);
if($data) {
$DB->rollBack();
return unserialize($data);
}
try{
$data = $func();
}catch(\Exception $e){
$DB->rollBack();
throw $e;
}
if($data){
$DB->update('order', ['ext'=>serialize($data)], ['trade_no' => $trade_no]);
}
$DB->commit();
return $data;
}
//获取微信客服跳转链接
public static function getWxkfPayUrl($pay_url){
global $order, $DB, $conf;
$cookiesid = $_COOKIE['mysid'];
if(!$cookiesid||!preg_match('/^[0-9a-z]{32}$/i', $cookiesid)){
$cookiesid = getSid();
setcookie("mysid", $cookiesid, time() + 2592000, '/');
}
if($conf['wework_paykfid'] > 0){
$wxkfaccount = $DB->getRow("SELECT * FROM pre_wxkfaccount WHERE id=:id", [':id'=>$conf['wework_paykfid']]);
}elseif($conf['wework_paymsgmode'] == 1){
$usekflist = $DB->getAll("SELECT DISTINCT aid FROM pre_wxkflog WHERE `sid`=:sid AND addtime>=:addtime AND status=1", [':sid'=>$cookiesid, ':addtime'=>date("Y-m-d H:i:s", strtotime('-48 hours'))]);
$usekfids = [0];
foreach($usekflist as $usekf){
$usekfids[] = intval($usekf['aid']);
}
$wxkfaccount = $DB->getRow("SELECT A.* FROM pre_wxkfaccount A LEFT JOIN pre_wework B ON A.wid=B.id WHERE A.id NOT IN (".implode(",", $usekfids).") AND B.status=1 ORDER BY A.usetime ASC LIMIT 1");
}else{
$wxkfaccount = $DB->getRow("SELECT A.* FROM pre_wxkfaccount A LEFT JOIN pre_wework B ON A.wid=B.id WHERE B.status=1 ORDER BY A.usetime ASC LIMIT 1");
}
if(!$wxkfaccount) return false;
$DB->insert('wxkflog', ['trade_no'=>$order['trade_no'], 'aid'=>$wxkfaccount['id'], 'sid'=>$cookiesid, 'payurl'=>$pay_url, 'addtime'=>'NOW()']);
$scene_param = 'orderid='.$order['trade_no'].'&money='.$order['realmoney'];
try{
if(!empty($wxkfaccount['url'])){
$kfurl = $wxkfaccount['url'];
$DB->update('wxkfaccount', ['usetime'=>'NOW()'], ['id'=>$wxkfaccount['id']]);
}else{
$wework = new wechat\WeWorkAPI($wxkfaccount['wid']);
$kfurl = $wework->getKFURL($wxkfaccount['openkfid'], 'pay');
$DB->update('wxkfaccount', ['url'=>$kfurl, 'usetime'=>'NOW()'], ['id'=>$wxkfaccount['id']]);
}
$kfurl = 'weixin://biz/ww/kefu/' . $kfurl . '&schema=1';
$kfurl .= '&scene_param='.urlencode($scene_param);
return $kfurl;
}catch(\Exception $e){
sysmsg($e->getMessage());
}
}
//支付宝直付通&微信收付通延迟结算处理
public static function settle_task(){
global $DB;
$orders = $DB->getAll("SELECT A.*,B.plugin FROM pre_order A LEFT JOIN pre_channel B ON A.channel=B.id WHERE A.status=1 AND A.settle=1 AND A.addtime<DATE_SUB(NOW(), INTERVAL 24 HOUR) AND B.plugin in ('alipayd','wxpaynp') ORDER BY A.trade_no ASC LIMIT 10");
foreach($orders as $row){
$trade_no = $row['trade_no'];
$channel = $row['subchannel'] > 0 ? \lib\Channel::getSub($row['subchannel']) : \lib\Channel::get($row['channel'], $DB->findColumn('user', 'channelinfo', ['uid'=>$row['uid']]));
if(!$channel) continue;
try{
if($row['plugin'] == 'alipayd'){
self::alipaydSettle($channel, $row);
}elseif($row['plugin'] == 'wxpaynp'){
self::wxpaynpSettle($channel, $row);
}
$DB->update('order', ['settle'=>2], ['trade_no'=>$trade_no]);
echo $trade_no.' 结算成功<br/>';
}catch(Exception $e){
$errmsg = $e->getMessage();
if(strpos($errmsg, 'ALREADY_CONFIRM_SETTLE')){
$DB->update('order', ['settle'=>2], ['trade_no'=>$trade_no]);
echo $trade_no.' 结算成功<br/>';
continue;
}
$DB->update('order', ['settle'=>3], ['trade_no'=>$trade_no]);
echo $trade_no.' 结算失败,'.$errmsg.'<br/>';
}
}
}
public static function getSubOrders($trade_no){
global $DB;
return $DB->getAll("SELECT * FROM pre_suborder WHERE trade_no=:trade_no", [':trade_no'=>$trade_no]);
}
public static function processSubOrders($trade_no, $sub_orders){
global $DB;
foreach($sub_orders as $data){
$DB->update('suborder', ['status'=>1, 'api_trade_no'=>$data['api_trade_no']], ['sub_trade_no'=>$data['sub_trade_no']]);
}
}
public static function refundSubOrder($sub_trade_no, $refundmoney = null){
global $DB;
$DB->update('suborder', ['status'=>2, 'refundmoney'=>$refundmoney], ['sub_trade_no'=>$sub_trade_no]);
}
public static function updateSubOrderSettle($sub_trade_no, $settle){
global $DB;
$DB->update('suborder', ['settle'=>$settle], ['sub_trade_no'=>$sub_trade_no]);
}
}
+445
View File
@@ -0,0 +1,445 @@
<?php
namespace lib;
class PdoHelper
{
private $sqlPrefix = "pre_";//SQL数据表前缀识别字符
private $db;
private $fetchStyle = \PDO::FETCH_ASSOC;
private $prefix;
private $errorInfo;
/**
* PdoHelper constructor.
*
* @param array $dbconfig 数据库信息
*/
function __construct($dbconfig)
{
$this->prefix = $dbconfig['dbqz'].'_';
try {
$this->db = new \PDO("mysql:host={$dbconfig['host']};dbname={$dbconfig['dbname']};port={$dbconfig['port']}",$dbconfig['user'],$dbconfig['pwd']);
} catch (\Exception $e) {
exit('链接数据库失败:' . $e->getMessage());
}
$this->db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_SILENT);
$this->db->exec("set sql_mode = ''");
$this->db->exec("set names utf8mb4");
$this->db->exec("set time_zone='+8:00'");
}
/**
* 设置结果集方式
*
* @param string $_style
*/
public function setFetchStyle($_style)
{
$this->fetchStyle = $_style;
}
/**
* 替换数据表前缀
* @param $_sql
*
* @return mixed
*/
private function dealPrefix($_sql){
return str_replace($this->sqlPrefix,$this->prefix,$_sql);
}
private function _where($conditions){
$result = array( "_where" => " ","_bindParams" => array());
if(is_array($conditions) && !empty($conditions)){
$fieldss = array(); $sql = null; $join = array();
if(isset($conditions[0]) && $sql = $conditions[0]) unset($conditions[0]);
foreach( $conditions as $key => $condition ){
if(substr($key, 0, 1) != ":"){
unset($conditions[$key]);
$conditions[":".$key] = $condition;
}
$join[] = "`{$key}` = :{$key}";
}
if(!$sql) $sql = join(" AND ",$join);
$result["_where"] = " WHERE ". $sql;
$result["_bindParams"] = $conditions;
}elseif(!empty($conditions)){
$result["_where"] = " WHERE ". $conditions;
}
return $result;
}
private function _select($table, $fields = '*', $where = array(), $sort = null, $limit = null){
$sort = !empty($sort) ? ' ORDER BY '.$sort : '';
$fields = !empty($fields) ? $fields : '*';
if(is_array($fields)){
$fields = implode(',',$fields);
}
$conditions = $this->_where($where);
$sql = ' FROM pre_'.$table.$conditions["_where"];
if(is_array($limit)){
$limit = ' LIMIT '.$limit[0].','.$limit[1];
}elseif(!empty($limit)){
$limit = ' LIMIT '.$limit;
}else{
$limit = '';
}
return array('sql'=>'SELECT '. $fields . $sql . $sort . $limit, 'bind'=>$conditions["_bindParams"]);
}
/**
* 查询一条数据
* @param string $table
* @param string $fields
* @param array $where
* @param string $sort
* @param int $limit
*
* @return array
*/
public function find($table, $fields = '*', $where = array(), $sort = null, $limit = null){
$sql_arr = $this->_select($table, $fields, $where, $sort, $limit);
return $this->getRow($sql_arr['sql'], $sql_arr['bind']);
}
/**
* 查询全部数据
* @param string $table
* @param string $fields
* @param array $where
* @param string $sort
* @param int $limit
*
* @return array
*/
public function findAll($table, $fields = '*', $where = array(), $sort = null, $limit = null){
$sql_arr = $this->_select($table, $fields, $where, $sort, $limit);
return $this->getAll($sql_arr['sql'], $sql_arr['bind']);
}
/**
* 查询字段数据
* @param string $table
* @param string $fields
* @param array $where
* @param string $sort
*
* @return mixed
*/
public function findColumn($table, $fields, $where = array(), $sort = null){
$sql_arr = $this->_select($table, $fields, $where, $sort, 1);
return $this->getColumn($sql_arr['sql'], $sql_arr['bind']);
}
/**
* 插入数据
* @param string $table
* @param array $data
* @param bool $replace
*
* @return int
*/
public function insert($table, $data, $replace = false){
$values = array();
foreach ($data as $k=>$v){
$keys[] = "`{$k}`";
if ($v == 'NOW()' || $v == 'CURDATE()' || $v == 'CURTIME()') {
$marks[] = $v;
}elseif ($v === null || $v === false) {
$marks[] = 'NULL';
}else{
$values[":".$k] = $v;
$marks[] = ":".$k;
}
}
$rowCount = $this->exec(($replace?"REPLACE":"INSERT")." INTO pre_".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $marks).")", $values);
if($rowCount){
return $this->lastInsertId();
}else{
return false;
}
}
/**
* 更新数据
* @param string $table
* @param array $data
* @param array $where
*
* @return int
*/
public function update($table, $data, $where){
if(is_array($data) && !empty($data)){
$values = array();
foreach ($data as $k=>$v){
if($v == 'NOW()' || $v == 'CURDATE()' || $v == 'CURTIME()'){
$setstr[] = "`{$k}` = ".$v;
}elseif($v === null || $v === false){
$setstr[] = "`{$k}` = NULL";
}else{
$values[":M_UPDATE_".$k] = $v;
$setstr[] = "`{$k}` = :M_UPDATE_".$k;
}
}
$update = implode(', ', $setstr);
}elseif(!empty($data)){
$update = $data;
}else{
return false;
}
$conditions = $this->_where($where);
$rowCount = $this->exec("UPDATE pre_".$table." SET ".$update.$conditions["_where"], $conditions["_bindParams"] + $values);
return $rowCount;
}
/**
* 删除数据
* @param string $table
* @param array $where
*
* @return int
*/
public function delete($table, $where){
$conditions = $this->_where($where);
$rowCount = $this->exec("DELETE FROM pre_".$table.$conditions["_where"], $conditions["_bindParams"]);
return $rowCount;
}
/**
* 统计行数
* @param string $table
* @param array $where
*
* @return int
*/
public function count($table, $where){
$conditions = $this->_where($where);
$count = $this->getColumn("SELECT COUNT(*) FROM pre_".$table.$conditions["_where"], $conditions["_bindParams"]);
return $count;
}
/**
* 执行语句
* @param string $_sql
* @param array $_array
*
* @return int|bool
*/
public function exec($_sql, $_array = null)
{
$_sql = $this->dealPrefix($_sql);
if (is_array($_array)) {
$stmt = $this->db->prepare($_sql);
if($stmt) {
$result = $stmt->execute($_array);
if($result!==false){
return $result;
}else{
$this->errorInfo = $stmt->errorInfo();
return false;
}
}else{
$this->errorInfo = $this->db->errorInfo();
return false;
}
} else {
$result = $this->db->exec($_sql);
if($result!==false){
return $result;
}else{
$this->errorInfo = $this->db->errorInfo();
return false;
}
}
}
/**
* 获取PDOStatement
* @param string $_sql
* @param array $_array
*
* @return \PDOStatement
*/
public function query($_sql, $_array = null)
{
$_sql = $this->dealPrefix($_sql);
if (is_array($_array)) {
$stmt = $this->db->prepare($_sql);
if($stmt) {
if($stmt->execute($_array)){
return $stmt;
}else{
$this->errorInfo = $stmt->errorInfo();
return false;
}
}else{
$this->errorInfo = $this->db->errorInfo();
return false;
}
} else {
if($stmt = $this->db->query($_sql)){
return $stmt;
}else{
$this->errorInfo = $this->db->errorInfo();
return false;
}
}
}
/**
* 查询一条结果
*
* @param string $_sql string
* @param array $_array array
*
* @return mixed
*/
public function getRow($_sql, $_array = null)
{
$stmt = $this->query($_sql, $_array);
if($stmt) {
return $stmt->fetch($this->fetchStyle);
}else{
return false;
}
}
/**
* 获取所有结果
*
* @param string $_sql
* @param array $_array
*
* @return array
*/
public function getAll($_sql, $_array = null)
{
$stmt = $this->query($_sql, $_array);
if($stmt) {
return $stmt->fetchAll($this->fetchStyle);
}else{
return false;
}
}
/**
* 获取结果数
* @param string $_sql
* @param array $_array
*
* @return int
*/
public function getCount($_sql, $_array = null)
{
$stmt = $this->query($_sql, $_array);
if($stmt) {
return $stmt->rowCount();
}else{
return false;
}
}
/**
* 获取一个字段值
* @param string $_sql
* @param array $_array
*
* @return int
*/
public function getColumn($_sql, $_array = null)
{
$stmt = $this->query($_sql, $_array);
if($stmt) {
return $stmt->fetchColumn();
}else{
return false;
}
}
/**
* 返回最后插入行的ID
*
* @return int|\PDOStatement
*/
public function lastInsertId()
{
return $this->db->lastInsertId();
}
/**
* 返回错误信息
*
* @return string|\PDOStatement
*/
public function error()
{
$error = $this->errorInfo;
if($error){
return '['.$error[1].']'.$error[2];
}else{
return null;
}
}
//开启事务
public function beginTransaction()
{
return $this->db->beginTransaction();
}
//提交事务
public function commit()
{
return $this->db->commit();
}
//回滚事务
public function rollBack()
{
return $this->db->rollBack();
}
//事务
public function transaction($action){
if (is_callable($action))
{
$this->db->beginTransaction();
try {
$result = $action($this);
if ($result === false)
{
$this->db->rollBack();
}
else
{
$this->db->commit();
}
}
catch (\Exception $e) {
$this->db->rollBack();
throw $e;
}
return $result;
}
return false;
}
function __get($name)
{
return $this->$name;
}
function __destruct()
{
$this->db = null;
}
}
+241
View File
@@ -0,0 +1,241 @@
<?php
namespace lib;
use Exception;
class Plugin {
static public function getList(){
$dir = PLUGIN_ROOT;
$dirArray[] = NULL;
if (false != ($handle = opendir($dir))) {
$i = 0;
while (false !== ($file = readdir($handle))) {
if ($file != "." && $file != ".." && strpos($file, ".")===false) {
$dirArray[$i] = $file;
$i++;
}
}
closedir($handle);
}
return $dirArray;
}
static public function getConfig($name){
$filename = PLUGIN_ROOT.$name.'/'.$name.'_plugin.php';
$classname = '\\'.$name.'_plugin';
if(file_exists($filename)){
include $filename;
if (class_exists($classname, false) && property_exists($classname, 'info')) {
return $classname::$info;
}else{
return false;
}
}else{
return false;
}
}
static public function loadForPay($s){
global $DB,$conf,$order,$channel,$ordername;
if(preg_match('/^(.[a-zA-Z0-9]+)\/([0-9]+)\/$/',$s, $matchs)){
$func = $matchs[1];
$trade_no = $matchs[2];
$order = $DB->getRow("SELECT A.*,B.name typename,B.showname typeshowname FROM pre_order A left join pre_type B on A.type=B.id WHERE trade_no=:trade_no limit 1", [':trade_no'=>$trade_no]);
$userrow = $DB->find('user', 'gid,ordername,channelinfo', ['uid'=>$order['uid']]);
if (!$order) {
$channelinfo = $userrow?$userrow['channelinfo']:null;
$channel = \lib\Channel::get($trade_no, $channelinfo);
if(!$channel) throw new Exception('该订单号不存在,请返回来源地重新发起请求!');
$trade_no = null;
}else{
$channelinfo = $userrow?$userrow['channelinfo']:null;
$channel = $order['subchannel'] > 0 ? \lib\Channel::getSub($order['subchannel']) : \lib\Channel::get($order['channel'], $channelinfo);
if(!$channel) throw new Exception('当前支付通道信息不存在');
$channel['apptype'] = explode(',',$channel['apptype']);
if(!empty($userrow['ordername']))$conf['ordername']=$userrow['ordername'];
$ordername = !empty($conf['ordername'])?ordername_replace($conf['ordername'],$order['name'],$order['uid'],$trade_no,$order['out_trade_no']):$order['name'];
$order['plugin'] = $channel['plugin'];
}
$groupconfig = getGroupConfig($userrow['gid']);
$conf = array_merge($conf, $groupconfig);
$result = self::loadClass($channel['plugin'], $func, $trade_no);
if($func == 'submit') {
$result['submit'] = true;
}
return $result;
}else{
throw new Exception('URL参数不符合规范');
}
}
static public function loadForSubmit($plugin, $trade_no, $ismapi=false){
global $DB,$conf,$order,$channel,$ordername,$userrow;
if(preg_match('/^(.[a-zA-Z0-9]+)$/',$plugin) && preg_match('/^(.[0-9]+)$/',$trade_no)){
$func = 'submit';
if($ismapi) $func = 'mapi';
$channelinfo = $userrow?$userrow['channelinfo']:null;
$channel = $order['subchannel'] > 0 ? \lib\Channel::getSub($order['subchannel']) : \lib\Channel::get($order['channel'], $channelinfo);
if(!$channel)throw new Exception('当前支付通道信息不存在');
$channel['apptype'] = explode(',',$channel['apptype']);
if(!empty($userrow['ordername']))$conf['ordername']=$userrow['ordername'];
$ordername = !empty($conf['ordername'])?ordername_replace($conf['ordername'],$order['name'],$order['uid'],$trade_no,$order['out_trade_no']):$order['name'];
return self::loadClass($plugin, $func, $trade_no);
}else{
throw new Exception('URL参数不符合规范');
}
}
static public function loadClass($plugin, $func, $trade_no){
$filename = PLUGIN_ROOT.$plugin.'/'.$plugin.'_plugin.php';
$classname = '\\'.$plugin.'_plugin';
if (file_exists($filename)) {
if(!defined("IN_PLUGIN")) define("IN_PLUGIN", true);
define("PAY_ROOT", PLUGIN_ROOT.$plugin.'/');
define("TRADE_NO", $trade_no);
include $filename;
if (class_exists($classname, false) && method_exists($classname, $func)) {
return $classname::$func();
} else {
if($func == 'mapi' && class_exists($classname, false) && method_exists($classname, 'submit')){
global $siteurl;
return ['type'=>'jump','url'=>$siteurl.'pay/submit/'.TRADE_NO.'/'];
}else{
throw new Exception('插件方法不存在:'.$func);
}
}
}else{
throw new Exception('Pay file not found');
}
}
static public function exists($name){
$filename = PLUGIN_ROOT.$name.'/'.$name.'_plugin.php';
if(file_exists($filename)){
return true;
}else{
return false;
}
}
static public function isrefund($name){
$filename = PLUGIN_ROOT.$name.'/'.$name.'_plugin.php';
$classname = '\\'.$name.'_plugin';
if(file_exists($filename)){
include $filename;
if (class_exists($classname, false) && method_exists($classname, 'refund')) {
return true;
}else{
return false;
}
}else{
return false;
}
}
static public function refund($refund_no, $trade_no, $money, &$message){
global $order,$channel,$DB;
if(!preg_match('/^(.[0-9]+)$/',$trade_no))return false;
$channel = $order['subchannel'] > 0 ? \lib\Channel::getSub($order['subchannel']) : \lib\Channel::get($order['channel'], $DB->findColumn('user', 'channelinfo', ['uid'=>$order['uid']]));
if(!$channel){
$message = '当前支付通道信息不存在';
return false;
}
$order['refund_no'] = $refund_no;
$order['refundmoney'] = $money;
$filename = PLUGIN_ROOT.$channel['plugin'].'/'.$channel['plugin'].'_plugin.php';
$classname = '\\'.$channel['plugin'].'_plugin';
$func = 'refund';
if($order['combine'] == 1) $func = 'refund_combine';
if(file_exists($filename)){
include $filename;
if (class_exists($classname, false) && method_exists($classname, $func)) {
if(!defined("IN_PLUGIN")) define("IN_PLUGIN", true);
define("PAY_ROOT", PLUGIN_ROOT.$channel['plugin'].'/');
define("TRADE_NO", $trade_no);
$result = $classname::$func($order);
if($result && $result['code']==0){
return true;
}else{
$message = $result['msg'];
return false;
}
}else{
$message = '当前支付通道不支持API退款';
return false;
}
}else{
$message = '支付插件不存在';
return false;
}
}
static public function loadForAdmin($func){
global $channel;
$filename = PLUGIN_ROOT.$channel['plugin'].'/'.$channel['plugin'].'_plugin.php';
$classname = '\\'.$channel['plugin'].'_plugin';
if(file_exists($filename)){
include_once $filename;
if (class_exists($classname, false) && method_exists($classname, $func)) {
if(!defined("IN_PLUGIN")) define("IN_PLUGIN", true);
define("PAY_ROOT", PLUGIN_ROOT.$channel['plugin'].'/');
return $classname::$func($channel);
}else{
throw new Exception('插件方法不存在:'.$func);
}
}else{
throw new Exception('支付插件不存在');
}
}
static public function call($func, $channel, $bizParam = null){
$filename = PLUGIN_ROOT.$channel['plugin'].'/'.$channel['plugin'].'_plugin.php';
$classname = '\\'.$channel['plugin'].'_plugin';
if(file_exists($filename)){
include_once $filename;
if (class_exists($classname, false) && method_exists($classname, $func)) {
if($bizParam){
$result = $classname::$func($channel, $bizParam);
}else{
$result = $classname::$func($channel);
}
return $result;
}else{
return ['code'=>-1, 'msg'=>'插件方法不存在:'.$func];
}
}else{
return ['code'=>-1, 'msg'=>'支付插件不存在'];
}
}
static public function updateAll(){
global $DB;
$DB->exec("TRUNCATE TABLE pre_plugin");
$list = self::getList();
foreach($list as $name){
if($config = self::getConfig($name)){
if($config['name']!=$name)continue;
$DB->insert('plugin',['name'=>$config['name'], 'showname'=>$config['showname'], 'author'=>$config['author'], 'link'=>$config['link'], 'types'=>implode(',',$config['types']), 'transtypes'=>$config['transtypes']?implode(',',$config['transtypes']):null]);
}
}
return true;
}
static public function get($name){
global $DB;
$result = $DB->getRow("SELECT * FROM pre_plugin WHERE name=:name", [':name'=>$name]);
return $result;
}
static public function getAll(){
global $DB;
$result = $DB->getAll("SELECT * FROM pre_plugin ORDER BY name ASC");
return $result;
}
}
+109
View File
@@ -0,0 +1,109 @@
<?php
namespace lib\ProfitSharing;
require_once PLUGIN_ROOT . 'adapay/inc/Build.class.php';
use Exception;
class Adapay implements IProfitSharing
{
static $paytype = 'adapay';
private $channel;
private $service;
function __construct($channel){
$this->channel = $channel;
$pay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php');
$this->service = \AdaPay::config($pay_config);
}
//请求分账
public function submit($trade_no, $api_trade_no, $account, $name, $money){
global $DB;
$order_money = $DB->findColumn('order', 'realmoney', ['trade_no'=>$trade_no]);
if(strpos($account, '|')){
$accounts = explode('|', $account);
$psorder = CommUtil::getOrder($trade_no);
$rates = explode('|', $psorder['rate']);
$div_members = [];
$allmoney = 0;
foreach($accounts as $i=>$account){
$rate = isset($rates[$i]) ? $rates[$i] : $rates[0];
$money = round($order_money * $rate / 100, 2);
$div_members[] = ['member_id'=>$account, 'amount' => sprintf('%.2f' , $money), 'fee_flag'=>$i==0?'Y':'N'];
$allmoney += $money;
}
if($order_money > $allmoney){
$psmoney2 = round($order_money-$allmoney, 2);
$div_members[] = ['member_id'=>'0', 'amount' => sprintf('%.2f' , $psmoney2), 'fee_flag'=>'N'];
}
}else{
$psmoney2 = round($order_money-$money, 2);
$div_members = [];
$div_members[] = ['member_id'=>$account, 'amount' => sprintf('%.2f' , $money), 'fee_flag'=>'Y'];
if($psmoney2 > 0){
$div_members[] = ['member_id'=>'0', 'amount' => sprintf('%.2f' , $psmoney2), 'fee_flag'=>'N'];
}
}
$params = [
'payment_id' => $api_trade_no,
'order_no' => date("YmdHis").rand(11111,99999),
'confirm_amt' => $order_money,
'div_members' => $div_members,
];
try{
$result = $this->service->createPaymentConfirm($params);
return ['code'=>1, 'msg'=>'分账成功', 'settle_no'=>$result['id']];
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
//查询分账结果
public function query($trade_no, $api_trade_no, $settle_no){
try{
$result = $this->service->queryPaymentConfirm($settle_no);
return ['code'=>0, 'status'=>1];
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
//解冻剩余资金
public function unfreeeze($trade_no, $api_trade_no){
global $DB;
$order_money = $DB->findColumn('order', 'realmoney', ['trade_no'=>$trade_no]);
$params = [
'payment_id' => $api_trade_no,
'order_no' => date("YmdHis").rand(11111,99999),
'reverse_amt' => $order_money,
];
try{
$result = $this->service->createPaymentReverse($params);
return ['code'=>0, 'msg'=>'解冻剩余资金成功', 'settle_no'=>$result['id']];
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
//分账回退
public function return($trade_no, $api_trade_no, $account, $money){
return ['code'=>-1,'msg'=>'不支持当前操作'];
}
//添加分账接收方
public function addReceiver($account, $name = null){
return ['code'=>0, 'msg'=>'添加分账接收方成功'];
}
//删除分账接收方
public function deleteReceiver($account){
return ['code'=>0, 'msg'=>'删除分账接收方成功'];
}
}
+159
View File
@@ -0,0 +1,159 @@
<?php
namespace lib\ProfitSharing;
use Exception;
class Alipay implements IProfitSharing
{
static $paytype = 'alipay';
private $channel;
private $service;
function __construct($channel){
$this->channel = $channel;
$alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php');
$this->service = new \Alipay\AlipaySettleService($alipay_config);
}
//请求分账
public function submit($trade_no, $api_trade_no, $account, $name, $money){
if(strpos($account, '|')){
global $DB;
$accounts = explode('|', $account);
$psorder = CommUtil::getOrder($trade_no);
$rates = explode('|', $psorder['rate']);
$order_money = $DB->findColumn('order', 'realmoney', ['trade_no'=>$trade_no]);
$receivers = [];
foreach($accounts as $i=>$account){
$rate = isset($rates[$i]) ? $rates[$i] : $rates[0];
$money = round($order_money * $rate / 100, 2);
$type = self::get_alipay_account_type($account);
$receivers[] = [
'trans_in_type' => $type,
'trans_in' => $account,
'amount' => $money
];
}
$bizContent = array(
'out_request_no' => date("YmdHis").rand(11111,99999),
'trade_no' => $api_trade_no,
'royalty_parameters' => $receivers,
'extend_params' => [
'royalty_finish' => 'true'
]
);
try{
$result = $this->service->aopExecute('alipay.trade.order.settle', $bizContent);
return ['code'=>1, 'msg'=>'分账成功', 'settle_no'=>$result['settle_no']];
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
$type = self::get_alipay_account_type($account);
try{
$result = $this->service->order_settle($api_trade_no, $type, $account, $money);
return ['code'=>1, 'msg'=>'分账成功', 'settle_no'=>$result['settle_no']];
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
//查询分账结果
public function query($trade_no, $api_trade_no, $settle_no){
try{
$result = $this->service->order_settle_query($settle_no);
$receiver = $result['royalty_detail_list'][0];
if($receiver['state'] == 'SUCCESS'){
return ['code'=>0, 'status'=>1];
}elseif($receiver['state'] == 'FAIL'){
return ['code'=>0, 'status'=>2, 'reason'=>'['.$receiver['error_code'].']'.$receiver['error_desc']];
}else{
return ['code'=>0, 'status'=>0];
}
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
//解冻剩余资金
public function unfreeeze($trade_no, $api_trade_no){
try{
$this->service->order_settle_unfreeze($api_trade_no);
return ['code'=>0, 'msg'=>'解冻剩余资金成功'];
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
//分账回退
public function return($trade_no, $api_trade_no, $account, $money){
$type = self::get_alipay_account_type($account);
try{
$this->service->order_settle_refund($api_trade_no, $type, $account, $money);
return ['code'=>0, 'msg'=>'退分账成功'];
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
//添加分账接收方
public function addReceiver($account, $name = null){
if(strpos($account, '|')){
$accounts = explode('|', $account);
$names = explode('|', $name);
foreach($accounts as $i => $account){
$type = self::get_alipay_account_type($account);
try{
$this->service->relation_bind($type, $account, $name ? $names[$i] : null);
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
return ['code'=>0, 'msg'=>'添加分账接收方成功'];
}
$type = self::get_alipay_account_type($account);
try{
$this->service->relation_bind($type, $account, $name);
return ['code'=>0, 'msg'=>'添加分账接收方成功'];
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
//删除分账接收方
public function deleteReceiver($account){
if(strpos($account, '|')){
$accounts = explode('|', $account);
foreach($accounts as $account){
$type = self::get_alipay_account_type($account);
try{
$this->service->relation_unbind($type, $account);
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
return ['code'=>0, 'msg'=>'删除分账接收方成功'];
}
$type = self::get_alipay_account_type($account);
try{
$this->service->relation_unbind($type, $account);
return ['code'=>0, 'msg'=>'删除分账接收方成功'];
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
private static function get_alipay_account_type($account){
if(is_numeric($account) && substr($account,0,4)=='2088' && strlen($account)==16)$type = 'userId';
else $type = 'loginName';
return $type;
}
}
+44
View File
@@ -0,0 +1,44 @@
<?php
namespace lib\ProfitSharing;
use Exception;
class Chinaums implements IProfitSharing
{
static $paytype = 'chinaums';
private $channel;
private $service;
function __construct($channel){
$this->channel = $channel;
}
//请求分账
public function submit($trade_no, $api_trade_no, $account, $name, $money){
}
//查询分账结果
public function query($trade_no, $api_trade_no, $settle_no){
}
//解冻剩余资金
public function unfreeeze($trade_no, $api_trade_no){
}
//分账回退
public function return($trade_no, $api_trade_no, $account, $money){
}
//添加分账接收方
public function addReceiver($account, $name = null){
return ['code'=>0, 'msg'=>'添加分账接收方成功'];
}
//删除分账接收方
public function deleteReceiver($account){
return ['code'=>0, 'msg'=>'删除分账接收方成功'];
}
}
+104
View File
@@ -0,0 +1,104 @@
<?php
namespace lib\ProfitSharing;
use Exception;
class CommUtil
{
public static $plugins = ['alipay','alipaysl','alipayd','wxpayn','wxpaynp','yeepay','yseqt','chinaums','dinpay','adapay'];
public static $no_order_plugins = ['chinaums','dinpay'];
public static function getModel($channel){
if($channel['plugin'] == 'alipay' || $channel['plugin'] == 'alipaysl' || $channel['plugin'] == 'alipayd'){
return new Alipay($channel);
}elseif($channel['plugin'] == 'wxpayn' || $channel['plugin'] == 'wxpaynp'){
return new Wxpay($channel);
}elseif($channel['plugin'] == 'yeepay'){
return new Yeepay($channel);
}elseif($channel['plugin'] == 'yseqt'){
return new Yseqt($channel);
}elseif($channel['plugin'] == 'chinaums'){
return new Chinaums($channel);
}elseif($channel['plugin'] == 'dinpay'){
return new Dinpay($channel);
}elseif($channel['plugin'] == 'adapay'){
return new Adapay($channel);
}
return false;
}
public static function getReceiver($id){
global $DB;
return $DB->find('psreceiver', '*', ['id'=>$id]);
}
public static function getOrder($trade_no){
global $DB;
return $DB->getRow("SELECT A.*,B.channel,B.account,B.name,B.rate FROM pre_psorder A LEFT JOIN pre_psreceiver B ON A.rid=B.id WHERE A.trade_no=:trade_no", [':trade_no'=>$trade_no]);
}
//订单分账定时任务
public static function task(){
global $DB;
$limit = 10; //每次查询分账的订单数量
$list = $DB->getAll("SELECT A.*,B.channel,B.account,B.name,B.uid psuid,C.uid,C.subchannel FROM pre_psorder A INNER JOIN pre_psreceiver B ON B.id=A.rid LEFT JOIN pre_order C ON C.trade_no=A.trade_no WHERE A.status=1 ORDER BY A.id ASC LIMIT {$limit}");
foreach($list as $srow){
self::process_item($srow);
}
$limit = 10; //每次提交分账的订单数量
$list = $DB->getAll("SELECT A.*,B.channel,B.account,B.name,B.uid psuid,C.uid,C.subchannel FROM pre_psorder A INNER JOIN pre_psreceiver B ON B.id=A.rid LEFT JOIN pre_order C ON C.trade_no=A.trade_no WHERE A.status=0 AND (A.addtime<=DATE_SUB(NOW(), INTERVAL 60 SECOND) AND A.delay=0 OR A.addtime<=DATE_SUB(NOW(), INTERVAL 24 HOUR) AND A.delay=1) ORDER BY A.id ASC LIMIT {$limit}");
foreach($list as $srow){
self::process_item($srow);
}
}
//处理一个订单分账任务
public static function process_item($row){
global $DB;
$id = $row['id'];
$channel = $row['subchannel'] > 0 ? \lib\Channel::getSub($row['subchannel']) : \lib\Channel::get($row['channel'], $row['uid']?$DB->findColumn('user', 'channelinfo', ['uid'=>$row['uid']]):null);
if(!$channel) return;
$model = self::getModel($channel);
// status:0-待分账,1-已提交,2-成功,3-失败
if($row['status']==0){
if($row['money'] == 0){
$DB->update('psorder', ['status'=>3,'result'=>'分账金额为0'], ['id'=>$id]);
echo $row['trade_no'].' 分账金额为0<br/>';
return;
}
$result = $model->submit($row['trade_no'], $row['api_trade_no'], $row['account'], $row['name'], $row['money']);
if($result['code'] == 0){
$DB->update('psorder', ['status'=>1,'settle_no'=>$result['settle_no']], ['id'=>$id]);
}elseif($result['code'] == 1){
$DB->update('psorder', ['status'=>2,'settle_no'=>$result['settle_no']], ['id'=>$id]);
if(!empty($row['psuid']) && $channel['mode']==0){
changeUserMoney($row['psuid'], $row['money'], false, '订单分账', $row['trade_no']);
}
}elseif($result['code'] == -1){
$DB->update('psorder', ['status'=>3,'result'=>$result['msg']], ['id'=>$id]);
}
echo $row['trade_no'].' '.$result['msg'].'<br/>';
}elseif($row['status']==1){
$result = $model->query($row['trade_no'], $row['api_trade_no'], $row['settle_no']);
if($result['code']==0){
if($result['status']==1){
$DB->update('psorder', ['status'=>2], ['id'=>$id]);
if(!empty($row['psuid']) && $channel['mode']==0){
changeUserMoney($row['psuid'], $row['money'], false, '订单分账', $row['trade_no']);
}
$result = '分账成功';
}elseif($result['status']==2){
$DB->update('psorder', ['status'=>3,'result'=>$result['reason']], ['id'=>$id]);
$result = '分账失败:'.$result['reason'];
}else{
$result = '正在分账';
}
echo $row['trade_no'].' '.$result.'<br/>';
}else{
echo $row['trade_no'].' 查询失败:'.$result['msg'].'<br/>';
}
}
}
}
+44
View File
@@ -0,0 +1,44 @@
<?php
namespace lib\ProfitSharing;
use Exception;
class Dinpay implements IProfitSharing
{
static $paytype = 'dinpay';
private $channel;
private $service;
function __construct($channel){
$this->channel = $channel;
}
//请求分账
public function submit($trade_no, $api_trade_no, $account, $name, $money){
}
//查询分账结果
public function query($trade_no, $api_trade_no, $settle_no){
}
//解冻剩余资金
public function unfreeeze($trade_no, $api_trade_no){
}
//分账回退
public function return($trade_no, $api_trade_no, $account, $money){
}
//添加分账接收方
public function addReceiver($account, $name = null){
return ['code'=>0, 'msg'=>'添加分账接收方成功'];
}
//删除分账接收方
public function deleteReceiver($account){
return ['code'=>0, 'msg'=>'删除分账接收方成功'];
}
}
@@ -0,0 +1,25 @@
<?php
namespace lib\ProfitSharing;
interface IProfitSharing
{
//请求分账
function submit($trade_no, $api_trade_no, $account, $name, $money);
//查询分账结果
function query($trade_no, $api_trade_no, $settle_no);
//解冻剩余资金
function unfreeeze($trade_no, $api_trade_no);
//分账回退
function return($trade_no, $api_trade_no, $account, $money);
//添加分账接收方
function addReceiver($account, $name = null);
//删除分账接收方
function deleteReceiver($account);
}
+219
View File
@@ -0,0 +1,219 @@
<?php
namespace lib\ProfitSharing;
use Exception;
class Wxpay implements IProfitSharing
{
static $paytype = 'wxpay';
private $channel;
private $service;
private $ecommerce;
function __construct($channel){
$this->channel = $channel;
$wechatpay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php');
$this->ecommerce = $wechatpay_config['ecommerce'];
$this->service = new \WeChatPay\V3\ProfitsharingService($wechatpay_config);
}
//请求分账
public function submit($trade_no, $api_trade_no, $account, $name, $money){
if(strpos($account, '|')){
global $DB;
$accounts = explode('|', $account);
$psorder = CommUtil::getOrder($trade_no);
$rates = explode('|', $psorder['rate']);
$order_money = $DB->findColumn('order', 'realmoney', ['trade_no'=>$trade_no]);
$receivers = [];
foreach($accounts as $i=>$account){
$rate = isset($rates[$i]) ? $rates[$i] : $rates[0];
$money = round($order_money * $rate / 100, 2);
$type = self::get_wxpay_account_type($account);
if($this->ecommerce){
$receivers[] = [
'type' => $type,
'receiver_account' => $account,
'amount' => intval(round($money*100)),
'description' => '订单分账'
];
}else{
$receivers[] = [
'type' => $type,
'account' => $account,
'amount' => intval(round($money*100)),
'description' => '订单分账'
];
}
}
if($this->ecommerce){
$param = [
'transaction_id' => $api_trade_no,
'out_order_no' => $trade_no,
'receivers' => $receivers,
'finish' => true,
];
}else{
$param = [
'transaction_id' => $api_trade_no,
'out_order_no' => $trade_no,
'receivers' => $receivers,
'unfreeze_unsplit' => true,
];
}
try{
$result = $this->service->submit($param);
return ['code'=>0, 'msg'=>'请求分账成功', 'settle_no'=>$result['order_id']];
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
$type = self::get_wxpay_account_type($account);
if($this->ecommerce){
$param = [
'transaction_id' => $api_trade_no,
'out_order_no' => $trade_no,
'receivers' => [
[
'type' => $type,
'receiver_account' => $account,
'amount' => intval(round($money*100)),
'description' => '订单分账'
]
],
'finish' => true,
];
}else{
$param = [
'transaction_id' => $api_trade_no,
'out_order_no' => $trade_no,
'receivers' => [
[
'type' => $type,
'account' => $account,
'amount' => intval(round($money*100)),
'description' => '订单分账'
]
],
'unfreeze_unsplit' => true,
];
}
try{
$result = $this->service->submit($param);
return ['code'=>0, 'msg'=>'请求分账成功', 'settle_no'=>$result['order_id']];
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
//查询分账结果
public function query($trade_no, $api_trade_no, $settle_no){
$reason_desc = ['ACCOUNT_ABNORMAL'=>'分账接收账户异常', 'NO_RELATION'=>'分账关系已解除', 'RECEIVER_HIGH_RISK'=>'高风险接收方', 'RECEIVER_REAL_NAME_NOT_VERIFIED'=>'接收方未实名', 'NO_AUTH'=>'分账权限已解除', 'RECEIVER_RECEIPT_LIMIT'=>'接收方已达收款限额', 'PAYER_ACCOUNT_ABNORMAL'=>'分出方账户异常', 'INVALID_REQUEST'=>'描述参数设置失败'];
try{
$result = $this->service->query($trade_no, $api_trade_no);
if(isset($result['state']) && $result['state'] == 'FINISHED' || isset($result['status']) && $result['status'] == 'FINISHED'){
$receiver = $result['receivers'][0];
if($receiver['result'] == 'SUCCESS'){
return ['code'=>0, 'status'=>1];
}elseif($receiver['result'] == 'CLOSED'){
return ['code'=>0, 'status'=>2, 'reason'=>'['.$receiver['fail_reason'].']'.$reason_desc[$receiver['fail_reason']]];
}else{
return ['code'=>0, 'status'=>0];
}
}else{
return ['code'=>0, 'status'=>0];
}
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
//解冻剩余资金
public function unfreeeze($trade_no, $api_trade_no){
try{
$this->service->unfreeze($trade_no, $api_trade_no);
return ['code'=>0, 'msg'=>'解冻剩余资金成功'];
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
//分账回退
public function return($trade_no, $api_trade_no, $account, $money){
$type = self::get_wxpay_account_type($account);
if($type == 'MERCHANT_ID'){
$params = [
'out_order_no' => $trade_no,
'out_return_no' => 'REF'.$trade_no,
'return_mchid' => $account,
'amount' => intval(round($money*100)),
'description' => '分账回退'
];
try{
$this->service->return($params);
return ['code'=>0, 'msg'=>'分账回退成功'];
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}else{
return ['code'=>-1,'msg'=>'分账到个人账户不支持回退'];
}
}
//添加分账接收方
public function addReceiver($account, $name = null){
if(strpos($account, '|')){
$accounts = explode('|', $account);
$names = explode('|', $name);
foreach($accounts as $i => $account){
$type = self::get_wxpay_account_type($account);
try{
$this->service->addReceiver($type, $account, $name ? $names[$i] : null);
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
return ['code'=>0, 'msg'=>'添加分账接收方成功'];
}
$type = self::get_wxpay_account_type($account);
try{
$this->service->addReceiver($type, $account, $name);
return ['code'=>0, 'msg'=>'添加分账接收方成功'];
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
//删除分账接收方
public function deleteReceiver($account){
if(strpos($account, '|')){
$accounts = explode('|', $account);
foreach($accounts as $account){
$type = self::get_wxpay_account_type($account);
try{
$this->service->deleteReceiver($type, $account);
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
return ['code'=>0, 'msg'=>'删除分账接收方成功'];
}
$type = self::get_wxpay_account_type($account);
try{
$this->service->deleteReceiver($type, $account);
return ['code'=>0, 'msg'=>'删除分账接收方成功'];
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
private static function get_wxpay_account_type($account){
if(is_numeric($account))$type = 'MERCHANT_ID';
else $type = 'PERSONAL_OPENID';
return $type;
}
}
+137
View File
@@ -0,0 +1,137 @@
<?php
namespace lib\ProfitSharing;
require(PLUGIN_ROOT.'yeepay/inc/YopClient.php');
use Exception;
class Yeepay implements IProfitSharing
{
static $paytype = 'yeepay';
private $channel;
private $service;
function __construct($channel){
$this->channel = $channel;
$this->service = new \Yeepay\YopClient($channel['appkey'], $channel['appsecret']);
}
//请求分账
public function submit($trade_no, $api_trade_no, $account, $name, $money){
$divideDetail = [
[
'ledgerNo' => $account,
'amount' => $money,
'ledgerType' => 'MERCHANT2MERCHANT',
]
];
$params = [
'parentMerchantNo' => $this->channel['appid'],
'merchantNo' => empty($this->channel['appmchid'])?$this->channel['appid']:$this->channel['appmchid'],
'orderId' => $trade_no,
'uniqueOrderNo' => $api_trade_no,
'divideRequestId' => 'F'.$trade_no,
'divideDetail' => json_encode($divideDetail),
'isUnfreezeResidualAmount' => 'TRUE',
];
try{
$result = $this->service->post('/rest/v1.0/divide/apply', $params);
if($result['code'] == 'OPR00000'){
if($result['status'] == 'SUCCESS'){
return ['code'=>1, 'msg'=>'分账成功', 'settle_no'=>$result['divideRequestId']];
}else{
return ['code'=>0, 'msg'=>'请求分账成功', 'settle_no'=>$result['divideRequestId']];
}
}else{
throw new Exception('['.$result['code'].']'.$result['message']);
}
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
//查询分账结果
public function query($trade_no, $api_trade_no, $settle_no){
$params = [
'parentMerchantNo' => $this->channel['appid'],
'merchantNo' => empty($this->channel['appmchid'])?$this->channel['appid']:$this->channel['appmchid'],
'divideRequestId' => $settle_no,
'orderId' => $trade_no,
'uniqueOrderNo' => $api_trade_no,
];
try{
$result = $this->service->get('/rest/v1.0/divide/query', $params);
if($result['code'] == 'OPR00000'){
if($result['status'] == 'SUCCESS'){
return ['code'=>0, 'status'=>1];
}elseif($result['status'] == 'FAIL'){
return ['code'=>0, 'status'=>2, 'reason'=>$result['message']];
}else{
return ['code'=>0, 'status'=>0];
}
}else{
throw new Exception('['.$result['code'].']'.$result['message']);
}
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
//解冻剩余资金
public function unfreeeze($trade_no, $api_trade_no){
$params = [
'parentMerchantNo' => $this->channel['appid'],
'merchantNo' => empty($this->channel['appmchid'])?$this->channel['appid']:$this->channel['appmchid'],
'divideRequestId' => 'C'.$trade_no,
'orderId' => $trade_no,
'uniqueOrderNo' => $api_trade_no,
];
try{
$result = $this->service->post('/rest/v1.0/divide/complete', $params);
if($result['code'] == 'OPR00000'){
return ['code'=>0, 'msg'=>'解冻剩余资金成功'];
}else{
throw new Exception('['.$result['code'].']'.$result['message']);
}
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
//分账回退
public function return($trade_no, $api_trade_no, $account, $money){
return ['code'=>-1, 'msg'=>'暂不支持分账回退'];
$params = [
'parentMerchantNo' => $this->channel['appid'],
'merchantNo' => empty($this->channel['appmchid'])?$this->channel['appid']:$this->channel['appmchid'],
'divideBackRequestId' => 'B'.$trade_no,
'divideRequestId' => $settle_no,
'orderId' => $trade_no,
'uniqueOrderNo' => $api_trade_no,
'divideBackDetail' => '',
];
try{
$result = $this->service->post('/rest/v1.0/divide/back', $params);
if($result['code'] == 'OPR00000'){
return ['code'=>0, 'msg'=>'退分账成功'];
}else{
throw new Exception('['.$result['code'].']'.$result['message']);
}
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
//添加分账接收方
public function addReceiver($account, $name = null){
return ['code'=>0, 'msg'=>'添加分账接收方成功'];
}
//删除分账接收方
public function deleteReceiver($account){
return ['code'=>0, 'msg'=>'删除分账接收方成功'];
}
}
+145
View File
@@ -0,0 +1,145 @@
<?php
namespace lib\ProfitSharing;
require(PLUGIN_ROOT.'yseqt/inc/YseqtClient.php');
use Exception;
class Yseqt implements IProfitSharing
{
static $paytype = 'yseqt';
private $channel;
private $service;
function __construct($channel){
$this->channel = $channel;
$this->service = new \YseqtClient($channel['appid'], $channel['appkey']);
}
//请求分账
public function submit($trade_no, $api_trade_no, $account, $name, $money){
global $DB;
$order_money = $DB->findColumn('order', 'realmoney', ['trade_no'=>$trade_no]);
$divisionList = [
[
'divisionMercId' => $account,
'isChargeFee' => 'N',
'divAmount' => $money,
],
[
'divisionMercId' => $this->channel['appmchid'],
'isChargeFee' => 'Y',
'divAmount' => round($order_money-$money, 2),
]
];
$requestNo = date('YmdHis').rand(1000,9999);
$params = [
'requestNo' => $requestNo,
'payeeMerchantNo' => $this->channel['appmchid'],
'origRequestNo' => $trade_no,
'amount' => $order_money,
'isDivision' => 'Y',
'divisionMode' => '02',
'divisionList' => $divisionList,
];
try{
$result = $this->service->execute('divisionRegister', $params);
if($result['subCode'] == 'COM000'){
if($result['state'] == 'SPLIT_SUCCESS'){
return ['code'=>1, 'msg'=>'预分账成功', 'settle_no'=>$result['requestNo']];
}elseif($result['state'] == 'SUCCESS'){
return ['code'=>1, 'msg'=>'分账成功', 'settle_no'=>$result['requestNo']];
}else{
return ['code'=>0, 'msg'=>'受理成功', 'settle_no'=>$result['requestNo']];
}
}else{
throw new Exception($result['subMsg']);
}
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
//查询分账结果
public function query($trade_no, $api_trade_no, $settle_no){
$params = [
'origRequestNo' => $trade_no,
];
try{
$result = $this->service->execute('divisionQuery', $params);
if($result['subCode'] == 'COM000'){
if($result['state'] == 'SPLIT_SUCCESS' || $result['state'] == 'SUCCESS'){
return ['code'=>0, 'status'=>1];
}elseif($result['state'] == 'FAILED'){
return ['code'=>0, 'status'=>2, 'reason'=>$result['note']];
}else{
return ['code'=>0, 'status'=>0];
}
}else{
throw new Exception($result['subMsg']);
}
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
//解冻剩余资金
public function unfreeeze($trade_no, $api_trade_no){
global $DB;
$requestNo = date('YmdHis').rand(1000,9999);
$order_money = $DB->findColumn('order', 'realmoney', ['trade_no'=>$trade_no]);
$params = [
'requestNo' => $requestNo,
'payeeMerchantNo' => $this->channel['appmchid'],
'origRequestNo' => $trade_no,
'amount' => $order_money,
'isDivision' => 'N',
'divisionMode' => '02',
];
try{
$result = $this->service->execute('divisionRegister', $params);
if($result['subCode'] == 'COM000'){
return ['code'=>0, 'msg'=>'解冻剩余资金成功'];
}else{
throw new Exception($result['subMsg']);
}
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
//分账回退
public function return($trade_no, $api_trade_no, $account, $money){
$requestNo = date('YmdHis').rand(1000,9999);
$params = [
'requestNo' => $requestNo,
'payeeMerchantNo' => $this->channel['appmchid'],
'origRequestNo' => $trade_no,
'divisionMercId' => $account,
'amount' => $money,
];
try{
$result = $this->service->execute('divisionBack', $params);
if($result['subCode'] == 'COM000'){
return ['code'=>0, 'msg'=>'退分账成功'];
}else{
throw new Exception($result['subMsg']);
}
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>$e->getMessage()];
}
}
//添加分账接收方
public function addReceiver($account, $name = null){
return ['code'=>0, 'msg'=>'添加分账接收方成功'];
}
//删除分账接收方
public function deleteReceiver($account){
return ['code'=>0, 'msg'=>'删除分账接收方成功'];
}
}
+124
View File
@@ -0,0 +1,124 @@
<?php
namespace lib;
/* PHP SDK
* @version 2.0.0
* @author connect@qq.com
* @copyright © 2013, Tencent Corporation. All rights reserved.
*/
/*
* @brief QC类,api外部对象,调用接口全部依赖于此对象
* */
class QC{
const VERSION = "2.0";
const GET_AUTH_CODE_URL = "https://graph.qq.com/oauth2.0/authorize";
const GET_ACCESS_TOKEN_URL = "https://graph.qq.com/oauth2.0/token";
const GET_OPENID_URL = "https://graph.qq.com/oauth2.0/me";
private $appid;
private $appkey;
private $callback;
function __construct($QC_config){
$this->appid = $QC_config["appid"];
$this->appkey = $QC_config["appkey"];
$this->callback = $QC_config['callback'];
}
public function qq_login($is_geturl = false){
$state = md5(uniqid(rand(), TRUE));
$_SESSION['Oauth_state'] = $state;
//-------构造请求参数列表
$keysArr = array(
"response_type" => "code",
"client_id" => $this->appid,
"redirect_uri" => $this->callback,
"state" => $state
);
$login_url = self::GET_AUTH_CODE_URL.'?'.http_build_query($keysArr);
if($is_geturl){
return $login_url;
}
header("Location: $login_url");
}
public function qq_callback(){
if($_GET['state'] != $_SESSION['Oauth_state']){
sysmsg("<h2>The state does not match. You may be a victim of CSRF.</h2>");
}
//-------请求参数列表
$keysArr = array(
"grant_type" => "authorization_code",
"client_id" => $this->appid,
"redirect_uri" => $this->callback,
"client_secret" => $this->appkey,
"code" => $_GET['code']
);
//------构造请求access_token的url
$token_url = self::GET_ACCESS_TOKEN_URL.'?'.http_build_query($keysArr);
$response = $this->get_curl($token_url);
if(strpos($response, "callback") !== false){
$lpos = strpos($response, "(");
$rpos = strrpos($response, ")");
$response = substr($response, $lpos + 1, $rpos - $lpos -1);
$msg = json_decode($response);
if(isset($msg->error)){
sysmsg('<h3>error:</h3>'.$msg->error.'<h3>msg :</h3>'.$msg->error_description);
}
}
$params = array();
parse_str($response, $params);
return $params["access_token"];
}
public function get_openid($access_token){
//-------请求参数列表
$keysArr = array(
"access_token" => $access_token
);
$graph_url = self::GET_OPENID_URL.'?'.http_build_query($keysArr);
$response = $this->get_curl($graph_url);
//--------检测错误是否发生
if(strpos($response, "callback") !== false){
$lpos = strpos($response, "(");
$rpos = strrpos($response, ")");
$response = substr($response, $lpos + 1, $rpos - $lpos -1);
}
$user = json_decode($response);
if(isset($user->error)){
sysmsg('<h3>error:</h3>'.$user->error.'<h3>msg :</h3>'.$user->error_description);
}
//------记录openid
return $user->openid;
}
public function get_curl($url){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_USERAGENT,'Mozilla/5.0 (Linux; U; Android 4.4.1; zh-cn) AppleWebKit/533.1 (KHTML, like Gecko)Version/4.0 MQQBrowser/5.5 Mobile Safari/533.1');
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
$ret = curl_exec($ch);
curl_close($ch);
return $ret;
}
}
+110
View File
@@ -0,0 +1,110 @@
<?php
namespace lib;
class QcloudFaceid {
private $SecretId;
private $SecretKey;
private $endpoint = "faceid.tencentcloudapi.com";
private $service = "faceid";
private $version = "2018-03-01";
private $region = "ap-guangzhou";
function __construct($SecretId, $SecretKey){
$this->SecretId = $SecretId;
$this->SecretKey = $SecretKey;
}
public function GetRealNameAuthToken($Name, $IDCard, $CallbackURL){
$action = 'GetRealNameAuthToken';
$param = [
'Name' => $Name,
'IDCard' => $IDCard,
'CallbackURL' => $CallbackURL
];
return $this->send_reuqest($action, $param);
}
public function GetRealNameAuthResult($AuthToken){
$action = 'GetRealNameAuthResult';
$param = [
'AuthToken' => $AuthToken
];
return $this->send_reuqest($action, $param);
}
private function send_reuqest($action, $param){
$payload = json_encode($param);
$time = time();
$authorization = $this->generateSign($payload, $time);
$header = [
'Authorization: '.$authorization,
'Content-Type: application/json; charset=utf-8',
'X-TC-Action: '.$action,
'X-TC-Timestamp: '.$time,
'X-TC-Version: '.$this->version,
'X-TC-Region: '.$this->region,
];
return $this->curl_post($payload, $header);
}
private function generateSign($payload, $time){
$algorithm = "TC3-HMAC-SHA256";
// step 1: build canonical request string
$httpRequestMethod = "POST";
$canonicalUri = "/";
$canonicalQueryString = "";
$canonicalHeaders = "content-type:application/json; charset=utf-8\n"."host:".$this->endpoint."\n";
$signedHeaders = "content-type;host";
$hashedRequestPayload = hash("SHA256", $payload);
$canonicalRequest = $httpRequestMethod."\n"
.$canonicalUri."\n"
.$canonicalQueryString."\n"
.$canonicalHeaders."\n"
.$signedHeaders."\n"
.$hashedRequestPayload;
// step 2: build string to sign
$date = gmdate("Y-m-d", $time);
$credentialScope = $date."/".$this->service."/tc3_request";
$hashedCanonicalRequest = hash("SHA256", $canonicalRequest);
$stringToSign = $algorithm."\n"
.$time."\n"
.$credentialScope."\n"
.$hashedCanonicalRequest;
// step 3: sign string
$secretDate = hash_hmac("SHA256", $date, "TC3".$this->SecretKey, true);
$secretService = hash_hmac("SHA256", $this->service, $secretDate, true);
$secretSigning = hash_hmac("SHA256", "tc3_request", $secretService, true);
$signature = hash_hmac("SHA256", $stringToSign, $secretSigning);
// step 4: build authorization
$authorization = $algorithm
." Credential=".$this->SecretId."/".$credentialScope
.", SignedHeaders=content-type;host, Signature=".$signature;
return $authorization;
}
private function curl_post($payload, $header){
$url = 'https://'.$this->endpoint.'/';
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
$json=curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if($httpCode==200){
$arr=json_decode($json,true);
return $arr['Response'];
}else{
return false;
}
}
}
+59
View File
@@ -0,0 +1,59 @@
<?php
namespace lib;
class Template {
static public function getList(){
$dir = TEMPLATE_ROOT;
$dirArray[] = NULL;
if (false != ($handle = opendir($dir))) {
$i = 0;
while (false !== ($file = readdir($handle))) {
if ($file != "." && $file != ".." && strpos($file, ".")===false) {
$dirArray[$i] = $file;
$i++;
}
}
closedir($handle);
}
return $dirArray;
}
static public function load($name = 'index'){
global $conf;
$template = $conf['template']?$conf['template']:'default';
if(!preg_match('/^[a-zA-Z0-9\_]+$/',$name))exit('error');
$filename = TEMPLATE_ROOT.$template.'/'.$name.'.php';
$filename_default = TEMPLATE_ROOT.'default/'.$name.'.php';
if(file_exists($filename)){
define("INDEX_ROOT",TEMPLATE_ROOT.$template.'/');
define("STATIC_ROOT",'/template/'.$template.'/assets/');
return $filename;
}elseif(file_exists($filename_default)){
define("INDEX_ROOT",TEMPLATE_ROOT.'default/');
define("STATIC_ROOT",'/template/default/assets/');
return $filename_default;
}else{
exit('Template file not found');
}
}
static public function loadDoc($name = 'index'){
if(!preg_match('/^[a-zA-Z0-9\_]+$/',$name))exit('error');
$filename = TEMPLATE_ROOT.'default/doc/'.$name.'.php';
if(file_exists($filename)){
return $filename;
}else{
exit('Document file not found');
}
}
static public function exists($template){
$filename = TEMPLATE_ROOT.$template.'/index.php';
if(file_exists($filename)){
return true;
}else{
return false;
}
}
}
+153
View File
@@ -0,0 +1,153 @@
<?php
namespace lib;
use Exception;
class Transfer
{
//通用转账
//type alipay:支付宝,wxpay:微信,qqpay:QQ钱包,bank:银行卡
public static function submit($type, $channel, $out_biz_no, $payee_account, $payee_real_name, $money, $desc = null){
global $conf;
$bizParam = [
'type' => $type,
'out_biz_no' => $out_biz_no,
'payee_account' => $payee_account,
'payee_real_name' => $payee_real_name,
'money' => $money,
'transfer_name' => $desc?$desc:$conf['transfer_name'],
'transfer_desc' => $desc?$desc:$conf['transfer_desc'],
];
return \lib\Plugin::call('transfer', $channel, $bizParam);
}
//转账状态刷新
public static function status($out_biz_no){
global $DB;
$order = $DB->find('transfer', '*', ['biz_no' => $out_biz_no]);
if(!$order) return ['code'=>-1, 'msg'=>'付款记录不存在'];
$channelinfo = null;
if($order['uid'] > 0){
$channelinfo = $DB->findColumn('user', 'channelinfo', ['uid'=>$order['uid']]);
}
$channel = \lib\Channel::get($order['channel'], $channelinfo);
if(!$channel) return ['code'=>-1, 'msg'=>'支付通道不存在'];
$result = self::query($order['type'], $channel, $out_biz_no, $order['pay_order_no']);
if($result['code'] == 0){
if($result['status'] == 2){
if($order['status'] == 0){
$resCount = $DB->update('transfer', ['status'=>2, 'result'=>$result['errmsg']], ['biz_no' => $out_biz_no]);
if($order['uid'] > 0 && $resCount > 0){
changeUserMoney($order['uid'], $order['costmoney'], true, '代付退回');
}
}
$result['msg'] = '转账失败:'.($result['errmsg']?$result['errmsg']:'原因未知');
}elseif($result['status'] == 1){
if($order['status'] == 0){
$paytime = $result['paydate'] ?? 'NOW()';
$DB->update('transfer', ['status'=>1, 'paytime'=>$paytime, 'result'=>''], ['biz_no' => $out_biz_no]);
}
$result['msg'] = '转账成功!';
}else{
$result['msg'] = '转账处理中,请稍后查询结果。';
}
}
return $result;
}
//转账查询
//status 0:处理中 1:成功 2:失败
public static function query($type, $channel, $out_biz_no, $pay_order_no){
$bizParam = [
'type' => $type,
'out_biz_no' => $out_biz_no,
'orderid' => $pay_order_no
];
return \lib\Plugin::call('transfer_query', $channel, $bizParam);
}
//撤销转账
public static function cancel($out_biz_no){
global $DB;
$order = $DB->find('transfer', '*', ['biz_no' => $out_biz_no]);
if(!$order) return ['code'=>-1, 'msg'=>'付款记录不存在'];
$channelinfo = null;
if($order['uid'] > 0){
$channelinfo = $DB->findColumn('user', 'channelinfo', ['uid'=>$order['uid']]);
}
$channel = \lib\Channel::get($order['channel'], $channelinfo);
if(!$channel) return ['code'=>-1, 'msg'=>'支付通道不存在'];
$bizParam = [
'type' => $order['type'],
'out_biz_no' => $order['biz_no'],
'orderid' => $order['pay_order_no'],
];
$result = \lib\Plugin::call('transfer_cancel', $channel, $bizParam);
if($result['code'] == 0){
$DB->update('transfer', ['status'=>2, 'result'=>'转账已撤销'], ['biz_no' => $out_biz_no]);
$result['msg'] = '转账已撤销';
}
return $result;
}
//账户余额查询
public static function balance($type, $channel, $user_id = null){
$bizParam = [
'type' => $type,
'user_id' => $user_id
];
return \lib\Plugin::call('balance_query', $channel, $bizParam);
}
//转账凭证查询
public static function proof($out_biz_no){
global $DB;
$order = $DB->find('transfer', '*', ['biz_no' => $out_biz_no]);
if(!$order) return ['code'=>-1, 'msg'=>'付款记录不存在'];
$channelinfo = null;
if($order['uid'] > 0){
$channelinfo = $DB->findColumn('user', 'channelinfo', ['uid'=>$order['uid']]);
}
$channel = \lib\Channel::get($order['channel'], $channelinfo);
if(!$channel) return ['code'=>-1, 'msg'=>'支付通道不存在'];
$bizParam = [
'type' => $order['type'],
'out_biz_no' => $out_biz_no,
'orderid' => $order['pay_order_no']
];
return \lib\Plugin::call('transfer_proof', $channel, $bizParam);
}
//转账回调处理
public static function processNotify($out_biz_no, $status, $errmsg = null){
global $DB;
$order = $DB->find('transfer', '*', ['biz_no' => $out_biz_no]);
if(!$order) {
$order = $DB->find('settle', '*', ['transfer_no' => $out_biz_no]);
if(!$order) return;
if($status == 2 && $order['transfer_status'] == 1){
$DB->update('settle', ['transfer_status'=>2, 'transfer_result'=>$errmsg, 'status'=>3, 'result'=>$errmsg], ['id' => $order['id']]);
}elseif($status == 1 && $order['transfer_status'] == 2){
$DB->update('settle', ['transfer_status'=>1, 'status'=>1, 'result'=>''], ['biz_no' => $out_biz_no]);
}
return;
}
if($status == 2 && $order['status'] == 0){ //转账失败
$data = ['status'=>2];
if($errmsg) $data['result'] = $errmsg;
$resCount = $DB->update('transfer', $data, ['biz_no' => $out_biz_no]);
if($order['uid'] > 0 && $resCount > 0){
changeUserMoney($order['uid'], $order['costmoney'], true, '代付退回');
}
}elseif($status == 1 && $order['status'] == 0){ //转账成功
$DB->update('transfer', ['status'=>1, 'paytime'=>'NOW()', 'result'=>''], ['biz_no' => $out_biz_no]);
}
}
}
+133
View File
@@ -0,0 +1,133 @@
<?php
namespace lib;
use Exception;
class VerifyCode{
const SMS_PHONE_DAYLY_TIME = 3; //短信验证码每个手机号每天最多发送次数
const SMS_IP_DAYLY_TIME = 6; //短信验证码每个IP每天最多发送次数
const EMAIL_ADDRESS_DAYLY_TIME = 7; //邮箱验证码每个邮箱每天最多发送次数
const EMAIL_IP_DAYLY_TIME = 11; //邮箱验证码每个IP每天最多发送次数
private static $regcodeid;
/**
* 发送验证码
* @param string $scene 验证码场景
* @param int $type 验证码类型:0邮件,1手机
* @param string $sendto 接收人
* @param int $uid 用户ID
* @return mixed
*/
public static function send_code($scene, $type, $sendto, $uid = 0){
global $DB, $conf, $clientip;
if($type == 1){
$phone = $sendto;
$row=$DB->getRow("select * from pre_regcode where `to`=:phone order by id desc limit 1", [':phone'=>$phone]);
if($row['time']>time()-60){
return '两次发送短信之间需要相隔60秒!';
}
$count=$DB->getColumn("select count(*) from pre_regcode where `to`=:phone and time>'".(time()-3600*24)."'", [':phone'=>$phone]);
if($count>=self::SMS_PHONE_DAYLY_TIME){
return '该手机号码发送次数过多,请更换号码!';
}
$count=$DB->getColumn("select count(*) from pre_regcode where ip=:ip and time>'".(time()-3600*24)."'", [':ip'=>$clientip]);
if($count>=self::SMS_IP_DAYLY_TIME){
return '你今天发送次数过多,请明天再试!';
}
$code = rand(111111,999999);
$result = send_sms($phone, $code, $scene);
if($result===true){
if($DB->insert('regcode', ['uid'=>$uid, 'scene'=>$scene, 'type'=>$type, 'code'=>$code, 'to'=>$phone, 'time'=>time(), 'ip'=>$clientip, 'status'=>0])){
return true;
}else{
return '写入数据库失败。'.$DB->error();
}
}else{
return '短信发送失败 '.$result;
}
}else{
$email = $sendto;
$row=$DB->getRow("select * from pre_regcode where `to`=:email order by id desc limit 1", [':email'=>$email]);
if($row['time']>time()-60){
return '两次发送邮件之间需要相隔60秒!';
}
$count=$DB->getColumn("select count(*) from pre_regcode where `to`=:email and time>'".(time()-3600*24)."'", [':email'=>$email]);
if($count>=self::EMAIL_ADDRESS_DAYLY_TIME){
return '该邮箱发送次数过多,请更换邮箱!';
}
$count=$DB->getColumn("select count(*) from pre_regcode where ip=:ip and time>'".(time()-3600*24)."'", [':ip'=>$clientip]);
if($count>=self::EMAIL_IP_DAYLY_TIME){
return '你今天发送次数过多,请明天再试!';
}
$code = rand(1111111,9999999);
$result = self::send_mail_code($email, $code, $scene);
if($result===true){
if($DB->insert('regcode', ['uid'=>$uid, 'scene'=>$scene, 'type'=>$type, 'code'=>$code, 'to'=>$email, 'time'=>time(), 'ip'=>$clientip, 'status'=>0])){
return true;
}else{
return '写入数据库失败。'.$DB->error();
}
}else{
return '邮件发送失败 '.$result;
}
}
}
private static function send_mail_code($email, $code, $scene){
global $conf;
$title = $conf['sitename'].' - 验证码获取';
if($scene == 'reg'){
$body = '您的验证码是:'.$code.',您正在注册成为'.$conf['sitename'].'的用户,如非本人操作请忽略。';
}elseif($scene == 'login'){
$body = '您的验证码是:'.$code.',用于'.$conf['sitename'].'登录验证,请勿泄露验证码,如非本人操作请忽略。';
}elseif($scene == 'find'){
$body = '您的验证码是:'.$code.',用于'.$conf['sitename'].'重置密码,请勿泄露验证码,如非本人操作请忽略。';
}elseif($scene == 'edit'){
global $situation;
if($situation=='settle')$body = '您正在修改结算账号信息,验证码是:'.$code;
elseif($situation=='mibao')$body = '您正在修改密保邮箱,验证码是:'.$code;
elseif($situation=='bind')$body = '您正在绑定新邮箱,验证码是:'.$code;
else $body = '您的验证码是:'.$code;
}
return send_mail($email, $title, $body);
}
/**
* 验证验证码
* @param string $scene 验证码场景
* @param int $type 验证码类型:0邮件,1手机
* @param string $sendto 接收人
* @param string $code 验证码
* @param int $uid 用户ID
* @return mixed
*/
public static function verify_code($scene, $type, $sendto, $code, $uid = 0){
global $DB;
$where = ['scene'=>$scene, 'type'=>$type, 'to'=>$sendto];
if($uid > 0) $where['uid'] = $uid;
$row = $DB->find('regcode', '*', $where, 'id DESC', 1);
if (!$row) {
return '请重新获取验证码!';
}elseif($row['time']<time()-3600 || $row['status']>0 || $row['errcount']>=5){
return '验证码已失效,请重新获取';
}elseif($row['code']!=$code){
$DB->exec("update `pre_regcode` set `errcount`=`errcount`+1 where `id`='{$row['id']}'");
return '验证码不正确!';
}
self::$regcodeid = $row['id'];
return true;
}
//作废验证码
public static function void_code(){
global $DB;
if(self::$regcodeid){
$DB->exec("update `pre_regcode` set `status`='1' where `id`=:id", [':id'=>self::$regcodeid]);
self::$regcodeid = null;
}
}
}
+50
View File
@@ -0,0 +1,50 @@
<?php
namespace lib\api;
use Exception;
class Merchant
{
public static function info(){
global $conf, $DB, $userrow, $queryArr;
$pid=intval($queryArr['pid']);
$orders=$DB->getColumn("SELECT count(*) from pre_order WHERE uid={$pid}");
$lastday=date("Y-m-d",strtotime("-1 day"));
$today=date("Y-m-d");
$order_today=$DB->getColumn("SELECT count(*) from pre_order where uid={$pid} and status=1 and date='$today'");
$order_lastday=$DB->getColumn("SELECT count(*) from pre_order where uid={$pid} and status=1 and date='$lastday'");
$order_today_all = round($DB->getColumn("SELECT sum(money) FROM pre_order WHERE uid={$pid} AND status=1 AND date='$today'"),2);
$order_lastday_all = round($DB->getColumn("SELECT sum(money) FROM pre_order WHERE uid={$pid} AND status=1 AND date='$lastday'"),2);
$result = ['code'=>0, 'pid'=>$pid, 'status'=>$userrow['status'], 'pay_status'=>$userrow['pay'], 'settle_status'=>$userrow['settle'], 'money'=>$userrow['money'], 'settle_type'=>$userrow['settle_id'], 'settle_account'=>$userrow['account'], 'settle_name'=>$userrow['username'], 'order_num'=>$orders, 'order_num_today'=>$order_today, 'order_num_lastday'=>$order_lastday, 'order_money_today'=>strval($order_today_all), 'order_money_lastday'=>strval($order_lastday_all)];
$result = array_filter($result, function($a){return !isEmpty($a);});
return $result;
}
public static function orders(){
global $conf, $DB, $userrow, $queryArr;
$pid=intval($queryArr['pid']);
$limit=isset($queryArr['limit'])?intval($queryArr['limit']):10;
$offset=isset($queryArr['offset'])?intval($queryArr['offset']):0;
$status=isset($queryArr['status'])?intval($queryArr['status']):null;
if($limit>50)$limit=50;
$sql = " uid='{$pid}'";
if(isset($queryArr['status'])){
$status = intval($_GET['status']);
$sql .= " AND A.status='{$status}'";
}
$data = [];
$rs=$DB->query("SELECT A.*,B.name typename FROM pre_order A LEFT JOIN pre_type B ON A.type=B.id WHERE{$sql} ORDER BY trade_no DESC LIMIT {$offset},{$limit}");
while($order=$rs->fetch(\PDO::FETCH_ASSOC)){
$data[]=['trade_no'=>$order['trade_no'],'out_trade_no'=>$order['out_trade_no'],'api_trade_no'=>$order['api_trade_no'],'type'=>$order['typename'],'pid'=>$order['uid'],'addtime'=>$order['addtime'],'endtime'=>$order['endtime'],'name'=>$order['name'],'money'=>$order['money'],'param'=>$order['param'],'buyer'=>$order['buyer'],'clientip'=>$order['ip'],'status'=>$order['status'],'refundmoney'=>$order['refundmoney']];
}
$result['code'] = 0;
$result['data'] = $data;
return $result;
}
}
+516
View File
@@ -0,0 +1,516 @@
<?php
namespace lib\api;
use Exception;
class Pay
{
public static function submit()
{
global $conf, $DB, $clientip, $order, $userrow;
@header('Content-Type: text/html; charset=UTF-8');
if(isset($_GET['pid'])){
$queryArr=$_GET;
}elseif(isset($_POST['pid'])){
$queryArr=$_POST;
}else{
exit('你还未配置支付接口商户!');
}
$pid=intval($queryArr['pid']);
if(empty($pid))sysmsg('商户ID不能为空');
$userrow=$DB->getRow("SELECT `uid`,`gid`,`key`,`money`,`mode`,`pay`,`cert`,`status`,`channelinfo`,`qq`,`ordername`,`keytype`,`publickey`,`deposit` FROM `pre_user` WHERE `uid`='{$pid}' LIMIT 1");
if(!$userrow)sysmsg('商户不存在!');
if(isset($queryArr['__defend'])){
$defend_result = $queryArr['__defend'];
unset($queryArr['__defend']);
}
try{
\lib\ApiHelper::api_verify($userrow, $queryArr);
}catch(Exception $e){
sysmsg($e->getMessage());
}
if($userrow['status']==0 || $userrow['pay']==0)sysmsg('商户已被封禁,无法支付!');
if($userrow['pay']==2 && $conf['user_review']==1)sysmsg('商户未通过审核,无法支付!');
$type=daddslashes($queryArr['type']);
$out_trade_no=daddslashes($queryArr['out_trade_no']);
$notify_url=htmlspecialchars(daddslashes($queryArr['notify_url']));
$return_url=htmlspecialchars(daddslashes($queryArr['return_url']));
$name=htmlspecialchars(daddslashes($queryArr['name']));
$money=daddslashes($queryArr['money']);
$sitename=urlencode(base64_encode(htmlspecialchars($queryArr['sitename'])));
$param=isset($queryArr['param'])?htmlspecialchars(daddslashes($queryArr['param'])):null;
if(empty($out_trade_no))sysmsg('订单号(out_trade_no)不能为空');
if(empty($notify_url))sysmsg('通知地址(notify_url)不能为空');
if(empty($return_url))sysmsg('回调地址(return_url)不能为空');
if(empty($name))sysmsg('商品名称(name)不能为空');
if(empty($money))sysmsg('金额(money)不能为空');
if($money<=0 || !is_numeric($money) || !preg_match('/^[0-9.]+$/', $money))sysmsg('金额不合法');
if($conf['pay_maxmoney']>0 && $money>$conf['pay_maxmoney'])sysmsg('最大支付金额是'.$conf['pay_maxmoney'].'元');
if($conf['pay_minmoney']>0 && $money<$conf['pay_minmoney'])sysmsg('最小支付金额是'.$conf['pay_minmoney'].'元');
if(!preg_match('/^[a-zA-Z0-9.\_\-|]+$/',$out_trade_no))sysmsg('订单号(out_trade_no)格式不正确');
$domain=getdomain($notify_url);
$groupconfig = getGroupConfig($userrow['gid']);
$conf = array_merge($conf, $groupconfig);
if($conf['cert_force']==1 && $userrow['cert']==0){
sysmsg('当前商户未完成实名认证,无法收款');
}
if($conf['forceqq']==1 && empty($userrow['qq'])){
sysmsg('当前商户未填写联系QQ,无法收款');
}
if($conf['pay_domain_forbid']==1){
if(!$DB->getRow("SELECT * FROM pre_domain WHERE uid=:uid AND (domain=:domain OR domain=:domain2) AND status=1 LIMIT 1", [':uid'=>$pid, ':domain'=>get_host($notify_url), ':domain2'=>'*.'.get_main_host($notify_url)])){
sysmsg('该域名不可发起支付,原因:域名没过白,请前往支付平台授权支付域名');
}
}
if($conf['user_deposit']==1 && $conf['user_deposit_min'] > 0 && $conf['user_deposit_min'] > $userrow['deposit']){
sysmsg('商户保证金不足,请前往支付平台充值保证金后再发起支付');
}
if(!empty($conf['blockname'])){
$block_name = explode('|',$conf['blockname']);
foreach($block_name as $rows){
if(!empty($rows) && strpos($name,$rows)!==false){
$DB->exec("INSERT INTO `pre_risk` (`uid`, `url`, `content`, `date`) VALUES (:uid, :domain, :rows, NOW())", [':uid'=>$pid,':domain'=>$domain,':rows'=>$rows]);
sysmsg($conf['blockalert']?$conf['blockalert']:'该商品禁止出售');
}
}
}
$blackip = $DB->find('blacklist', '*', ['type'=>1, 'content'=>$clientip], null, 1);
if($blackip)sysmsg('系统异常无法完成付款');
if($conf['pay_iplimit'] > 0){
$ipcount = $DB->getColumn("select count(*) from pre_order where `ip`='$clientip' and `date`='".date('Y-m-d')."' and status>0");
if($ipcount >= $conf['pay_iplimit']){
sysmsg('你今天已无法再发起支付,请明天再试');
}
}
if(checkPayVerifyOpen($pid)){
$defend_key = getDefendKey($pid, $out_trade_no);
if(empty($defend_result) || $defend_key!==substr($defend_result,10,32)){
showPayVerifyPage($defend_key, $queryArr);
}
}
if(strlen($name)>127)$name=mb_strcut($name, 0, 127, 'utf-8');
$firstGetChannel = true;
$oldorder = $DB->getRow("SELECT * FROM `pre_order` WHERE `uid`=:uid AND `out_trade_no`=:out_trade_no", [':uid'=>$pid, ':out_trade_no'=>$out_trade_no]);
if($oldorder && time() - strtotime($oldorder['addtime']) < 864000){
if($oldorder['status']>0){
sysmsg('该订单('.$out_trade_no.')已完成支付,请勿重复发起支付');
}
if(round($oldorder['money'],2) != round($money,2) || $oldorder['name'] != $name || $oldorder['notify_url'] != $notify_url || $oldorder['return_url'] != $return_url || $oldorder['param'] != $param){
sysmsg('该订单('.$out_trade_no.')支付参数有变化,请更换订单号重新发起支付');
}
$trade_no=$oldorder['trade_no'];
$typeid = $DB->getColumn("SELECT id FROM pre_type WHERE name=:name LIMIT 1", [':name'=>$type]);
if($oldorder['type'] > 0 && $oldorder['channel'] > 0 && $oldorder['realmoney'] > 0 && $oldorder['getmoney'] > 0 && $typeid == $oldorder['type']){ //订单已经获取过支付通道信息
$firstGetChannel = false;
}
}else{
$version = defined('API_INIT') ? 1 : 0;
$trade_no=date("YmdHis").rand(11111,99999);
if(!$DB->exec("INSERT INTO `pre_order` (`trade_no`,`out_trade_no`,`uid`,`addtime`,`name`,`money`,`notify_url`,`return_url`,`param`,`domain`,`ip`,`status`,`version`) VALUES (:trade_no, :out_trade_no, :uid, NOW(), :name, :money, :notify_url, :return_url, :param, :domain, :clientip, 0, :version)", [':trade_no'=>$trade_no, ':out_trade_no'=>$out_trade_no, ':uid'=>$pid, ':name'=>$name, ':money'=>$money, ':notify_url'=>$notify_url, ':return_url'=>$return_url, ':domain'=>$domain, ':clientip'=>$clientip, ':param'=>$param, ':version'=>$version]))sysmsg('创建订单失败,请返回重试!');
}
if(empty($type)){
echo "<script>window.location.replace('/cashier.php?trade_no={$trade_no}&sitename={$sitename}');</script>";
exit;
}
// 获取订单支付方式ID、支付插件、支付通道、支付费率
if($firstGetChannel){
$submitData = \lib\Channel::submit($type, $userrow['uid'], $userrow['gid'], $money);
if(!$submitData){
echo "<script>window.location.replace('/cashier.php?trade_no={$trade_no}&sitename={$sitename}&other=1');</script>";
exit;
}
if($userrow['mode']==1){ //订单加费模式
$realmoney = round($money*(100+100-$submitData['rate'])/100,2);
$getmoney = $money;
if($conf['payfee_lessthan'] > 0 && $conf['payfee_mincost'] > 0){
$feemoney = round($money*(100-$submitData['rate'])/100,2);
if($feemoney < round($conf['payfee_lessthan'], 2)){
$realmoney = round($money + $conf['payfee_mincost'], 2);
}
}
}else{
$realmoney = $money;
$getmoney = round($money*$submitData['rate']/100,2);
if($conf['payfee_lessthan'] > 0 && $conf['payfee_mincost'] > 0){
$feemoney = round($money*(100-$submitData['rate'])/100,2);
if($feemoney < round($conf['payfee_lessthan'], 2)){
$getmoney = round($money - $conf['payfee_mincost'], 2);
if($getmoney < 0) $getmoney = 0;
}
}
}
}else{
$submitData = \lib\Channel::info($oldorder['channel']);
$submitData['typename'] = $type;
$submitData['subchannel'] = $oldorder['subchannel'];
$realmoney = $oldorder['realmoney'];
$getmoney = $oldorder['getmoney'];
}
// 判断通道单笔支付限额
if(!empty($submitData['paymin']) && $submitData['paymin']>0 && $money<$submitData['paymin']){
sysmsg('<center>当前支付方式单笔最小限额为'.$submitData['paymin'].'元,请选择其他支付方式!</center>', '跳转提示');
}
if(!empty($submitData['paymax']) && $submitData['paymax']>0 && $money>$submitData['paymax']){
sysmsg('<center>当前支付方式单笔最大限额为'.$submitData['paymax'].'元,请选择其他支付方式!</center>', '跳转提示');
}
// 商户直清模式判断商户余额
if($submitData['mode']==1 && $realmoney-$getmoney>$userrow['money']){
sysmsg('当前商户余额不足,无法完成支付,请商户登录用户中心充值余额');
}
if($firstGetChannel){
// 随机增减金额
if(!empty($conf['pay_payaddstart'])&&$conf['pay_payaddstart']!=0&&!empty($conf['pay_payaddmin'])&&$conf['pay_payaddmin']!=0&&!empty($conf['pay_payaddmax'])&&$conf['pay_payaddmax']!=0&&$realmoney>=$conf['pay_payaddstart'])$realmoney = round($realmoney + randomFloat(round($conf['pay_payaddmin'],2),round($conf['pay_payaddmax'],2)), 2);
$DB->update('order', ['type'=>$submitData['typeid'], 'channel'=>$submitData['channel'], 'subchannel'=>$submitData['subchannel'], 'realmoney'=>$realmoney, 'getmoney'=>$getmoney], ['trade_no'=>$trade_no]);
}
$order['trade_no'] = $trade_no;
$order['out_trade_no'] = $out_trade_no;
$order['uid'] = $pid;
$order['addtime'] = date('Y-m-d H:i:s');
$order['name'] = $name;
$order['realmoney'] = $realmoney;
$order['type'] = $submitData['typeid'];
$order['channel'] = $submitData['channel'];
$order['subchannel'] = $submitData['subchannel'];
$order['typename'] = $submitData['typename'];
$order['plugin'] = $submitData['plugin'];
$order['profits'] = \lib\Payment::updateOrderProfits($order, $submitData['plugin']);
try{
$result = \lib\Plugin::loadForSubmit($submitData['plugin'], $trade_no);
$result['submit'] = true;
\lib\Payment::echoDefault($result);
}catch(Exception $e){
sysmsg($e->getMessage());
}
}
public static function create(){
global $conf, $DB, $clientip, $order, $userrow, $method, $device, $mdevice, $siteurl;
if(isset($_POST['pid'])){
$queryArr=$_POST;
}else{
echojsonmsg('未传入任何参数', -4);
}
$pid=intval($queryArr['pid']);
if(empty($pid))echojsonmsg('商户ID不能为空');
$userrow=$DB->getRow("SELECT `uid`,`gid`,`key`,`money`,`mode`,`pay`,`cert`,`status`,`channelinfo`,`qq`,`ordername`,`keytype`,`publickey`,`deposit` FROM `pre_user` WHERE `uid`='{$pid}' LIMIT 1");
if(!$userrow)echojsonmsg('商户不存在!');
try{
\lib\ApiHelper::api_verify($userrow, $queryArr);
}catch(Exception $e){
echojsonmsg($e->getMessage(), -3);
}
if($userrow['status']==0 || $userrow['pay']==0)echojsonmsg('商户已被封禁,无法支付!');
if($userrow['pay']==2 && $conf['user_review']==1)echojsonmsg('商户未通过审核,无法支付!');
$type=daddslashes($queryArr['type']);
$out_trade_no=daddslashes($queryArr['out_trade_no']);
$notify_url=htmlspecialchars(daddslashes($queryArr['notify_url']));
$return_url=htmlspecialchars(daddslashes($queryArr['return_url']));
$name=htmlspecialchars(daddslashes($queryArr['name']));
$money=daddslashes($queryArr['money']);
$clientip=daddslashes($queryArr['clientip']);
$device=$queryArr['device'];
if(empty($device))$device = 'pc';
$sub_openid=$queryArr['sub_openid'];
$sub_appid=$queryArr['sub_appid'];
$auth_code=$queryArr['auth_code'];
$sitename=urlencode(base64_encode(htmlspecialchars($queryArr['sitename'])));
$param=isset($queryArr['param'])?htmlspecialchars(daddslashes($queryArr['param'])):null;
$method=$queryArr['method']; //web/jump/jsapi/scan
if($device == 'jump')$method = 'jump';
$mdevice='';
if ($device=='qq'||$device=='wechat'||$device=='alipay'||$device=='app') {
$mdevice=$device;
$device='mobile';
}
if(empty($out_trade_no))echojsonmsg('订单号(out_trade_no)不能为空');
if(empty($notify_url))echojsonmsg('通知地址(notify_url)不能为空');
if(empty($name))echojsonmsg('商品名称(name)不能为空');
if(empty($money))echojsonmsg('金额(money)不能为空');
if(empty($type) && $method != 'scan')echojsonmsg('支付方式(type)不能为空');
if(empty($clientip))echojsonmsg('用户IP地址(clientip)不能为空');
if($money<=0 || !is_numeric($money) || !preg_match('/^[0-9.]+$/', $money))echojsonmsg('金额不合法');
if($conf['pay_maxmoney']>0 && $money>$conf['pay_maxmoney'])echojsonmsg('最大支付金额是'.$conf['pay_maxmoney'].'元');
if($conf['pay_minmoney']>0 && $money<$conf['pay_minmoney'])echojsonmsg('最小支付金额是'.$conf['pay_minmoney'].'元');
if(!preg_match('/^[a-zA-Z0-9.\_\-|]+$/',$out_trade_no))echojsonmsg('订单号(out_trade_no)格式不正确');
if($method == 'jsapi' && empty($sub_openid))echojsonmsg('jsapi支付时参数(sub_openid)不能为空');
if($method == 'jsapi' && $type=='wxpay' && empty($sub_appid))echojsonmsg('jsapi支付时参数(sub_appid)不能为空');
if($method == 'scan' && empty($auth_code))echojsonmsg('付款码支付时授权码(auth_code)不能为空');
if($method == 'scan' && empty($type)){
$type = getScanPayType($auth_code);
if($type == 'unknown') echojsonmsg('未知的付款码类型');
}
$groupconfig = getGroupConfig($userrow['gid']);
$conf = array_merge($conf, $groupconfig);
$domain=getdomain($notify_url);
if($conf['cert_force']==1 && $userrow['cert']==0){
echojsonmsg('当前商户未完成实名认证,无法收款');
}
if($conf['forceqq']==1 && empty($userrow['qq'])){
echojsonmsg('当前商户未填写联系QQ,无法收款');
}
if($conf['pay_domain_forbid']==1){
if(!$DB->getRow("SELECT * FROM pre_domain WHERE uid=:uid AND (domain=:domain OR domain=:domain2) AND status=1 LIMIT 1", [':uid'=>$pid, ':domain'=>get_host($notify_url), ':domain2'=>'*.'.get_main_host($notify_url)])){
echojsonmsg('该域名不可发起支付,原因:域名没过白,请前往支付平台授权支付域名');
}
}
if($conf['user_deposit']==1 && $conf['user_deposit_min'] > 0 && $conf['user_deposit_min'] > $userrow['deposit']){
echojsonmsg('商户保证金不足,请前往支付平台充值保证金后再发起支付');
}
if(!empty($conf['blockname'])){
$block_name = explode('|',$conf['blockname']);
foreach($block_name as $rows){
if(!empty($rows) && strpos($name,$rows)!==false){
$DB->exec("INSERT INTO `pre_risk` (`uid`, `url`, `content`, `date`) VALUES (:uid, :domain, :rows, NOW())", [':uid'=>$pid,':domain'=>$domain,':rows'=>$rows]);
echojsonmsg($conf['blockalert']?$conf['blockalert']:'该商品禁止出售');
}
}
}
$blackip = $DB->find('blacklist', '*', ['type'=>1, 'content'=>$clientip], null, 1);
if($blackip)echojsonmsg('系统异常无法完成付款');
if($conf['pay_iplimit'] > 0){
$ipcount = $DB->getColumn("select count(*) from pre_order where `ip`='$clientip' and `date`='".date('Y-m-d')."' and status>0");
if($ipcount >= $conf['pay_iplimit']){
echojsonmsg('你今天已无法再发起支付,请明天再试');
}
}
if(checkPayVerifyOpen($pid)){
echojsonmsg('本次支付需要安全验证,请使用跳转支付接口发起支付');
}
if(strlen($name)>127)$name=mb_strcut($name, 0, 127, 'utf-8');
$firstGetChannel = true;
$oldorder = $DB->getRow("SELECT * FROM `pre_order` WHERE `uid`=:uid AND `out_trade_no`=:out_trade_no", [':uid'=>$pid, ':out_trade_no'=>$out_trade_no]);
if($oldorder && time() - strtotime($oldorder['addtime']) < 864000){
if($oldorder['status']>0){
echojsonmsg('该订单('.$out_trade_no.')已完成支付,请勿重复发起支付');
}
if(round($oldorder['money'],2) != round($money,2) || $oldorder['name'] != $name || $oldorder['notify_url'] != $notify_url || $oldorder['return_url'] != $return_url || $oldorder['param'] != $param){
echojsonmsg('该订单('.$out_trade_no.')支付参数有变化,请更换订单号重新发起支付');
}
$trade_no=$oldorder['trade_no'];
$typeid = $DB->getColumn("SELECT id FROM pre_type WHERE name=:name LIMIT 1", [':name'=>$type]);
if($oldorder['type'] > 0 && $oldorder['channel'] > 0 && $oldorder['realmoney'] > 0 && $oldorder['getmoney'] > 0 && $typeid == $oldorder['type']){ //订单已经获取过支付通道信息
$firstGetChannel = false;
}
}else{
$version = defined('API_INIT') ? 1 : 0;
$trade_no=date("YmdHis").rand(11111,99999);
if(!$DB->exec("INSERT INTO `pre_order` (`trade_no`,`out_trade_no`,`uid`,`addtime`,`name`,`money`,`notify_url`,`return_url`,`param`,`domain`,`ip`,`status`,`version`) VALUES (:trade_no, :out_trade_no, :uid, NOW(), :name, :money, :notify_url, :return_url, :param, :domain, :clientip, 0, :version)", [':trade_no'=>$trade_no, ':out_trade_no'=>$out_trade_no, ':uid'=>$pid, ':name'=>$name, ':money'=>$money, ':notify_url'=>$notify_url, ':return_url'=>$return_url, ':domain'=>$domain, ':clientip'=>$clientip, ':param'=>$param, ':version'=>$version]))echojsonmsg('创建订单失败,请返回重试!');
}
if(empty($type)){
define("TRADE_NO", $trade_no);
\lib\Payment::echoJson(['type'=>'jump','url'=>$siteurl.'cashier.php?trade_no='.$trade_no.'&sitename='.$sitename]);
}
// 获取订单支付方式ID、支付插件、支付通道、支付费率
if($firstGetChannel){
$submitData = \lib\Channel::submit($type, $userrow['uid'], $userrow['gid'], $money);
if(!$submitData){
define("TRADE_NO", $trade_no);
\lib\Payment::echoJson(['type'=>'jump','url'=>$siteurl.'cashier.php?trade_no='.$trade_no.'&sitename='.$sitename.'&other=1']);
}
if($userrow['mode']==1){ //订单加费模式
$realmoney = round($money*(100+100-$submitData['rate'])/100,2);
$getmoney = $money;
if($conf['payfee_lessthan'] > 0 && $conf['payfee_mincost'] > 0){
$feemoney = round($money*(100-$submitData['rate'])/100,2);
if($feemoney < round($conf['payfee_lessthan'], 2)){
$realmoney = round($money + $conf['payfee_mincost'], 2);
}
}
}else{
$realmoney = $money;
$getmoney = round($money*$submitData['rate']/100,2);
if($conf['payfee_lessthan'] > 0 && $conf['payfee_mincost'] > 0){
$feemoney = round($money*(100-$submitData['rate'])/100,2);
if($feemoney < round($conf['payfee_lessthan'], 2)){
$getmoney = round($money - $conf['payfee_mincost'], 2);
if($getmoney < 0) $getmoney = 0;
}
}
}
}else{
$submitData = \lib\Channel::info($oldorder['channel']);
$submitData['typename'] = $type;
$submitData['subchannel'] = $oldorder['subchannel'];
$realmoney = $oldorder['realmoney'];
$getmoney = $oldorder['getmoney'];
}
// 判断通道单笔支付限额
if(!empty($submitData['paymin']) && $submitData['paymin']>0 && $money<$submitData['paymin']){
echojsonmsg('当前支付方式单笔最小限额为'.$submitData['paymin'].'元,请选择其他支付方式!');
}
if(!empty($submitData['paymax']) && $submitData['paymax']>0 && $money>$submitData['paymax']){
echojsonmsg('当前支付方式单笔最大限额为'.$submitData['paymax'].'元,请选择其他支付方式!');
}
// 商户直清模式判断商户余额
if($submitData['mode']==1 && $realmoney-$getmoney>$userrow['money']){
echojsonmsg('当前商户余额不足,无法完成支付,请商户登录用户中心充值余额');
}
if($firstGetChannel){
// 随机增减金额
if(!empty($conf['pay_payaddstart'])&&$conf['pay_payaddstart']!=0&&!empty($conf['pay_payaddmin'])&&$conf['pay_payaddmin']!=0&&!empty($conf['pay_payaddmax'])&&$conf['pay_payaddmax']!=0&&$realmoney>=$conf['pay_payaddstart'])$realmoney = $realmoney + randomFloat(round($conf['pay_payaddmin'],2),round($conf['pay_payaddmax'],2));
$DB->update('order', ['type'=>$submitData['typeid'], 'channel'=>$submitData['channel'], 'subchannel'=>$submitData['subchannel'], 'realmoney'=>$realmoney, 'getmoney'=>$getmoney], ['trade_no'=>$trade_no]);
}
$order['trade_no'] = $trade_no;
$order['out_trade_no'] = $out_trade_no;
$order['uid'] = $pid;
$order['addtime'] = date('Y-m-d H:i:s');
$order['name'] = $name;
$order['realmoney'] = $realmoney;
$order['type'] = $submitData['typeid'];
$order['channel'] = $submitData['channel'];
$order['subchannel'] = $submitData['subchannel'];
$order['typename'] = $submitData['typename'];
$order['plugin'] = $submitData['plugin'];
$order['profits'] = \lib\Payment::updateOrderProfits($order, $submitData['plugin']);
$order['sub_openid'] = $sub_openid;
$order['sub_appid'] = $sub_appid;
$order['auth_code'] = $auth_code;
if($method == 'jump'){
define("TRADE_NO", $trade_no);
\lib\Payment::echoJson(['type'=>'jump','url'=>$siteurl.'pay/submit/'.$trade_no.'/']);
}
try{
$result = \lib\Plugin::loadForSubmit($submitData['plugin'], $trade_no, true);
\lib\Payment::echoJson($result);
}catch(Exception $e){
echojsonmsg($e->getMessage());
}
}
public static function query(){
global $conf, $DB, $queryArr;
$pid=intval($queryArr['pid']);
if(!empty($queryArr['trade_no'])){
$trade_no=daddslashes($queryArr['trade_no']);
$order=$DB->getRow("SELECT * FROM pre_order WHERE uid='{$pid}' and trade_no='{$trade_no}' limit 1");
}elseif(!empty($queryArr['out_trade_no'])){
$out_trade_no=daddslashes($queryArr['out_trade_no']);
$order=$DB->getRow("SELECT * FROM pre_order WHERE uid='{$pid}' and out_trade_no='{$out_trade_no}' limit 1");
}else{
throw new Exception('订单号不能为空');
}
if($order){
$type=$DB->getColumn("SELECT name FROM pre_type WHERE id='{$order['type']}' LIMIT 1");
$result = ['code'=>0, 'trade_no'=>$order['trade_no'],'out_trade_no'=>$order['out_trade_no'],'api_trade_no'=>$order['api_trade_no'],'bill_trade_no'=>$order['bill_trade_no'],'type'=>$type,'pid'=>$order['uid'],'addtime'=>$order['addtime'],'endtime'=>$order['endtime'],'name'=>$order['name'],'money'=>$order['money'],'param'=>$order['param'],'buyer'=>$order['buyer'],'clientip'=>$order['ip'],'status'=>$order['status'],'refundmoney'=>$order['refundmoney']];
$result = array_filter($result, function($a){return !isEmpty($a);});
return $result;
}else{
throw new Exception('订单号不存在');
}
}
public static function refund(){
global $conf, $DB, $userrow, $queryArr;
$pid=intval($queryArr['pid']);
if(!$conf['user_refund']) throw new Exception('管理员未开启商户后台自助退款');
$money = trim($queryArr['money']);
if(!is_numeric($money) || !preg_match('/^[0-9.]+$/', $money))throw new Exception('金额输入错误');
if(!empty($queryArr['trade_no'])){
$trade_no=daddslashes($queryArr['trade_no']);
}elseif(!empty($queryArr['out_trade_no'])){
$out_trade_no=daddslashes($queryArr['out_trade_no']);
$trade_no = $DB->findColumn('order', 'trade_no', ['out_trade_no'=>$out_trade_no, 'uid'=>$pid]);
if(!$trade_no) throw new Exception('当前订单不存在!');;
}else{
throw new Exception('订单号不能为空');
}
$refund_no = date("YmdHis").rand(11111,99999);
if(!empty($queryArr['out_refund_no']) && strlen($queryArr['out_refund_no']) > 5){ //判断商户是否重复提交退款
$out_refund_no = daddslashes($queryArr['out_refund_no']);
$refund_order = $DB->find('refundorder', '*', ['out_refund_no'=>$out_refund_no, 'uid'=>$pid]);
if($refund_order && $refund_order['status'] == 1){
$result = ['code'=>0, 'refund_no'=>$refund_order['refund_no'], 'out_refund_no'=>$refund_order['out_refund_no'], 'trade_no'=>$refund_order['trade_no'], 'uid'=>$refund_order['uid'], 'money'=>$refund_order['money'], 'reducemoney'=>$refund_order['reducemoney'], 'msg'=>'已存在相同退款单号!退款金额¥'.$refund_order['money']];
return $result;
}elseif($refund_order && $refund_order['status'] == 0){
$refund_no = $refund_order['refund_no'];
}
}
$result = \lib\Order::refund($refund_no, $trade_no, $money, 1, $pid, $out_refund_no);
if($result['code'] == 0){
$result['msg'] = '退款成功!退款金额¥'.$result['money'];
}
return $result;
}
public static function refundquery(){
global $conf, $DB, $queryArr;
$pid=intval($queryArr['pid']);
if(!$conf['user_refund']) throw new Exception('管理员未开启商户后台自助退款');
if(!empty($queryArr['refund_no'])){
$refund_no=daddslashes($queryArr['refund_no']);
$refund_order = $DB->find('refundorder', '*', ['refund_no'=>$refund_no, 'uid'=>$pid]);
}elseif(!empty($queryArr['out_refund_no'])){
$out_refund_no=daddslashes($queryArr['out_refund_no']);
$refund_order = $DB->find('refundorder', '*', ['out_refund_no'=>$out_refund_no, 'uid'=>$pid]);
}else{
throw new Exception('商户退款单号不能为空');
}
if(!$refund_order)throw new Exception('退款记录不存在');
$out_trade_no = $DB->findColumn('order', 'out_trade_no', ['trade_no'=>$refund_order['trade_no']]);
$result = ['code'=>0, 'refund_no'=>$refund_order['refund_no'], 'out_refund_no'=>$refund_order['out_refund_no'], 'trade_no'=>$refund_order['trade_no'], 'out_trade_no'=>$out_trade_no, 'uid'=>$refund_order['uid'], 'money'=>$refund_order['money'], 'reducemoney'=>$refund_order['reducemoney'], 'status'=>$refund_order['status'], 'addtime'=>$refund_order['addtime'], 'endtime'=>$refund_order['endtime']];
return $result;
}
}
+186
View File
@@ -0,0 +1,186 @@
<?php
namespace lib\api;
use Exception;
class Transfer
{
public static function submit(){
global $conf, $DB, $userrow, $queryArr, $siteurl;
$pid=intval($queryArr['pid']);
$groupconfig = getGroupConfig($userrow['gid']);
$conf = array_merge($conf, $groupconfig);
if(!$conf['user_transfer']) throw new Exception('管理员未开启代付功能');
if($userrow['transfer'] == 0) throw new Exception('商户未开启代付API接口');
if($conf['settle_type']==1){
$today=date("Y-m-d").' 00:00:00';
$order_today=$DB->getColumn("SELECT SUM(realmoney) from pre_order where uid={$pid} and tid<>2 and status=1 and endtime>='$today'");
if(!$order_today) $order_today = 0;
$enable_money=round($userrow['money']-$order_today,2);
if($enable_money<0)$enable_money=0;
}else{
$enable_money=$userrow['money'];
}
if(!$conf['transfer_rate'])$conf['transfer_rate'] = $conf['settle_rate'];
$type = $queryArr['type'];
$out_biz_no = trim($queryArr['out_biz_no']);
$account = htmlspecialchars(trim($queryArr['account']));
$name = htmlspecialchars(trim($queryArr['name']));
$money = trim($queryArr['money']);
$desc = htmlspecialchars(trim($queryArr['remark']));
if(empty($type))throw new Exception('代付方式(type)不能为空');
if(empty($out_biz_no)) $out_biz_no = date("YmdHis").rand(11111,99999);
if(empty($account))throw new Exception('收款人账号(account)不能为空');
if(empty($name))throw new Exception('收款人姓名(name)不能为空');
if(empty($money))throw new Exception('转账金额(money)不能为空');
if(strlen($out_biz_no)!=19 || !is_numeric($out_biz_no))throw new Exception('交易号输入不规范');
if($desc && mb_strlen($desc)>32)throw new Exception('转账备注最多32个字');
if(!is_numeric($money) || !preg_match('/^[0-9.]+$/', $money) || $money<=0)throw new Exception('转账金额输入不规范');
$need_money = round($money + $money*$conf['transfer_rate']/100,2);
if($need_money>$enable_money)throw new Exception('需支付金额大于可转账余额');
if($conf['transfer_minmoney']>0 && $money<$conf['transfer_minmoney'])throw new Exception('单笔最小代付金额限制为'.$conf['transfer_minmoney'].'元');
if($conf['transfer_maxmoney']>0 && $money>$conf['transfer_maxmoney'])throw new Exception('单笔最大代付金额限制为'.$conf['transfer_maxmoney'].'元');
if($userrow['settle']==0)throw new Exception('您的商户出现异常,无法使用代付功能');
if($conf['transfer_maxlimit']>0){
$a_count = $DB->getColumn('SELECT count(*) FROM pre_transfer WHERE uid=:uid AND type=:type AND account=:account AND paytime>=:paytime', [':uid'=>$pid, ':type'=>$type, ':account'=>$account, ':paytime'=>date('Y-m-d').' 00:00:00']);
if($a_count >= $conf['transfer_maxlimit']){
throw new Exception('您今天向该账号的转账次数已达到上限');
}
}
if($type=='alipay'){
$channelid = $conf['transfer_alipay'];
}elseif($type=='wxpay'){
$channelid = $conf['transfer_wxpay'];
}elseif($type=='qqpay'){
if (!is_numeric($account) || strlen($account)<6 || strlen($account)>10)throw new Exception('QQ号码格式错误');
$channelid = $conf['transfer_qqpay'];
}elseif($type=='bank'){
$channelid = $conf['transfer_bank'];
}else{
throw new Exception('type参数错误');
}
if(!$channelid) throw new Exception('未开启此转账方式');
$channel = \lib\Channel::get($channelid, $userrow['channelinfo']);
if(!$channel)throw new Exception('当前支付通道信息不存在',4);
if(class_exists('\\lib\\AlipaySATF\\AlipaySATF') && $conf['alipay_satf']==1 && ($type=='alipay' || $type=='bank' && $conf['transfer_alipay']==$conf['transfer_bank'])){
$bookid = $queryArr['bookid'];
if(!$bookid) $bookid = $DB->findColumn('satf_account_book', 'id', ['uid'=>$pid, 'status'=>1], 'money DESC');
$satf = new \lib\AlipaySATF\AlipaySATF();
$params = [
'out_biz_no' => $out_biz_no,
'account' => $account,
'name' => $name,
'money' => $money,
'remark' => $desc,
];
$result = $satf->transfer($bookid, $type=='bank' ? 2 : 1, $params, $pid);
return $result;
}
$result = \lib\Transfer::submit($type, $channel, $out_biz_no, $account, $name, $money, $desc);
$result['out_biz_no'] = $out_biz_no;
if($result['code']==0){
$paytime = $result['status'] == 1 ? 'NOW()' : null;
$data = ['biz_no'=>$out_biz_no, 'uid'=>$pid, 'type'=>$type, 'channel'=>$channelid, 'account'=>$account, 'username'=>$name, 'money'=>$money, 'costmoney'=>$need_money, 'addtime'=>'NOW()', 'paytime'=>$paytime, 'pay_order_no'=>$result['orderid'], 'status'=>$result['status'], 'desc'=>$desc];
if(isset($result['wxpackage'])) $data['ext'] = $result['wxpackage'];
$id = $DB->insert('transfer', $data);
if($id!==false){
changeUserMoney($pid, $need_money, false, '代付');
}
if($result['status'] == 1){
$result['msg']='转账成功!转账单据号:'.$result['orderid'].' 支付时间:'.$result['paydate'];
}elseif(isset($result['wxpackage'])){
$jumpurl = $siteurl.'paypage/wxtrans.php?type=transfer&id='.$id;
$result='提交成功!请在微信打开 '.$jumpurl.' 确认收款。转账单据号:'.$result['orderid'].' 支付时间:'.$result['paydate'];
$result['jumpurl'] = $jumpurl;
}else{
$result['msg']='提交成功!转账处理中。转账单据号:'.$result['orderid'].' 支付时间:'.$result['paydate'];
}
$result['cost_money'] = $need_money;
}
return $result;
}
public static function query(){
global $conf, $DB, $userrow, $queryArr;
$pid=intval($queryArr['pid']);
$groupconfig = getGroupConfig($userrow['gid']);
$conf = array_merge($conf, $groupconfig);
if(!$conf['user_transfer']) throw new Exception('管理员未开启代付功能');
if($userrow['transfer'] == 0) throw new Exception('商户未开启代付API接口');
$out_biz_no = trim($queryArr['out_biz_no']);
if(empty($out_biz_no)) throw new Exception('转账交易号(out_biz_no)不能为空');
$order = $DB->find('transfer', '*', ['biz_no'=>$out_biz_no, 'uid'=>$pid]);
if(!$order) throw new Exception('当前转账订单不存在');
if($order['status'] == 1){
$result = ['code'=>0, 'msg'=>'转账成功!', 'status'=>1, 'amount'=>$order['money'], 'cost_money'=>$order['costmoney'], 'paydate'=>$order['paytime'], 'remark'=>$order['desc']];
}elseif($order['status'] == 2){
$errmsg = ($order['result']?$order['result']:'原因未知');
$result = ['code'=>0, 'msg'=>'转账失败:'.($order['result']?$order['result']:'原因未知'), 'status'=>2, 'amount'=>$order['money'], 'cost_money'=>$order['money'], 'paydate'=>$order['paytime'], 'remark'=>$order['desc'], 'errmsg'=>$errmsg];
}else{
$result = \lib\Transfer::status($out_biz_no);
$result['remark'] = $order['desc'];
$result['cost_money'] = $order['costmoney'];
}
return $result;
}
public static function proof(){
global $conf, $DB, $userrow, $queryArr;
$pid=intval($queryArr['pid']);
$groupconfig = getGroupConfig($userrow['gid']);
$conf = array_merge($conf, $groupconfig);
if(!$conf['user_transfer']) throw new Exception('管理员未开启代付功能');
if($userrow['transfer'] == 0) throw new Exception('商户未开启代付API接口');
$out_biz_no = trim($queryArr['out_biz_no']);
if(empty($out_biz_no)) throw new Exception('转账交易号(out_biz_no)不能为空');
$order = $DB->find('transfer', '*', ['biz_no'=>$out_biz_no, 'uid'=>$pid]);
if(!$order) throw new Exception('当前转账订单不存在');
$result = \lib\Transfer::proof($out_biz_no);
return $result;
}
public static function balance(){
global $conf, $DB, $userrow, $queryArr;
$pid=intval($queryArr['pid']);
$groupconfig = getGroupConfig($userrow['gid']);
$conf = array_merge($conf, $groupconfig);
if(!$conf['user_transfer']) throw new Exception('管理员未开启代付功能');
if($userrow['transfer'] == 0) throw new Exception('商户未开启代付API接口');
if($conf['settle_type']==1){
$today=date("Y-m-d").' 00:00:00';
$order_today=$DB->getColumn("SELECT SUM(realmoney) from pre_order where uid={$pid} and tid<>2 and status=1 and endtime>='$today'");
if(!$order_today) $order_today = 0;
$enable_money=round($userrow['money']-$order_today,2);
if($enable_money<0)$enable_money=0;
}else{
$enable_money=$userrow['money'];
}
if(!$conf['transfer_rate'])$conf['transfer_rate'] = $conf['settle_rate'];
$result = ['code'=>0, 'available_money'=>strval($enable_money), 'transfer_rate'=>$conf['transfer_rate']];
return $result;
}
}
+171
View File
@@ -0,0 +1,171 @@
<?php
namespace lib;
class hieroglyphy{
private $characters;
private $numbers;
private $unescape;
private $functionConstructor;
public function __construct(){
$this->precharacters();
}
private function precharacters(){
$this->numbers = array(
"+[]",
"+!![]",
"!+[]+!![]",
"!+[]+!![]+!![]",
"!+[]+!![]+!![]+!![]",
"!+[]+!![]+!![]+!![]+!![]",
"!+[]+!![]+!![]+!![]+!![]+!![]",
"!+[]+!![]+!![]+!![]+!![]+!![]+!![]",
"!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]",
"!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]"
);
$this->characters = array(
"0" => "(" . $this->numbers[0] . "+[])",
"1" => "(" . $this->numbers[1] . "+[])",
"2" => "(" . $this->numbers[2] . "+[])",
"3" => "(" . $this->numbers[3] . "+[])",
"4" => "(" . $this->numbers[4] . "+[])",
"5" => "(" . $this->numbers[5] . "+[])",
"6" => "(" . $this->numbers[6] . "+[])",
"7" => "(" . $this->numbers[7] . "+[])",
"8" => "(" . $this->numbers[8] . "+[])",
"9" => "(" . $this->numbers[9] . "+[])"
);
$_object_Object = "[]+{}";
$_NaN = "+{}+[]";
$_true = "!![]+[]";
$_false = "![]+[]";
$_undefined = "[][[]]+[]";
$this->characters[" "] = "(" . $_object_Object . ")[" . $this->numbers[7] . "]";
$this->characters["["] = "(" . $_object_Object . ")[" . $this->numbers[0] . "]";
$this->characters["]"] = "(" . $_object_Object . ")[" . $this->characters[1] . "+" . $this->characters[4] . "]";
$this->characters["a"] = "(" . $_NaN . ")[" . $this->numbers[1] . "]";
$this->characters["b"] = "(" . $_object_Object . ")[" . $this->numbers[2] . "]";
$this->characters["c"] = "(" . $_object_Object . ")[" . $this->numbers[5] . "]";
$this->characters["d"] = "(" . $_undefined . ")[" . $this->numbers[2] . "]";
$this->characters["e"] = "(" . $_undefined . ")[" . $this->numbers[3] . "]";
$this->characters["f"] = "(" . $_false . ")[" . $this->numbers[0] . "]";
$this->characters["i"] = "(" . $_undefined . ")[" . $this->numbers[5] . "]";
$this->characters["j"] = "(" . $_object_Object . ")[" . $this->numbers[3] . "]";
$this->characters["l"] = "(" . $_false . ")[" . $this->numbers[2] . "]";
$this->characters["n"] = "(" . $_undefined . ")[" . $this->numbers[1] . "]";
$this->characters["o"] = "(" . $_object_Object . ")[" . $this->numbers[1] . "]";
$this->characters["r"] = "(" . $_true . ")[" . $this->numbers[1] . "]";
$this->characters["s"] = "(" . $_false . ")[" . $this->numbers[3] . "]";
$this->characters["t"] = "(" . $_true . ")[" . $this->numbers[0] . "]";
$this->characters["u"] = "(" . $_undefined . ")[" . $this->numbers[0] ."]";
$this->characters["N"] = "(" . $_NaN . ")[" . $this->numbers[0] . "]";
$this->characters["O"] = "(" . $_object_Object . ")[" . $this->numbers[8] . "]";
$_Infinity = "+(" . $this->numbers[1] . "+" . $this->characters["e"] . "+" . $this->characters[1] . "+" . $this->characters[0] . "+" . $this->characters[0] . "+" . $this->characters[0] . ")+[]";
$this->characters["y"] = "(" . $_Infinity . ")[" . $this->numbers[7] . "]";
$this->characters["I"] = "(" . $_Infinity . ")[" . $this->numbers[0] . "]";
$_1e100 = "+(" . $this->numbers[1] . "+" . $this->characters["e"] . "+" . $this->characters[1] . "+" . $this->characters[0] . "+" . $this->characters[0] . ")+[]";
$this->characters["+"] = "(" . $_1e100 . ")[" . $this->numbers[2] . "]";
$this->functionConstructor = "[][" . $this->hieroglyphyString("sort") . "][" . $this->hieroglyphyString("constructor") . "]";
//Below $this->characters need target http(s) pages
$locationString = "[]+" . $this->hieroglyphyScript("return location");
$this->characters["h"] = "(" . $locationString . ")" . "[" . $this->numbers[0] . "]";
$this->characters["p"] = "(" . $locationString . ")" . "[" . $this->numbers[3] . "]";
$this->characters["/"] = "(" . $locationString . ")" . "[" . $this->numbers[6] . "]";
$this->unescape = $this->hieroglyphyScript("return unescape");
$escape = $this->hieroglyphyScript("return escape");
$this->characters["%"] = $escape . "(" . $this->hieroglyphyString("[") . ")[" . $this->numbers[0] . "]";
}
private function getHexaString ($number, $digits) {
$string = bin2hex($number);
while (strlen($string) < $digits) {
$string = "0" . $string;
}
return $string;
}
private function getUnescapeSequence ($charCode) {
return $this->unescape . "(" .
$this->hieroglyphyString("%" . $this->getHexaString($charCode, 2)) . ")";
}
private function getHexaSequence ($charCode) {
return $this->hieroglyphyString("\\x" . $this->getHexaString($charCode, 2));
}
private function getUnicodeSequence ($charCode) {
return $this->hieroglyphyString("\\u" . $this->getHexaString($charCode, 4));
}
private function hieroglyphyCharacter ($char) {
$charCode = ord($char);
if (isset($this->characters[$char])) {
return $this->characters[$char];
}
if (($char == "\\") || ($char == "x")) {
//These chars must be handled appart becuase the others need them
$this->characters[$char] = $this->getUnescapeSequence($charCode);
return $this->characters[$char];
}
$shortestSequence = $this->getUnicodeSequence($charCode);
//ASCII $characters can be obtained with hexa and unscape sequences
if ($charCode < 128) {
$unescapeSequence = $this->getUnescapeSequence($charCode);
if (strlen($shortestSequence) > strlen($unescapeSequence)) {
$shortestSequence = $unescapeSequence;
}
$hexaSequence = $this->getHexaSequence($charCode);
if (strlen($shortestSequence) > strlen($hexaSequence)) {
$shortestSequence = $hexaSequence;
}
}
$this->characters[$char] = $shortestSequence;
return $shortestSequence;
}
public function hieroglyphyString ($str) {
$hieroglyphiedStr = "";
for ($i = 0; $i < strlen($str); $i++) {
$hieroglyphiedStr .= ($i > 0) ? "+" : "";
$hieroglyphiedStr .= $this->hieroglyphyCharacter($str[$i]);
}
return $hieroglyphiedStr;
}
public function hieroglyphyNumber ($n) {
$n = +$n;
if ($n <= 9) {
return $this->numbers[$n];
}
return "+(" . $this->hieroglyphyString(ord($n[10])) . ")";
}
public function hieroglyphyScript ($src) {
return $this->functionConstructor . "(" . $this->hieroglyphyString($src) . ")()";
}
}
+73
View File
@@ -0,0 +1,73 @@
<?php
namespace lib\mail;
class Aliyun
{
private $AccessKeyId;
private $AccessKeySecret;
function __construct($AccessKeyId, $AccessKeySecret)
{
$this->AccessKeyId = $AccessKeyId;
$this->AccessKeySecret = $AccessKeySecret;
}
private function aliyunSignature($parameters, $accessKeySecret, $method)
{
ksort($parameters);
$canonicalizedQueryString = '';
foreach ($parameters as $key => $value) {
if($value === null) continue;
$canonicalizedQueryString .= '&' . $this->percentEncode($key) . '=' . $this->percentEncode($value);
}
$stringToSign = $method . '&%2F&' . $this->percentencode(substr($canonicalizedQueryString, 1));
$signature = base64_encode(hash_hmac("sha1", $stringToSign, $accessKeySecret . "&", true));
return $signature;
}
private function percentEncode($str)
{
$search = ['+', '*', '%7E'];
$replace = ['%20', '%2A', '~'];
return str_replace($search, $replace, urlencode($str));
}
public function send($to, $sub, $msg, $from, $from_name)
{
if (empty($this->AccessKeyId) || empty($this->AccessKeySecret)) return false;
$url = 'https://dm.aliyuncs.com/';
$data = array(
'Action' => 'SingleSendMail',
'AccountName' => $from,
'ReplyToAddress' => 'false',
'AddressType' => 1,
'ToAddress' => $to,
'FromAlias' => $from_name,
'Subject' => $sub,
'HtmlBody' => $msg,
'Format' => 'JSON',
'Version' => '2015-11-23',
'AccessKeyId' => $this->AccessKeyId,
'SignatureMethod' => 'HMAC-SHA1',
'Timestamp' => gmdate('Y-m-d\TH:i:s\Z'),
'SignatureVersion' => '1.0',
'SignatureNonce' => random(8)
);
$data['Signature'] = $this->aliyunSignature($data, $this->AccessKeySecret, 'POST');
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
$json = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$arr = json_decode($json, true);
if ($httpCode == 200) {
return true;
} else {
return $arr['Message'];
}
}
}
@@ -0,0 +1,2 @@
<?php
namespace lib\mail\PHPMailer; class Exception extends \Exception { public function errorMessage() { return '<strong>' . htmlspecialchars($this->getMessage(), ENT_COMPAT | ENT_HTML401) . "</strong><br />\n"; } }
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+37
View File
@@ -0,0 +1,37 @@
<?php
namespace lib\mail;
class Sendcloud {
private $apiUser;
private $apiKey;
function __construct($apiUser, $apiKey){
$this->apiUser = $apiUser;
$this->apiKey = $apiKey;
}
public function send($to, $sub, $msg, $from, $from_name){
if(empty($this->apiUser)||empty($this->apiKey))return false;
$url='http://api.sendcloud.net/apiv2/mail/send';
$data=array(
'apiUser' => $this->apiUser,
'apiKey' => $this->apiKey,
'from' => $from,
'fromName' => $from_name,
'to' => $to,
'subject' => $sub,
'html' => $msg);
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$json=curl_exec($ch);
curl_close($ch);
$arr=json_decode($json,true);
if($arr['statusCode']==200){
return true;
}else{
return implode("\n",$arr['message']);
}
}
}
+68
View File
@@ -0,0 +1,68 @@
<?php
namespace lib\sms;
class Aliyun
{
private $AccessKeyId;
private $AccessKeySecret;
function __construct($AccessKeyId, $AccessKeySecret)
{
$this->AccessKeyId = $AccessKeyId;
$this->AccessKeySecret = $AccessKeySecret;
}
private function aliyunSignature($parameters, $accessKeySecret, $method)
{
ksort($parameters);
$canonicalizedQueryString = '';
foreach ($parameters as $key => $value) {
if($value === null) continue;
$canonicalizedQueryString .= '&' . $this->percentEncode($key) . '=' . $this->percentEncode($value);
}
$stringToSign = $method . '&%2F&' . $this->percentencode(substr($canonicalizedQueryString, 1));
$signature = base64_encode(hash_hmac("sha1", $stringToSign, $accessKeySecret . "&", true));
return $signature;
}
private function percentEncode($str)
{
$search = ['+', '*', '%7E'];
$replace = ['%20', '%2A', '~'];
return str_replace($search, $replace, urlencode($str));
}
public function send($phone, $param, $moban, $sign, $sitename)
{
if (empty($this->AccessKeyId) || empty($this->AccessKeySecret)) return false;
$url = 'https://dysmsapi.aliyuncs.com/';
$TemplateParam = json_encode($param);
$data = array(
'Action' => 'SendSms',
'PhoneNumbers' => $phone,
'SignName' => $sign,
'TemplateCode' => $moban,
'TemplateParam' => $TemplateParam,
'Format' => 'JSON',
'RegionId' => 'cn-hangzhou',
'Version' => '2017-05-25',
'AccessKeyId' => $this->AccessKeyId,
'SignatureMethod' => 'HMAC-SHA1',
'Timestamp' => gmdate('Y-m-d\TH:i:s\Z'),
'SignatureVersion' => '1.0',
'SignatureNonce' => random(8)
);
$data['Signature'] = $this->aliyunSignature($data, $this->AccessKeySecret, 'POST');
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
$json = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$arr = json_decode($json, true);
return $arr;
}
}
+86
View File
@@ -0,0 +1,86 @@
<?php
namespace lib\sms;
class Qcloud
{
private static $apiurl = 'https://yun.tim.qq.com/v5/tlssmssvr/sendsms';
private $appid;
private $appkey;
public function __construct($appid, $appkey)
{
$this->appid = $appid;
$this->appkey = $appkey;
}
/**
* 指定模板单发短信
* @param string $mobile 手机号码
* @param string $tpl_id 模板ID
* @param array $params 模板参数
* @param string $sign 签名内容
* @return bool
*/
public function send($mobile, $tpl_id, $params, $sign)
{
if (empty($this->appid) || empty($this->appkey)) return false;
$time = time();
$random = rand(100000, 999999);
$url = self::$apiurl . "?sdkappid=" . $this->appid . "&random=" . $random;
$data = [
'tel' => [
'nationcode' => '86',
'mobile' => $mobile
],
'params' => $params,
'time' => $time,
'tpl_id' => intval($tpl_id),
'sign' => $sign,
'sig' => $this->getSig($random, $time, $mobile),
];
try {
$res = $this->curlPost($url, json_encode($data));
$arr = json_decode($res, true);
return $arr;
} catch (\Exception $e) {
return ['result' => -1, 'errmsg' => $e->getMessage()];
}
}
//生成签名
private function getSig($random, $time, $mobile)
{
$signstr = 'appkey=' . $this->appkey . '&random=' . $random . '&time=' . $time . '&mobile=' . $mobile;
return hash("sha256", $signstr);
}
//发起POST请求
private function curlPost($url, $data)
{
$httpheader[] = "Accept: */*";
$httpheader[] = "Content-Type: application/json; charset=utf8";
$ch = curl_init();
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$res = curl_exec($ch);
if (curl_errno($ch) > 0) {
$errmsg = curl_error($ch);
curl_close($ch);
throw new \Exception($errmsg);
}
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode != 200) {
throw new \Exception('http_code=' . $httpCode);
}
return $res;
}
}
+38
View File
@@ -0,0 +1,38 @@
<?php
namespace lib\sms;
class SmsBao {
private $user;
private $pass;
function __construct($user, $pass){
$this->user = $user;
$this->pass = $pass;
}
public function send($phone, $param, $moban, $sign){
if(empty($this->user)||empty($this->pass))return false;
$statusStr = array(
"0" => "短信发送成功",
"-1" => "参数不全",
"-2" => "服务器空间不支持",
"30" => "密码错误",
"40" => "账号不存在",
"41" => "余额不足",
"42" => "帐户已过期",
"43" => "IP地址限制",
"50" => "内容含有敏感词"
);
foreach($param as $k=>$v){
$moban = str_replace('{'.$k.'}',$v,$moban);
}
$content = '【'.$sign.'】'.$moban;
$sendurl = "http://api.smsbao.com/sms?u=".$this->user."&p=".md5($this->pass)."&m=".$phone."&c=".urlencode($content);
$result = get_curl($sendurl) ;
if ($result == '0'){
return true;
}else{
return isset($statusStr[$result])?$statusStr[$result]:('CODE:'.$result);
}
}
}
+228
View File
@@ -0,0 +1,228 @@
<?php
namespace lib\wechat;
use Exception;
class WeWorkAPI
{
private $wid;
private $accessToken;
public function __construct($id)
{
$this->wid = $id;
}
public function getAccessToken($force = false)
{
global $DB;
if(!empty($this->accessToken)) return $this->accessToken;
$DB->beginTransaction();
try{
$row = $DB->getRow("SELECT * FROM pre_wework WHERE id='{$this->wid}' LIMIT 1 FOR UPDATE");
if(!$row) throw new Exception('当前企业微信不存在');
if($row['access_token'] && strtotime($row['expiretime']) - 200 >= time() && !$force){
$DB->rollback();
$this->accessToken = $row['access_token'];
return $this->accessToken;
}
$corpId = $row['appid'];
$secret = $row['appsecret'];
$url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=".$corpId."&corpsecret=".$secret;
$output = get_curl($url);
$res = json_decode($output, true);
if (isset($res['access_token'])) {
$this->accessToken = $res['access_token'];
$expire_time = time() + $res['expires_in'];
$DB->exec("UPDATE pre_wework SET access_token=:access_token,updatetime=NOW(),expiretime=:expiretime WHERE id=:id", [':access_token'=>$this->accessToken, ':expiretime'=>date("Y-m-d H:i:s", $expire_time), ':id'=>$this->wid]);
}elseif(isset($res['errmsg'])){
throw new Exception('AccessToken获取失败:'.$res['errmsg']);
}else{
throw new Exception('AccessToken获取失败');
}
$DB->commit();
return $this->accessToken;
}catch(Exception $e){
$DB->rollback();
throw $e;
}
}
//获取客服帐号列表
public function getKFList()
{
$accessToken = $this->getAccessToken();
$url = 'https://qyapi.weixin.qq.com/cgi-bin/kf/account/list?access_token='.$accessToken;
$post = ['offset'=>0, 'limit'=>100];
$response = get_curl($url, json_encode($post));
$result = json_decode($response, true);
if ($result['errcode'] == 0) {
return $result['account_list'];
}else{
throw new Exception('客服帐号列表获取失败:'.$result['errmsg']);
}
}
//获取微信客服链接
public function getKFURL($kfid, $scene)
{
$accessToken = $this->getAccessToken();
$url = 'https://qyapi.weixin.qq.com/cgi-bin/kf/add_contact_way?access_token='.$accessToken;
$post = ['open_kfid'=>$kfid, 'scene'=>$scene];
$response = get_curl($url, json_encode($post));
$result = json_decode($response, true);
if ($result['errcode'] == 0 && $result['url']) {
return $result['url'];
}else{
throw new Exception('微信客服链接获取失败:'.$result['errmsg']);
}
}
//读取消息
public function syncMsg($kfid, $token, $cursor = '')
{
$accessToken = $this->getAccessToken();
$url = 'https://qyapi.weixin.qq.com/cgi-bin/kf/sync_msg?access_token='.$accessToken;
$post = ['cursor'=>$cursor, 'token'=>$token, 'open_kfid'=>$kfid];
$response = get_curl($url, json_encode($post));
$result = json_decode($response, true);
if ($result['errcode'] == 0) {
return $result;
}else{
throw new Exception('客服消息列表获取失败:'.$result['errmsg']);
}
}
//加锁获取最新消息
public function lockGetMsg($kfid, $token)
{
global $DB;
$this->getAccessToken();
$DB->beginTransaction();
$wxkfaccount = $DB->getRow("SELECT `id`,`cursor` FROM pre_wxkfaccount WHERE openkfid=:openkfid LIMIT 1 FOR UPDATE", [':openkfid'=>$kfid]);
$cursor = $wxkfaccount['cursor'];
try{
$result = $this->syncMsg($kfid, $token, $cursor?$cursor:'');
}catch(Exception $e){
$DB->rollBack();
throw $e;
}
$cursor = $result['next_cursor'];
$DB->update('wxkfaccount', ['cursor'=>$cursor], ['id'=>$wxkfaccount['id']]);
$DB->commit();
return $result['msg_list'];
}
//发送消息
public function sendMsg($touser, $kfid, $msgtype, $msgparam)
{
$accessToken = $this->getAccessToken();
$url = 'https://qyapi.weixin.qq.com/cgi-bin/kf/send_msg?access_token='.$accessToken;
$post = ['touser'=>$touser, 'open_kfid'=>$kfid, 'msgtype'=>$msgtype];
$post[$msgtype] = $msgparam;
$response = get_curl($url, json_encode($post));
$result = json_decode($response, true);
if ($result['errcode'] == 0) {
return $result['msgid'];
}else{
throw new Exception('发送消息失败:'.$result['errmsg']);
}
}
//发送文本消息
public function sendTextMsg($touser, $kfid, $content)
{
$param = ['content'=>$content];
return $this->sendMsg($touser, $kfid, 'text', $param);
}
//发送菜单消息
public function sendMenuMsg($touser, $kfid, $head_content, $list, $tail_content = '')
{
$param = ['head_content'=>$head_content, 'list'=>$list];
if($tail_content) $param['tail_content'] = $tail_content;
return $this->sendMsg($touser, $kfid, 'msgmenu', $param);
}
//发送欢迎消息
public function sendWelcomeMsg($code, $msgtype, $msgparam)
{
$accessToken = $this->getAccessToken();
$url = 'https://qyapi.weixin.qq.com/cgi-bin/kf/send_msg_on_event?access_token='.$accessToken;
$post = ['code'=>$code, 'msgtype'=>$msgtype];
$post[$msgtype] = $msgparam;
$response = get_curl($url, json_encode($post));
$result = json_decode($response, true);
if ($result['errcode'] == 0) {
return $result['msgid'];
}else{
throw new Exception('发送消息失败:'.$result['errmsg']);
}
}
//发送欢迎文本消息
public function sendWelcomeTextMsg($code, $content)
{
$param = ['content'=>$content];
return $this->sendWelcomeMsg($code, 'text', $param);
}
//发送欢迎菜单消息
public function sendWelcomeMenuMsg($code, $head_content, $list, $tail_content = '')
{
$param = ['head_content'=>$head_content, 'list'=>$list];
if($tail_content) $param['tail_content'] = $tail_content;
return $this->sendWelcomeMsg($code, 'msgmenu', $param);
}
public function connect($appid, $callback, $agentid){
$url = 'https://open.weixin.qq.com/connect/oauth2/authorize';
$param = [
"appid" => $appid,
"redirect_uri" => $callback,
"response_type" => "code",
"scope" => "snsapi_base",
"agentid" => $agentid
];
$url .= '?'.http_build_query($param)."#wechat_redirect";
header("Location: ".$url);
exit;
}
public function get_userinfo($code){
$accessToken = $this->getAccessToken();
$url = 'https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo';
$param = [
"access_token" => $accessToken,
"code" => $code
];
$url .= '?'.http_build_query($param);
$response = get_curl($url);
$arr = json_decode($response, true);
if(isset($arr['errcode']) && $arr['errcode'] == 0){
return $arr;
}else{
throw new Exception('获取用户信息失败 ['.$arr['errcode'].']'.$arr['errmsg']);
}
}
public function send_message($touser, $agentid, $msgtype, $msgparam){
$accessToken = $this->getAccessToken();
$url = 'https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token='.$accessToken;
$post = [
'touser' => $touser,
'agentid' => intval($agentid),
'msgtype' => $msgtype
];
$post[$msgtype] = $msgparam;
$response = get_curl($url, json_encode($post));
$result = json_decode($response, true);
if ($result['errcode'] == 0) {
return true;
}else{
throw new Exception('发送消息失败:'.$result['errmsg']);
}
}
}
+103
View File
@@ -0,0 +1,103 @@
<?php
namespace lib\wechat;
use Exception;
class WeWorkMsg
{
private $token;
private $crypt;
public function __construct($token, $aeskey)
{
global $conf;
$this->token = $token;
$this->crypt = new WechatCrypt($aeskey);
}
//验证URL有效性
public function verifyURL()
{
if (!$this->verifySignature($_GET['echostr'])) {
exit('签名验证失败');
}
$msg = $this->crypt->decrypt($_GET['echostr']);
if(!$msg) exit('消息解密失败');
exit($msg);
}
//获取回调的消息内容
public function getMessage()
{
$xml = file_get_contents('php://input');
$arr = $this->xml2array($xml);
if (!$arr) exit('消息体解析失败');
if (!$this->verifySignature($arr['Encrypt'])) {
exit('签名验证失败');
}
$msgtext = $this->crypt->decrypt($arr['Encrypt']);
if(!$msgtext) exit('消息解密失败');
$msg = $this->xml2array($msgtext);
return $msg;
}
//响应消息内容
public function responseMessage($array, $corpid)
{
$xml = $this->array2Xml($array);
$encrypted = $this->crypt->encrypt($xml, $corpid);
$timestamp = time();
$nonce = time();
$signature = $this->getSignature($timestamp, $nonce, $encrypted);
$array = [
'Encrypt' => $encrypted,
'MsgSignature' => $signature,
'TimeStamp' => $timestamp,
'Nonce' => $nonce
];
echo $this->array2Xml($array);
}
//验证回调签名
private function verifySignature($msg_encrypt)
{
if (!(isset($_GET['msg_signature']) && isset($_GET['timestamp']) && isset($_GET['nonce']))) {
return false;
}
$signature = $this->getSignature($_GET['timestamp'], $_GET['nonce'], $msg_encrypt);
return $signature === $_GET['msg_signature'];
}
//生成SHA1签名
private function getSignature($timestamp, $nonce, $encrypt_msg)
{
$signatureArray = array($encrypt_msg, $this->token, $timestamp, $nonce);
sort($signatureArray, SORT_STRING);
return sha1(implode($signatureArray));
}
//转为XML数据
private function array2Xml($data)
{
if (!is_array($data)) {
return false;
}
$xml = '<xml>';
foreach ($data as $key => $val) {
$xml .= (is_numeric($val) ? "<{$key}>{$val}</{$key}>" : "<{$key}><![CDATA[{$val}]]></{$key}>");
}
return $xml . '</xml>';
}
//解析XML数据
private function xml2array($xml)
{
if (!$xml) {
return false;
}
LIBXML_VERSION < 20900 && libxml_disable_entity_loader(true);
return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true);
}
}
+149
View File
@@ -0,0 +1,149 @@
<?php
namespace lib\wechat;
use Exception;
class WechatAPI
{
private $wid;
private $accessToken;
private $jsapiTicket;
public function __construct($id)
{
$this->wid = $id;
}
public function getAccessToken($force = false)
{
global $DB;
if(!empty($this->accessToken)) return $this->accessToken;
$DB->beginTransaction();
try{
$row = $DB->getRow("SELECT * FROM pre_weixin WHERE id='{$this->wid}' LIMIT 1 FOR UPDATE");
if(!$row) throw new Exception('记录不存在');
if($row['access_token'] && strtotime($row['expiretime']) - 200 >= time() && !$force){
$DB->rollback();
$this->accessToken = $row['access_token'];
return $this->accessToken;
}
$appid = $row['appid'];
$secret = $row['appsecret'];
$url = "https://api.weixin.qq.com/cgi-bin/stable_token";
$post = json_encode(['grant_type'=>'client_credential', 'appid'=>$appid, 'secret'=>$secret]);
$output = get_curl($url, $post);
$res = json_decode($output, true);
if (isset($res['access_token'])) {
$this->accessToken = $res['access_token'];
$expire_time = time() + $res['expires_in'];
$DB->exec("UPDATE pre_weixin SET access_token=:access_token,updatetime=NOW(),expiretime=:expiretime WHERE id=:id", [':access_token'=>$this->accessToken, ':expiretime'=>date("Y-m-d H:i:s", $expire_time), ':id'=>$this->wid]);
}elseif(isset($res['errmsg'])){
throw new Exception('AccessToken获取失败:'.$res['errmsg']);
}else{
throw new Exception('AccessToken获取失败');
}
$DB->commit();
return $this->accessToken;
}catch(Exception $e){
$DB->rollback();
throw $e;
}
}
public function generate_scheme($path, $query, $expire = 600)
{
$access_token = $this->getAccessToken();
$url = "https://api.weixin.qq.com/wxa/generatescheme?access_token=".$access_token;
$data = ['jump_wxa'=>['path'=>$path, 'query'=>$query]];
if($expire>0){
$data['is_expire'] = true;
$data['expire_time'] = time()+$expire;
}
$output = get_curl($url, json_encode($data));
$res = json_decode($output, true);
if ($res && $res['errcode'] == 0) {
return $res['openlink'];
}else{
throw new Exception('urlscheme生成失败:'.$res['errmsg']);
}
}
//发送微信公众号模板消息
public function sendTemplateMessage($openid, $template_id, $jumpurl, $data){
$access_token = $this->getAccessToken();
$url = 'https://api.weixin.qq.com/cgi-bin/message/template/send?access_token='.$access_token;
$post = [
'touser' => $openid,
'template_id' => $template_id,
'url' => $jumpurl,
'data' => $data
];
$response = get_curl($url, json_encode($post));
$res = json_decode($response, true);
if ($res && $res['errcode'] == 0) {
return true;
}else{
throw new Exception('模板消息发送失败:'.$res['errmsg']);
}
}
public function getJsapiTicket($force = false)
{
global $CACHE;
if(!empty($this->jsapiTicket)) return $this->jsapiTicket;
$cachekey = 'wx_jsapi_ticket_'.$this->wid;
$row = $CACHE->read($cachekey);
if($row){
$row = unserialize($row);
if($row['ticket'] && strtotime($row['expiretime']) - 200 >= time() && !$force){
$this->jsapiTicket = $row['ticket'];
return $this->jsapiTicket;
}
}
$access_token = $this->getAccessToken();
$url = 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token='.$access_token.'&type=jsapi';
$output = get_curl($url);
$res = json_decode($output, true);
if (isset($res['ticket'])) {
$this->jsapiTicket = $res['ticket'];
$expire_time = time() + $res['expires_in'];
$CACHE->save($cachekey, ['ticket'=>$this->jsapiTicket, 'expiretime'=>date("Y-m-d H:i:s", $expire_time)], $res['expires_in']);
}elseif(isset($res['errmsg'])){
throw new Exception('JsapiTicket获取失败:'.$res['errmsg']);
}else{
throw new Exception('JsapiTicket获取失败');
}
}
public function getJsapiConfig($appid, $url, $jsApiList, $debug = false)
{
$ticket = $this->getJsapiTicket();
$data = [
'jsapi_ticket' => $ticket,
'timestamp' => time(),
'noncestr' => random(16),
'url' => $url
];
$config = [
'debug' => $debug,
'appId' => $appid,
'timestamp' => $data['timestamp'],
'nonceStr' => $data['noncestr'],
'signature' => $this->getSignature($data),
'jsApiList' => $jsApiList
];
return $config;
}
public function getSignature($data)
{
ksort($data);
$params = array();
foreach ($data as $key => $value) {
$params[] = "{$key}={$value}";
}
return sha1(join('&', $params));
}
}
+77
View File
@@ -0,0 +1,77 @@
<?php
namespace lib\wechat;
class WechatCrypt
{
private $key;
private $iv;
private static $block_size = 32;
public function __construct($enckey)
{
$this->key = base64_decode($enckey . '=');
$this->iv = substr($this->key, 0, 16);
}
public function encrypt($data, $appid)
{
$str = $this->getRandomStr() . pack('N', strlen($data)) . $data . $appid;
$str = $this->enPKSC7($str);
$encrypted = openssl_encrypt($str, 'AES-256-CBC', $this->key, OPENSSL_ZERO_PADDING, $this->iv);
return $encrypted;
}
public function decrypt($data)
{
$decrypted = openssl_decrypt($data, 'AES-256-CBC', $this->key, OPENSSL_ZERO_PADDING, $this->iv);
if(!$decrypted) return false;
$decrypted = $this->dePKSC7($decrypted);
$content = substr($decrypted, 16, strlen($decrypted));
$len_list = unpack('N', substr($content, 0, 4));
$xml_len = $len_list[1];
$xml_content = substr($content, 4, $xml_len);
$from_appid = substr($content, $xml_len + 4);
return $xml_content;
}
private function enPKSC7($text)
{
$block_size = self::$block_size; //128:16、256:32
$text_length = strlen($text);
//计算需要填充的位数
$amount_to_pad = $block_size - ($text_length % $block_size);
if ($amount_to_pad == 0) {
$amount_to_pad = $block_size;
}
//获得补位所用的字符
$pad_chr = chr($amount_to_pad);
$tmp = "";
for ($index = 0; $index < $amount_to_pad; $index++) {
$tmp .= $pad_chr;
}
return $text . $tmp;
}
private function dePKSC7($text)
{
$block_size = self::$block_size; //128:16、256:32
$pad = ord(substr($text, -1));
if ($pad < 1 || $pad > $block_size) {
$pad = 0;
}
return substr($text, 0, (strlen($text) - $pad));
}
private function getRandomStr()
{
$str = '';
$str_pol = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyl';
$max = strlen($str_pol) - 1;
for ($i = 0; $i < 16; $i++) {
$str .= $str_pol[mt_rand(0, $max)];
}
return $str;
}
}
+24
View File
@@ -0,0 +1,24 @@
<?php
$clientip=real_ip($conf['ip_type']?$conf['ip_type']:0);
if(isset($_COOKIE["admin_token"]))
{
$token=authcode(daddslashes($_COOKIE['admin_token']), 'DECODE', SYS_KEY);
list($user, $sid, $expiretime) = explode("\t", $token);
$session=md5($conf['admin_user'].$conf['admin_pwd'].$password_hash);
if($session==$sid && $expiretime>time()) {
$islogin=1;
}
}
if(isset($_COOKIE["user_token"]))
{
$token=authcode(daddslashes($_COOKIE['user_token']), 'DECODE', SYS_KEY);
list($uid, $sid, $expiretime) = explode("\t", $token);
$uid = intval($uid);
$userrow=$DB->getRow("SELECT * FROM pre_user WHERE uid=:uid limit 1", [':uid'=>$uid]);
$session=md5($userrow['uid'].$userrow['key'].$password_hash);
if($userrow && $session==$sid && $expiretime>time()) {
$islogin2=1;
}
}
?>
+109
View File
@@ -0,0 +1,109 @@
<?php
// 支付宝H5支付页面
if (!defined('IN_PLUGIN'))
exit();
?>
<html lang="zh-cn">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<meta name="renderer" content="webkit" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>支付宝支付手机版</title>
<link href="<?php echo $cdnpublic ?>twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet" />
<link href="/assets/pay/css/mobile-style.css" rel="stylesheet" />
</head>
<body>
<div class="main">
<div class="bg-ali"></div>
<div class="payment-logo">
<img src="/assets/pay/icon/alipay-white.svg" alt="logo">
<span class="logo-tile">支付宝支付</span>
</div>
<div class="payment-content">
<div class="content-info">
<h1>¥<?php echo $order['realmoney'] ?></h1>
<ul class="nk-activity">
<li class="nk-activity-item">
<span>商品名称:<?php echo $order['name'] ?></span>
</li>
<li class="nk-activity-item">
<span>商户订单号:<?php echo $order['trade_no'] ?></span>
</li>
<li class="nk-activity-item">
<span>创建时间:<?php echo $order['addtime'] ?></span>
</li>
</ul>
</div>
<div class="content-footer">
<a href="javascript:;" id="openUrl" class="btn btn-primary btn-block btn-lg">跳转到支付宝支付</a>
<a href="javascript:checkresult()" onclick="" class="btn btn-info btn-block btn-lg">检测支付状态</a>
</div>
</div>
</div>
<script src="<?php echo $cdnpublic ?>jquery/1.12.4/jquery.min.js"></script>
<script src="<?php echo $cdnpublic ?>layer/3.1.1/layer.min.js"></script>
<script>
var url_scheme = '<?php echo $code_url ?>';
function loadmsg() {
$.ajax({
type: "GET",
dataType: "json",
url: "/getshop.php",
data: { type: "wxpay", trade_no: "<?php echo $order['trade_no'] ?>" },
success: function (data) {
if (data.code == 1) {
layer.msg('支付成功,正在跳转中...', { icon: 16, shade: 0.01, time: 15000 });
setTimeout(window.location.href = <?php echo $redirect_url ?>, 1000);
} else {
setTimeout("loadmsg()", 2000);
}
},
error: function () {
setTimeout("loadmsg()", 2000);
}
});
}
function checkresult() {
$.ajax({
type: "GET",
dataType: "json",
url: "/getshop.php",
data: { type: "wxpay", trade_no: "<?php echo $order['trade_no'] ?>" },
success: function (data) {
if (data.code == 1) {
layer.msg('支付成功,正在跳转中...', { icon: 16, shade: 0.01, time: 15000 });
setTimeout(window.location.href = <?php echo $redirect_url ?>, 1000);
} else {
layer.msg('您还未完成付款,请继续付款', { shade: 0, time: 1500 });
}
},
error: function () {
layer.msg('服务器错误');
}
});
}
window.onload = function () {
window.onpopstate = function (e) {
if (e.state == 'forward' || confirm('是否取消支付并返回?')) {
window.history.back();
} else {
e.preventDefault();
window.history.pushState('forward', null, '');
}
};
window.history.pushState('forward', null, '');
document.getElementById("openUrl").href = url_scheme;
if (!url_scheme.startsWith('http://') && !url_scheme.startsWith('https://') && navigator.userAgent.indexOf('EdgA/') == -1) {
window.location.href = url_scheme;
}
setTimeout("loadmsg()", 3000);
}
</script>
</body>
</html>
+86
View File
@@ -0,0 +1,86 @@
<?php
// 支付宝JS支付页面
if(!defined('IN_PLUGIN'))exit();
?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<title>支付宝支付</title>
<link href="<?php echo $cdnpublic?>ionic/1.3.2/css/ionic.min.css" rel="stylesheet" />
</head>
<body>
<div class="bar bar-header bar-light" align-title="center">
<h1 class="title">支付宝支付</h1>
</div>
<div class="has-header" style="padding: 5px;position: absolute;width: 100%;">
<div class="text-center" style="color: #a09ee5;">
<i class="icon ion-information-circled" style="font-size: 80px;"></i><br>
<span>正在跳转...</span>
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
<script src="<?php echo $cdnpublic?>layer/3.1.1/layer.min.js"></script>
<script>
document.body.addEventListener('touchmove', function (event) {
event.preventDefault();
},{ passive: false });
var tradeNO = '<?php echo $alipay_trade_no?>';
function Alipayready(callback) {
// 如果jsbridge已经注入则直接调用
if (window.AlipayJSBridge) {
callback && callback();
} else {
// 如果没有注入则监听注入的事件
document.addEventListener('AlipayJSBridgeReady', callback, false);
}
}
function AlipayJsPay() {
Alipayready(function(){
AlipayJSBridge.call("tradePay",{
tradeNO: tradeNO
}, function(result){
var msg = "";
if(result.resultCode == "9000"){
loadmsg();
}else if(result.resultCode == "8000"){
msg = "正在处理中";
}else if(result.resultCode == "4000"){
msg = "订单支付失败";
}else if(result.resultCode == "6002"){
msg = "网络连接出错";
}
if (msg!="") {
layer.msg(msg);
}
});
});
}
function loadmsg() {
$.ajax({
type: "GET",
dataType: "json",
url: "/getshop.php",
data: {type: "wxpay", trade_no: "<?php echo TRADE_NO?>"},
success: function (data) {
if (data.code == 1) {
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.01,time: 15000});
window.location.href=<?php echo $redirect_url?>;
}else{
setTimeout("loadmsg()", 2000);
}
},
error: function () {
setTimeout("loadmsg()", 2000);
}
});
}
window.onload = AlipayJsPay();
</script>
</div>
</div>
</body>
</html>
+169
View File
@@ -0,0 +1,169 @@
<?php
// 支付宝扫码支付页面
if(!defined('IN_PLUGIN'))exit();
?>
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Language" content="zh-cn">
<meta name="renderer" content="webkit">
<title>支付宝扫码支付</title>
<link href="/assets/css/alipay_pay.css?v=3" rel="stylesheet" media="screen">
</head>
<body>
<img src="/assets/img/guide1.png" alt="引导图" class="top-guide" style="max-width: 100%;margin: auto;display:none">
<div class="guide" style="display:none"><img src="/assets/img/guide2.png" alt="引导图" style="width: 96%;"></div>
<div class="body">
<h1 class="mod-title">
<span class="ico-wechat"></span><span class="text">支付宝扫码支付</span>
</h1>
<div class="mod-ct">
<div class="order">
</div>
<div class="amount">¥<?php echo $order['realmoney']?></div>
<div class="qr-image" id="qrcode">
</div>
<div class="open_app" style="display: none;">
<a class="btn-open-app">打开支付宝APP继续付款</a><br/><br/><br/>
<a onclick="checkresult()" class="btn-check">我已付款,返回查看订单</a>
</div>
<div class="detail" id="orderDetail">
<dl class="detail-ct" style="display: none;">
<dt>购买物品</dt>
<dd id="productName"><?php echo $order['name']?></dd>
<dt>商户订单号</dt>
<dd id="billId"><?php echo $order['trade_no']?></dd>
<dt>创建时间</dt>
<dd id="createTime"><?php echo $order['addtime']?></dd>
</dl>
<a href="javascript:void(0)" class="arrow"><i class="ico-arrow"></i></a>
</div>
<div class="tip">
<span class="dec dec-left"></span>
<span class="dec dec-right"></span>
<div class="ico-scan"></div>
<div class="tip-text">
<p>请使用支付宝扫一扫</p>
<p>扫描二维码完成支付</p>
</div>
</div>
<div class="tip-text">
</div>
</div>
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
<script src="<?php echo $cdnpublic?>layer/3.1.1/layer.min.js"></script>
<script src="<?php echo $cdnpublic?>jquery.qrcode/1.0/jquery.qrcode.min.js"></script>
<script>
var code_url = '<?php echo $code_url?>';
var code_type = code_url.indexOf('data:image/')>-1?1:0;
if(code_type == 0){
var url_scheme = 'alipays://platformapi/startapp?appId=20000067&url=' + encodeURIComponent(code_url);
$('#qrcode').qrcode({
text: code_url,
width: 230,
height: 230,
foreground: "#000000",
background: "#ffffff",
typeNumber: -1
});
}else{
$('#qrcode').html('<img src="'+code_url+'"/>');
}
// 订单详情
$('#orderDetail .arrow').click(function (event) {
if ($('#orderDetail').hasClass('detail-open')) {
$('#orderDetail .detail-ct').slideUp(500, function () {
$('#orderDetail').removeClass('detail-open');
});
} else {
$('#orderDetail .detail-ct').slideDown(500, function () {
$('#orderDetail').addClass('detail-open');
});
}
});
function loadmsg() {
$.ajax({
type: "GET",
dataType: "json",
url: "/getshop.php",
data: {type: "alipay", trade_no: "<?php echo $order['trade_no']?>"},
success: function (data) {
if (data.code == 1) {
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.1,time: 15000});
setTimeout(window.location.href=data.backurl, 1000);
}else{
setTimeout("loadmsg()", 2000);
}
},
error: function () {
setTimeout("loadmsg()", 2000);
}
});
}
function checkresult() {
$.ajax({
type: "GET",
dataType: "json",
url: "/getshop.php",
data: {type: "alipay", trade_no: "<?php echo $order['trade_no']?>"},
success: function (data) {
if (data.code == 1) {
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.1,time: 15000});
setTimeout(window.location.href=data.backurl, 1000);
}else{
layer.msg('您还未完成付款,请继续付款', {shade: 0,time: 1500});
}
},
error: function () {
layer.msg('服务器错误');
}
});
}
var isMobile = function (){
var ua = navigator.userAgent;
var ipad = ua.match(/(iPad).*OS\s([\d_]+)/),
isIphone =!ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/),
isAndroid = ua.match(/(Android)\s+([\d.]+)/);
return isIphone || isAndroid;
}
function wx_open(){
$(".guide").show();
//layer.alert('请点击屏幕右上角,<b>在浏览器打开</b>即可跳转支付。<br/><font color="red">支付成功后,回到微信查看结果</font>', {title:'支付提示'});
}
window.onload = function(){
if(isMobile()){
window.onpopstate=function (e) {
if(e.state=='forward' || confirm('是否取消支付并返回?')){
window.history.back();
}else{
e.preventDefault();
window.history.pushState('forward', null, '');
}
};
window.history.pushState('forward', null, '');
}
if(isMobile() && code_type==0){
$('.open_app').show();
if(navigator.userAgent.indexOf('MicroMessenger/')>0){
$(".top-guide").show();
$('.btn-open-app').attr('href', 'javascript:wx_open()');
}else{
$('.btn-open-app').attr('href', url_scheme)
if(navigator.userAgent.indexOf('EdgA/')==-1){
setTimeout(window.location.href = url_scheme, 1000);
}
}
}
$("body").click(function(){
if (!$(".guide").is(":hidden")) {
$(".guide").hide();
}
});
setTimeout("loadmsg()", 2000);
}
</script>
</body>
</html>
+91
View File
@@ -0,0 +1,91 @@
<?php
// 支付宝扫码支付页面
if(!defined('IN_PLUGIN'))exit();
?>
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Language" content="zh-cn">
<meta name="renderer" content="webkit">
<title>支付宝扫码支付</title>
<link href="/assets/css/alipay_pay.css?v=2" rel="stylesheet" media="screen">
</head>
<body>
<div class="body">
<h1 class="mod-title">
<span class="ico-wechat"></span><span class="text">支付宝扫码支付</span>
</h1>
<div class="mod-ct">
<div class="order">
</div>
<div class="amount">¥<?php echo $order['realmoney']?></div>
<div class="qr-image" id="qrcode">
<iframe src="<?php echo $code_url?>" width="230px" height="230px" frameborder="0" scrolling="no" seamless></iframe>
</div>
<div class="detail" id="orderDetail">
<dl class="detail-ct" style="display: none;">
<dt>购买物品</dt>
<dd id="productName"><?php echo $order['name']?></dd>
<dt>商户订单号</dt>
<dd id="billId"><?php echo $order['trade_no']?></dd>
<dt>创建时间</dt>
<dd id="createTime"><?php echo $order['addtime']?></dd>
</dl>
<a href="javascript:void(0)" class="arrow"><i class="ico-arrow"></i></a>
</div>
<div class="tip">
<span class="dec dec-left"></span>
<span class="dec dec-right"></span>
<div class="ico-scan"></div>
<div class="tip-text">
<p>请使用支付宝扫一扫</p>
<p>扫描二维码完成支付</p>
</div>
</div>
<div class="tip-text">
</div>
</div>
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
<script src="<?php echo $cdnpublic?>layer/3.1.1/layer.min.js"></script>
<script src="<?php echo $cdnpublic?>jquery.qrcode/1.0/jquery.qrcode.min.js"></script>
<script>
// 订单详情
$('#orderDetail .arrow').click(function (event) {
if ($('#orderDetail').hasClass('detail-open')) {
$('#orderDetail .detail-ct').slideUp(500, function () {
$('#orderDetail').removeClass('detail-open');
});
} else {
$('#orderDetail .detail-ct').slideDown(500, function () {
$('#orderDetail').addClass('detail-open');
});
}
});
function loadmsg() {
$.ajax({
type: "GET",
dataType: "json",
url: "/getshop.php",
data: {type: "alipay", trade_no: "<?php echo $order['trade_no']?>"},
success: function (data) {
if (data.code == 1) {
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.1,time: 15000});
setTimeout(window.location.href=data.backurl, 1000);
}else{
setTimeout("loadmsg()", 2000);
}
},
error: function () {
setTimeout("loadmsg()", 2000);
}
});
}
window.onload = function(){
setTimeout("loadmsg()", 2000);
}
</script>
</body>
</html>
+149
View File
@@ -0,0 +1,149 @@
<?php
// 银联云闪付扫码支付页面
if(!defined('IN_PLUGIN'))exit();
?>
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Language" content="zh-cn">
<meta name="renderer" content="webkit">
<title>银联云闪付扫码支付</title>
<link href="/assets/css/bank_pay.css?v=3" rel="stylesheet" media="screen">
</head>
<body>
<div class="body">
<h1 class="mod-title">
<span class="ico-wechat"></span><span class="text">银联云闪付扫码支付</span>
</h1>
<div class="mod-ct">
<div class="order">
</div>
<div class="amount">¥<?php echo $order['realmoney']?></div>
<div class="qr-image" id="qrcode">
</div>
<div class="open_app" style="display: none;">
<a class="btn-open-app">打开云闪付APP继续付款</a><br/><br/><br/>
<a onclick="checkresult()" class="btn-check">我已付款,返回查看订单</a>
</div>
<div class="detail" id="orderDetail">
<dl class="detail-ct" style="display: none;">
<dt>购买物品</dt>
<dd id="productName"><?php echo $order['name']?></dd>
<dt>商户订单号</dt>
<dd id="billId"><?php echo $order['trade_no']?></dd>
<dt>创建时间</dt>
<dd id="createTime"><?php echo $order['addtime']?></dd>
</dl>
<a href="javascript:void(0)" class="arrow"><i class="ico-arrow"></i></a>
</div>
<div class="tip">
<span class="dec dec-left"></span>
<span class="dec dec-right"></span>
<div class="ico-scan"></div>
<div class="tip-text">
<p>请使用银联云闪付扫一扫</p>
<p>扫描二维码完成支付</p>
</div>
</div>
<div class="tip-text">
</div>
</div>
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
<script src="<?php echo $cdnpublic?>layer/3.1.1/layer.min.js"></script>
<script src="<?php echo $cdnpublic?>jquery.qrcode/1.0/jquery.qrcode.min.js"></script>
<script>
var code_url = '<?php echo $code_url?>';
var code_type = code_url.indexOf('data:image/')>-1?1:0;
if(code_type == 0){
var url_scheme = 'upwallet://html/' + code_url.replace('https://', '').replace('http://', '');
$('#qrcode').qrcode({
text: code_url,
width: 230,
height: 230,
foreground: "#000000",
background: "#ffffff",
typeNumber: -1
});
}else{
$('#qrcode').html('<img src="'+code_url+'"/>');
}
// 订单详情
$('#orderDetail .arrow').click(function (event) {
if ($('#orderDetail').hasClass('detail-open')) {
$('#orderDetail .detail-ct').slideUp(500, function () {
$('#orderDetail').removeClass('detail-open');
});
} else {
$('#orderDetail .detail-ct').slideDown(500, function () {
$('#orderDetail').addClass('detail-open');
});
}
});
function loadmsg() {
$.ajax({
type: "GET",
dataType: "json",
url: "/getshop.php",
data: {type: "bank", trade_no: "<?php echo $order['trade_no']?>"},
success: function (data) {
if (data.code == 1) {
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.1,time: 15000});
setTimeout(window.location.href=data.backurl, 1000);
}else{
setTimeout("loadmsg()", 2000);
}
},
error: function () {
setTimeout("loadmsg()", 2000);
}
});
}
function checkresult() {
$.ajax({
type: "GET",
dataType: "json",
url: "/getshop.php",
data: {type: "bank", trade_no: "<?php echo $order['trade_no']?>"},
success: function (data) {
if (data.code == 1) {
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.1,time: 15000});
setTimeout(window.location.href=data.backurl, 1000);
}else{
layer.msg('您还未完成付款,请继续付款', {shade: 0,time: 1500});
}
},
error: function () {
layer.msg('服务器错误');
}
});
}
var isMobile = function (){
var ua = navigator.userAgent;
var ipad = ua.match(/(iPad).*OS\s([\d_]+)/),
isIphone =!ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/),
isAndroid = ua.match(/(Android)\s+([\d.]+)/);
return isIphone || isAndroid;
}
function wx_open(){
layer.alert('请点击屏幕右上角,<b>在浏览器打开</b>即可跳转支付。<br/><font color="red">支付成功后,回到微信查看结果</font>', {title:'支付提示'});
}
window.onload = function(){
if(isMobile() && code_type==0){
$('.open_app').show();
if(navigator.userAgent.indexOf('MicroMessenger/')>0){
$('.btn-open-app').attr('onclick', 'wx_open()');
}else{
$('.btn-open-app').attr('href', url_scheme);
if(navigator.userAgent.indexOf('EdgA/')==-1){
window.location.href = url_scheme;
}
}
}
setTimeout("loadmsg()", 2000);
}
</script>
</body>
</html>
+79
View File
@@ -0,0 +1,79 @@
<?php
/**
* 实名认证成功页面
**/
if(!defined('IN_CRONLITE'))exit();
?>
<html class="weui-msg">
<head>
<meta charset="UTF-8">
<meta id="viewport" name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<title>实名认证成功</title>
<link href="/assets/css/weui.min.css" rel="stylesheet">
<style>.page{position:absolute;top:0;right:0;bottom:0;left:0;overflow-y:auto;-webkit-overflow-scrolling:touch;box-sizing:border-box}</style>
</head>
<body>
<div class="container">
<div class="page">
<div class="weui-msg">
<div class="weui-msg__icon-area">
<i class="weui-icon-success weui-icon_msg"></i>
</div>
<div class="weui-msg__text-area">
<h2 class="weui-msg__title">实名认证成功</h2>
<p class="weui-msg__desc">请返回浏览器查看结果</p>
</div>
<div class="weui-msg__opr-area">
<p class="weui-btn-area">
<a href="javascript:;" class="weui-btn weui-btn_default" id="Close">关闭</a>
</p>
</div>
<div class="weui-msg__extra-area">
<div class="weui-footer"><p class="weui-footer__links"></p></div>
</div>
</div>
</div>
</div>
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
<script>
document.body.addEventListener('touchmove', function (event) {
event.preventDefault();
},{ passive: false });
if(navigator.userAgent.indexOf("AlipayClient") > -1){
function Alipayready(callback) {
if (window.AlipayJSBridge) {
callback && callback();
} else {
document.addEventListener('AlipayJSBridgeReady', callback, false);
}
}
Alipayready(function(){
$('#Close').click(function() {
AlipayJSBridge.call('popWindow');
});
})
}else if(navigator.userAgent.indexOf("MicroMessenger") > -1){
if (typeof WeixinJSBridge == "undefined") {
if (document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
} else if (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', jsApiCall);
document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
}
} else {
jsApiCall();
}
function jsApiCall() {
$('#Close').click(function() {
WeixinJSBridge.call('closeWindow');
});
}
}else{
$('#Close').click(function() {
window.opener=null;window.close();
});
}
</script>
</body>
</html>
+38
View File
@@ -0,0 +1,38 @@
<?php
/*
* 支付失败提示页面
*/
if(!defined('IN_PLUGIN'))exit();
?>
<html class="weui-msg">
<head>
<meta charset="UTF-8">
<meta id="viewport" name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<title>错误提示</title>
<link href="/assets/css/weui.min.css" rel="stylesheet">
<style>.page{position:absolute;top:0;right:0;bottom:0;left:0;overflow-y:auto;-webkit-overflow-scrolling:touch;box-sizing:border-box}</style>
</head>
<body>
<div class="container">
<div class="page">
<div class="weui-msg">
<div class="weui-msg__icon-area">
<i class="weui-icon-warn weui-icon_msg"></i>
</div>
<div class="weui-msg__text-area">
<h2 class="weui-msg__title">错误提示</h2>
<p class="weui-msg__desc">支付失败或支付超时,请返回重新发起支付</p>
</div>
<div class="weui-msg__extra-area">
<div class="weui-footer"><p class="weui-footer__links"></p></div>
</div>
</div>
</div>
</div>
<script>
document.body.addEventListener('touchmove', function (event) {
event.preventDefault();
},{ passive: false });
</script>
</body>
</html>
+108
View File
@@ -0,0 +1,108 @@
<?php
// 京东扫码支付页面
if(!defined('IN_PLUGIN'))exit();
?>
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Language" content="zh-cn">
<meta name="renderer" content="webkit">
<title>京东扫码支付</title>
<link href="/assets/css/jd_pay.css" rel="stylesheet" media="screen">
</head>
<body>
<div class="body">
<h1 class="mod-title">
<span class="ico-wechat"></span><span class="text">京东扫码支付</span>
</h1>
<div class="mod-ct">
<div class="order">
</div>
<div class="amount">¥<?php echo $order['realmoney']?></div>
<div class="qr-image" id="qrcode">
</div>
<div class="detail" id="orderDetail">
<dl class="detail-ct" style="display: none;">
<dt>商家</dt>
<dd id="storeName"><?php echo $sitename?></dd>
<dt>购买物品</dt>
<dd id="productName"><?php echo $order['name']?></dd>
<dt>商户订单号</dt>
<dd id="billId"><?php echo $order['trade_no']?></dd>
<dt>创建时间</dt>
<dd id="createTime"><?php echo $order['addtime']?></dd>
</dl>
<a href="javascript:void(0)" class="arrow"><i class="ico-arrow"></i></a>
</div>
<div class="tip">
<span class="dec dec-left"></span>
<span class="dec dec-right"></span>
<div class="ico-scan"></div>
<div class="tip-text">
<p>请使用京东APP扫一扫</p>
<p>扫描二维码完成支付</p>
</div>
</div>
<div class="tip-text">
</div>
</div>
</div>
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
<script src="<?php echo $cdnpublic?>layer/3.1.1/layer.min.js"></script>
<script src="<?php echo $cdnpublic?>jquery.qrcode/1.0/jquery.qrcode.min.js"></script>
<script>
var code_url = '<?php echo $code_url?>';
var code_type = code_url.indexOf('data:image/')>-1?1:0;
if(code_type == 0){
$('#qrcode').qrcode({
text: code_url,
width: 230,
height: 230,
foreground: "#000000",
background: "#ffffff",
typeNumber: -1
});
}else{
$('#qrcode').html('<img src="'+code_url+'"/>');
}
// 订单详情
$('#orderDetail .arrow').click(function (event) {
if ($('#orderDetail').hasClass('detail-open')) {
$('#orderDetail .detail-ct').slideUp(500, function () {
$('#orderDetail').removeClass('detail-open');
});
} else {
$('#orderDetail .detail-ct').slideDown(500, function () {
$('#orderDetail').addClass('detail-open');
});
}
});
function loadmsg() {
$.ajax({
type: "GET",
dataType: "json",
url: "/getshop.php",
data: {type: "jdpay", trade_no: "<?php echo $order['trade_no']?>"},
success: function (data) {
if (data.code == 1) {
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.1,time: 15000});
setTimeout(window.location.href=data.backurl, 1000);
}else{
setTimeout("loadmsg()", 2000);
}
},
error: function () {
setTimeout("loadmsg()", 2000);
}
});
}
window.onload = function(){
setTimeout("loadmsg()", 2000);
}
</script>
</body>
</html>
+67
View File
@@ -0,0 +1,67 @@
<?php
if(!defined('IN_PLUGIN'))exit();
$useragent = strtolower($_SERVER['HTTP_USER_AGENT']);
if(strpos($useragent, 'iphone')!==false || strpos($useragent, 'ipod')!==false){
$alert = '<img src="//puep.qpic.cn/coral/Q3auHgzwzM4fgQ41VTF2rLrNvRzmibibqrjTFj5g2kzGyoQj3ViartAEQ/0" class="icon-safari" /> <span id="openm">Safari打开</span>';
}elseif(strpos($useragent, 'micromessenger')!==false){
$alert = '<img src="//puep.qpic.cn/coral/Q3auHgzwzM4fgQ41VTF2rLbNVmztN9ia6GPRJ0IFicucFTr4Pp8xzibsw/0" class="icon-safari" /> <span id="openm">浏览器打开</span>';
}else{
$alert = '<img src="//puep.qpic.cn/coral/Q3auHgzwzM4fgQ41VTF2rOCTm6gtUeQKX7m84xg47iaVosibGckrP0JQ/0" class="icon-safari" /> <span id="openm">浏览器打开</span>';
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>请使用浏览器打开</title>
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
<meta content="yes" name="apple-mobile-web-app-capable"/>
<meta content="black" name="apple-mobile-web-app-status-bar-style"/>
<meta name="format-detection" content="telephone=no"/>
<meta content="false" name="twcClient" id="twcClient"/>
<meta name="aplus-touch" content="1"/>
<style>
body,html{width:100%;height:100%}
*{margin:0;padding:0}
body{background-color:#fff}
.top-bar-guidance{font-size:15px;color:#fff;height:70%;line-height:1.8;padding-left:20px;padding-top:20px;background:url(//gw.alicdn.com/tfs/TB1eSZaNFXXXXb.XXXXXXXXXXXX-750-234.png) center top/contain no-repeat}
.top-bar-guidance .icon-safari{width:25px;height:25px;vertical-align:middle;margin:0 .2em}
.app-download-tip{margin:0 auto;width:290px;text-align:center;font-size:15px;color:#2466f4;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAcAQMAAACak0ePAAAABlBMVEUAAAAdYfh+GakkAAAAAXRSTlMAQObYZgAAAA5JREFUCNdjwA8acEkAAAy4AIE4hQq/AAAAAElFTkSuQmCC) left center/auto 15px repeat-x}
.app-download-tip .guidance-desc{background-color:#fff;padding:0 5px}
.app-download-btn{display:block;width:214px;height:40px;line-height:40px;margin:18px auto 0 auto;text-align:center;font-size:18px;color:#2466f4;border-radius:20px;border:.5px #2466f4 solid;text-decoration:none}
</style>
</head>
<body>
<div class="top-bar-guidance">
<p>点击右上角<?php echo $alert?></p>
<p>可以继续浏览本站哦~</p>
</div>
<div class="app-download-tip">
<span class="guidance-desc">您也可以复制本站网址,到其它浏览器打开</span>
</div>
<a class="app-download-btn" id="J_BtnDowanloadApp">点此继续访问</a>
<a style="display: none;" href="" id="vurl" rel="noreferrer"></a>
<script src="//lib.baomitu.com/jquery/1.12.4/jquery.min.js"></script>
<script>
function openu(u){
document.getElementById("vurl").href= u;
document.getElementById("vurl").click();
}
var url = window.location.href;
document.querySelector('body').addEventListener('touchmove', function (event) {
event.preventDefault();
});
if(navigator.userAgent.indexOf("QQ/") > -1){
openu("ucbrowser://"+url);
openu("mttbrowser://url="+url);
openu("googlechrome://"+url);
$("html").on("click",function(){
openu("ucbrowser://"+url);
openu("mttbrowser://url="+url);
openu("googlechrome://"+url);
});
}
</script>
</body>
</html>
File diff suppressed because one or more lines are too long
+101
View File
@@ -0,0 +1,101 @@
<?php
/*
* 获取openid结果页面
*/
if(!defined('IN_CRONLITE'))exit();
?>
<html class="weui-msg">
<head>
<meta charset="UTF-8">
<meta id="viewport" name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<title>获取<?php echo $openid_name?></title>
<link href="/assets/css/weui.min.css" rel="stylesheet">
<style>.page{position:absolute;top:0;right:0;bottom:0;left:0;overflow-y:auto;-webkit-overflow-scrolling:touch;box-sizing:border-box}</style>
</head>
<body>
<div class="container">
<div class="page">
<div class="weui-form">
<div class="weui-msg__icon-area">
<i class="weui-icon-success weui-icon_msg"></i>
</div>
<div class="weui-form__text-area">
<h2 class="weui-form__title">获取<?php echo $openid_name?>成功</h2>
</div>
<div class="weui-form__control-area">
<div class="weui-cells__group weui-cells__group_form">
<div class="weui-cells__title">如未自动填写,请手动复制下方<?php echo $openid_name?></div>
<div class="weui-cells weui-cells_form">
<div class="weui-cell weui-cell_active">
<div class="weui-cell__bd">
<textarea class="weui-textarea" rows="2" style="text-align:center"><?php echo $openid_content?></textarea>
</div>
</div>
</div>
</div>
</div>
<div class="weui-form__opr-area">
<a role="button" class="weui-btn weui-btn_default copy-btn" href="javascript:" data-clipboard-text="<?php echo $openid_content?>">点击复制</a>
<a href="javascript:;" class="weui-btn weui-btn_warn" id="Close">关闭</a>
</div>
<div class="weui-form__extra-area">
<div class="weui-footer"><p class="weui-footer__links"></p><p class="weui-footer__text">Copyright © <?php echo date("Y")?> <?php echo $conf['sitename']?></p></div>
</div>
</div>
</div>
</div>
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
<script src="<?php echo $cdnpublic?>layer/3.1.1/layer.min.js"></script>
<script src="<?php echo $cdnpublic?>clipboard.js/1.7.1/clipboard.min.js"></script>
<script>
document.body.addEventListener('touchmove', function (event) {
event.preventDefault();
},{ passive: false });
$(document).ready(function(){
var clipboard = new Clipboard('.copy-btn');
clipboard.on('success', function (e) {
layer.msg('复制成功!', {icon: 1});
});
clipboard.on('error', function (e) {
layer.msg('复制失败,请长按链接后手动复制', {icon: 2});
});
});
if(navigator.userAgent.indexOf("AlipayClient/") > -1){
function Alipayready(callback) {
if (window.AlipayJSBridge) {
callback && callback();
} else {
document.addEventListener('AlipayJSBridgeReady', callback, false);
}
}
Alipayready(function(){
$('#Close').click(function() {
AlipayJSBridge.call('popWindow');
});
})
}else if(navigator.userAgent.indexOf("MicroMessenger/") > -1){
if (typeof WeixinJSBridge == "undefined") {
if (document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
} else if (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', jsApiCall);
document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
}
} else {
jsApiCall();
}
function jsApiCall() {
$('#Close').click(function() {
WeixinJSBridge.call('closeWindow');
});
}
}else if(navigator.userAgent.indexOf("QQ/") > -1){
$('#Close').hide();
}else {
$('#Close').click(function() {
window.opener=null;window.close();
});
}
</script>
</body>
</html>
+65
View File
@@ -0,0 +1,65 @@
<?php
// QQ公众号支付页面
if(!defined('IN_PLUGIN'))exit();
?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<link href="<?php echo $cdnpublic?>ionic/1.3.2/css/ionic.min.css" rel="stylesheet" />
</head>
<body>
<div class="bar bar-header bar-light" align-title="center">
<h1 class="title">QQ钱包支付</h1>
</div>
<div class="has-header" style="padding: 5px;position: absolute;width: 100%;">
<div class="text-center" style="color: #a09ee5;">
<i class="icon ion-information-circled" style="font-size: 80px;"></i><br>
<span>正在跳转...</span>
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
<script src="<?php echo $cdnpublic?>layer/3.1.1/layer.min.js"></script>
<script src="//open.mobile.qq.com/sdk/qqapi.js?_bid=152"></script>
<script>
document.body.addEventListener('touchmove', function (event) {
event.preventDefault();
},{ passive: false });
function callpay()
{
mqq.tenpay.pay({
tokenId: '<?php echo $tokenId; ?>',
appInfo: "<?php echo $appInfo; ?>"
}, function(result, resultCode){
if(result.resultCode == 0){ //支付成功
loadmsg();
}
});
}
function loadmsg() {
$.ajax({
type: "GET",
dataType: "json",
url: "/getshop.php",
data: {type: "qqpay", trade_no: "<?php echo $order['trade_no']?>"},
success: function (data) {
if (data.code == 1) {
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.1,time: 15000});
setTimeout(window.location.href=data.backurl, 1000);
}else{
setTimeout("loadmsg()", 2000);
}
},
error: function () {
setTimeout("loadmsg()", 2000);
}
});
}
window.onload = callpay();
</script>
</div>
</div>
</body>
</html>
+112
View File
@@ -0,0 +1,112 @@
<?php
// QQ钱包扫码支付页面
if(!defined('IN_PLUGIN'))exit();
?>
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Language" content="zh-cn">
<meta name="renderer" content="webkit">
<title>QQ钱包扫码支付</title>
<link href="/assets/css/mqq_pay.css?v=1" rel="stylesheet" media="screen">
</head>
<body>
<div class="body">
<h1 class="mod-title">
<span class="ico-wechat"></span><span class="text">QQ钱包扫码支付</span>
</h1>
<div class="mod-ct">
<div class="order">
</div>
<div class="amount">¥<?php echo $order['realmoney']?></div>
<div class="qr-image" id="qrcode">
</div>
<div class="detail" id="orderDetail">
<dl class="detail-ct" style="display: none;">
<dt>商家</dt>
<dd id="storeName"><?php echo $sitename?></dd>
<dt>购买物品</dt>
<dd id="productName"><?php echo $order['name']?></dd>
<dt>商户订单号</dt>
<dd id="billId"><?php echo $order['trade_no']?></dd>
<dt>创建时间</dt>
<dd id="createTime"><?php echo $order['addtime']?></dd>
</dl>
<a href="javascript:void(0)" class="arrow"><i class="ico-arrow"></i></a>
</div>
<div class="tip">
<span class="dec dec-left"></span>
<span class="dec dec-right"></span>
<div class="ico-scan"></div>
<div class="tip-text">
<p>请使用手机QQ扫一扫</p>
<p>扫描二维码完成支付</p>
</div>
</div>
<div class="tip-text">
</div>
</div>
<div class="foot">
<div class="inner">
<p>手机用户可保存上方二维码到手机中</p>
<p>在手机QQ扫一扫中选择“相册”即可</p>
</div>
</div>
</div>
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
<script src="<?php echo $cdnpublic?>layer/3.1.1/layer.min.js"></script>
<script src="<?php echo $cdnpublic?>jquery.qrcode/1.0/jquery.qrcode.min.js"></script>
<script>
var code_url = '<?php echo $code_url?>';
var code_type = code_url.indexOf('data:image/')>-1?1:0;
if(code_type == 0){
$('#qrcode').qrcode({
text: code_url,
width: 230,
height: 230,
foreground: "#000000",
background: "#ffffff",
typeNumber: -1
});
}else{
$('#qrcode').html('<img src="'+code_url+'"/>');
}
// 订单详情
$('#orderDetail .arrow').click(function (event) {
if ($('#orderDetail').hasClass('detail-open')) {
$('#orderDetail .detail-ct').slideUp(500, function () {
$('#orderDetail').removeClass('detail-open');
});
} else {
$('#orderDetail .detail-ct').slideDown(500, function () {
$('#orderDetail').addClass('detail-open');
});
}
});
function loadmsg() {
$.ajax({
type: "GET",
dataType: "json",
url: "/getshop.php",
data: {type: "qqpay", trade_no: "<?php echo $order['trade_no']?>"},
success: function (data) {
if (data.code == 1) {
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.1,time: 15000});
setTimeout(window.location.href=data.backurl, 1000);
}else{
setTimeout("loadmsg()", 2000);
}
},
error: function () {
setTimeout("loadmsg()", 2000);
}
});
}
window.onload = loadmsg();
</script>
</body>
</html>
+111
View File
@@ -0,0 +1,111 @@
<?php
// QQ钱包手机扫码页面
if (!defined('IN_PLUGIN'))
exit();
?>
<html lang="zh-cn">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<meta name="renderer" content="webkit" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>QQ钱包支付手机版</title>
<link href="<?php echo $cdnpublic ?>twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet" />
<link href="/assets/pay/css/mobile-style.css" rel="stylesheet" />
</head>
<body>
<div class="main">
<div class="bg-qq"></div>
<div class="payment-logo">
<img src="/assets/pay/icon/qqpay.svg" alt="logo">
<span class="logo-tile">QQ支付</span>
</div>
<div class="payment-content">
<div class="content-info">
<h1>¥<?php echo $order['realmoney'] ?></h1>
<ul class="nk-activity">
<li class="nk-activity-item">
<span>商品名称:<?php echo $order['name'] ?></span>
</li>
<li class="nk-activity-item">
<span>商户订单号:<?php echo $order['trade_no'] ?></span>
</li>
<li class="nk-activity-item">
<span>创建时间:<?php echo $order['addtime'] ?></span>
</li>
</ul>
</div>
<div class="content-footer">
<a href="javascript:;" id="openUrl" class="btn btn-warning btn-block btn-lg">跳转到QQ支付</a>
<a href="javascript:checkresult()" onclick="" class="btn btn-info btn-block btn-lg">检测支付状态</a>
<a href="?qrcode=1" class="btn btn-default btn-block btn-lg">切换扫码支付</a>
</div>
</div>
</div>
<script src="<?php echo $cdnpublic ?>jquery/1.12.4/jquery.min.js"></script>
<script src="<?php echo $cdnpublic ?>layer/3.1.1/layer.min.js"></script>
<script>
var code_url = '<?php echo $code_url ?>';
var url_scheme = 'mqqapi://forward/url?src_type=web&style=default&=1&version=1&url_prefix=' + window.btoa(code_url);
function loadmsg() {
$.ajax({
type: "GET",
dataType: "json",
url: "/getshop.php",
data: { type: "qqpay", trade_no: "<?php echo $order['trade_no'] ?>" },
success: function (data) {
if (data.code == 1) {
layer.msg('支付成功,正在跳转中...', { icon: 16, shade: 0.1, time: 15000 });
setTimeout(window.location.href = data.backurl, 1000);
} else {
setTimeout("loadmsg()", 2000);
}
},
error: function () {
setTimeout("loadmsg()", 2000);
}
});
}
function checkresult() {
$.ajax({
type: "GET",
dataType: "json",
url: "/getshop.php",
data: { type: "qqpay", trade_no: "<?php echo $order['trade_no'] ?>" },
success: function (data) {
if (data.code == 1) {
layer.msg('支付成功,正在跳转中...', { icon: 16, shade: 0.1, time: 15000 });
setTimeout(window.location.href = data.backurl, 1000);
} else {
layer.msg('您还未完成付款,请继续付款', { shade: 0, time: 1500 });
}
},
error: function () {
layer.msg('服务器错误');
}
});
}
window.onload = function () {
window.onpopstate = function (e) {
if (e.state == 'forward' || confirm('是否取消支付并返回?')) {
window.history.back();
} else {
e.preventDefault();
window.history.pushState('forward', null, '');
}
};
window.history.pushState('forward', null, '');
document.getElementById("openUrl").href = url_scheme;
if (navigator.userAgent.indexOf('EdgA/') == -1) {
window.location.href = url_scheme;
}
setTimeout("loadmsg()", 3000);
}
</script>
</body>
</html>
+52
View File
@@ -0,0 +1,52 @@
<?php
// 微信H5支付返回中转页面
if(!defined('IN_PLUGIN'))exit();
?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<link href="<?php echo $cdnpublic?>ionic/1.3.2/css/ionic.min.css" rel="stylesheet" />
</head>
<body>
<div class="bar bar-header bar-light" align-title="center">
<h1 class="title">订单处理结果</h1>
</div>
<div class="has-header" style="padding: 5px;position: absolute;width: 100%;">
<div class="text-center" style="color: #a09ee5;">
<i class="icon ion-information-circled" style="font-size: 80px;"></i><br>
<span>正在检测付款结果...</span>
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
<script src="<?php echo $cdnpublic?>layer/3.1.1/layer.min.js"></script>
<script>
document.body.addEventListener('touchmove', function (event) {
event.preventDefault();
},{ passive: false });
function loadmsg() {
$.ajax({
type: "GET",
dataType: "json",
url: "/getshop.php",
data: {type: "wxpay", trade_no: "<?php echo $order['trade_no']?>"},
success: function (data) {
if (data.code == 1) {
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.1,time: 15000});
setTimeout(window.location.href=data.backurl, 1000);
}else{
setTimeout("loadmsg()", 2000);
}
},
error: function () {
setTimeout("loadmsg()", 2000);
}
});
}
window.onload = loadmsg();
</script>
</div>
</div>
</body>
</html>
+75
View File
@@ -0,0 +1,75 @@
<?php
if (!defined('IN_CRONLITE')) exit();
$html = '<form id="dopay" action="'.$siteurl.'submit.php" method="post">';
foreach ($query_arr as $k=>$v) {
$html.= '<input type="hidden" name="'.$k.'" value="'.$v.'"/>';
}
$html .= '<input type="submit" value="Loading"></form>';
?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>正在进行支付安全验证,请稍候...</title>
<style type="text/css">
body{margin:0;padding:0}
#waiting{position:absolute;left:50%;top:50%;height:35px;margin:-35px 0 0 -160px;padding:20px;font:16px/30px "Helvetica Neue",Helvetica,Arial,sans-serif;background:#f9fafc url(/assets/img/loading.gif) no-repeat 20px 20px;text-indent:40px;border:1px solid #c5d0dc}
</style>
</head>
<body>
<p id="waiting">正在进行支付安全验证,请稍候...</p>
<?php echo $html?>
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
<script src="https://static.geetest.com/v4/gt4.js"></script>
<script>
window.appendChildOrg = Element.prototype.appendChild;
Element.prototype.appendChild = function() {
if(arguments[0].tagName == 'SCRIPT'){
arguments[0].setAttribute('referrerpolicy', 'no-referrer');
}
return window.appendChildOrg.apply(this, arguments);
};
initGeetest4({
captchaId: "99b142aaece96330d0f3ffb565ffb3ef",
product: 'bind',
protocol: 'https://',
riskType: 'ai',
},function (captcha) {
captcha.onReady(function(){
captcha.showCaptcha();
}).onSuccess(function(){
var result = captcha.getValidate();
result.pid = '<?php echo $query_arr['pid']?>';
result.trade_no = '<?php echo $query_arr['out_trade_no']?>';
$.ajax({
url: 'getshop.php?act=captcha_verify',
type: 'post',
dataType: 'json',
data: result,
cache: false,
success: function (data) {
if(data.code == 0){
var elem = document.getElementById("dopay");
var input = document.createElement("input");
input.type="hidden";
input.name="__defend";
input.value=data.key;
elem.appendChild(input);
elem.submit();
}else{
alert(data.msg);
}
},
error: function () {
alert('服务器错误');
}
});
}).onError(function(){
alert('验证码加载失败,请刷新页面重试');
})
});
</script>
</body>
</html>
+39
View File
@@ -0,0 +1,39 @@
<?php
if (!defined('IN_CRONLITE')) exit();
$x = new \lib\hieroglyphy();
$key_enc = $x->hieroglyphyString($key);
$html = '<form id="dopay" action="'.$siteurl.'submit.php" method="post">';
foreach ($query_arr as $k=>$v) {
$html.= '<input type="hidden" name="'.$k.'" value="'.$v.'"/>';
}
$html .= '<input type="submit" value="Loading"></form>';
?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>正在进行支付安全验证,请稍候...</title>
<style type="text/css">
body{margin:0;padding:0}
#waiting{position:absolute;left:50%;top:50%;height:35px;margin:-35px 0 0 -160px;padding:20px;font:16px/30px "Helvetica Neue",Helvetica,Arial,sans-serif;background:#f9fafc url(/assets/img/loading.gif) no-repeat 20px 20px;text-indent:40px;border:1px solid #c5d0dc}
</style>
</head>
<body>
<p id="waiting">正在进行支付安全验证,请稍候...</p>
<?php echo $html?>
<script>
var key = <?php echo $key_enc;?>;
window.onload=function(){
var elem = document.getElementById("dopay");
var input=document.createElement("input");
input.type="hidden";
input.name="__defend";
input.value=key;
elem.appendChild(input);
elem.submit();
}
</script>
</body>
</html>
+90
View File
@@ -0,0 +1,90 @@
<?php
if (!defined('IN_CRONLITE')) exit();
$html = '<form id="dopay" action="'.$siteurl.'submit.php" method="post">';
foreach ($query_arr as $k=>$v) {
$html.= '<input type="hidden" name="'.$k.'" value="'.$v.'"/>';
}
$html .= '<input type="submit" value="Loading" style="display:none"></form>';
?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>支付环境安全验证</title>
<style type="text/css">
body{font-family:"微软雅黑";height:auto!important;height:555px;min-height:555px;margin:0}
.container{margin:0 auto;margin-top:100px;background:#fff;text-align:center}
.header>p{margin:0;margin-top:24px;font-size:18px;line-height:1.7;color:#5d5d5d}
strong{color:#3190e6}
@media screen and (max-width:767px){.container{margin-top:10px}
.header>p{margin:0;padding:20px;font-size:20px;line-height:1.7;color:#5d5d5d}
}
@media screen and (max-width:320px){.container{margin-top:0}
.header>p{margin:0;padding:20px;font-size:18px;line-height:1.7;color:#5d5d5d}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<p>
很抱歉,当前支付人数过多,请完成<strong>“滑动验证”</strong>后继续支付
</p>
</div>
</div>
<?php echo $html?>
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
<script src="https://static.geetest.com/v4/gt4.js"></script>
<script>
window.appendChildOrg = Element.prototype.appendChild;
Element.prototype.appendChild = function() {
if(arguments[0].tagName == 'SCRIPT'){
arguments[0].setAttribute('referrerpolicy', 'no-referrer');
}
return window.appendChildOrg.apply(this, arguments);
};
initGeetest4({
captchaId: "54088bb07d2df3c46b79f80300b0abbe",
product: 'bind',
protocol: 'https://',
riskType: 'slide',
hideSuccess: true
},function (captcha) {
captcha.onReady(function(){
captcha.showCaptcha();
}).onSuccess(function(){
var result = captcha.getValidate();
result.pid = '<?php echo $query_arr['pid']?>';
result.trade_no = '<?php echo $query_arr['out_trade_no']?>';
$.ajax({
url: 'getshop.php?act=captcha_verify',
type: 'post',
dataType: 'json',
data: result,
cache: false,
success: function (data) {
if(data.code == 0){
var elem = document.getElementById("dopay");
var input = document.createElement("input");
input.type="hidden";
input.name="__defend";
input.value=data.key;
elem.appendChild(input);
elem.submit();
}else{
alert(data.msg);
}
},
error: function () {
alert('服务器错误');
}
});
}).onError(function(){
alert('验证码加载失败,请刷新页面重试');
})
});
</script>
</body>
</html>
+72
View File
@@ -0,0 +1,72 @@
<?php
if(!defined('IN_PLUGIN'))exit();
$useragent = strtolower($_SERVER['HTTP_USER_AGENT']);
if(strpos($useragent, 'iphone')!==false || strpos($useragent, 'ipod')!==false){
$background_img = '/assets/img/ios.png';
}else{
$background_img = '/assets/img/android.png';
}
?><!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>支付提示</title>
<meta name="apple-mobile-web-app-capable" content="yes"/>
<meta name="apple-mobile-web-app-status-bar-style" content="black"/>
<meta name="format-detection" content="telephone=no"/>
<meta name="format-detection" content="email=no"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0"/>
<style>
*,:after,:before{-webkit-tap-highlight-color:transparent}
blockquote,body,dd,div,dl,dt,fieldset,form,h1,h2,h3,h4,h5,h6,input,legend,li,ol,p,td,textarea,th,ul{margin:0;padding:0}
table{border-collapse:collapse;border-spacing:0}
fieldset,img{border:0}
li{list-style:none}
caption,th{text-align:left}
q:after,q:before{content:""}
input:password{ime-mode:disabled}
:focus{outline:0}
body,html{-webkit-touch-callout:none;touch-callout:none;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;tap-highlight-color:transparent;height:100%;margin:0;padding:0;text-align:center;font-size:15px;font-weight:300;font-family:"Helvetica Neue",Helvetica,Arial,"Lucida Grande",sans-serif}
a{text-decoration:none}
body{background:#F4F4F8}
.weixin-tip{-webkit-box-sizing:border-box;box-sizing:border-box;position:absolute;top:15px;right:20px;width:265px;padding:55px 0 0;text-align:left;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAACICAMAAABQgAwUAAAAMFBMVEUAAADY2NjY2NjY2NjY2NjY2NjY2NjY2NjX19fY2NjY2NjY2NjY2NjY2NjY2NjY2Njr/TvvAAAAD3RSTlMAxy89c9CdTRyG7lvcD7FzqbJAAAACFklEQVR42uWYy4rkMBAErZdlPdzx/3+7LAw0tH0Y2orDsnnyKQlSVaWytoc6xrEpigFoinUAIBnWABAE5woW9o6GPbGwI1jYGSzsgoV9goU9wMLe0bA7FnYCC7uBhV2wsE+wsAdY2AENGyzsBBZ2Q8MuWNgH94pLbgELO6Bhg4VdwcJuaNgTCzuChZ3Bwg5o2GBhV7CwdzTsjoUdwcLOYGEXLOwTLOwBFvaOht2xsBNY2I1f6lhaenvhrfpkAblab+k9b/OD0iuX2F9/x8D+7ZL2pmpbuj+6o3Vg//oWmPU9p65VkXL6+oIJ8S738nwj62Pb1lvHACH+fBs7sG59U3yrVD3rce3GVcp8qGkPAGTprQUYy6xfaE8i82b6S7/pfZnzdYQIHeOXdfYKpHoFcmrvWlM8RW+CDO8JMWoNM/+FeyB4UfMpL48g5qG1Iqc29YI3mqq2knXvEJu2onJoQy9ok4mkQZf/GjqitUvQyqN6SU8NOvOhHq25xNCWj6LFQdLiyKuaZWpxBC2OrFVHxdryElbQsVtBx6KN0qAd4a71yo610uxa2b0s5xg052I5p26d4MCqusZFwzrAnqQhSogSMnkNcr+GUS3kEKWS62NJFlNCToWLZpWMe14RReGqdjz2PfNECbkGbrQ/Nj5q5y7j8/HRTW5UhvHfA7Mdzitji8rfWsgX3gVZ91eO22odKed6LLf9A/sRnc74RV7lAAAAAElFTkSuQmCC) no-repeat right top;background-size:45px 68px}
.weixin-tip-img{padding:110px 0 0}
.weixin-tip-img::after{display:block;margin:15px auto;content:' ';background-size:cover;width:150px;height:150px;background-image:url('<?php echo $background_img?>')}
</style>
</head>
<body>
<div class="J-weixin-tip weixin-tip">
<div class="weixin-tip-content">
请在菜单中选择在浏览器中打开,<br/>
以完成支付
</div>
</div>
<div class="J-weixin-tip-img weixin-tip-img"></div>
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
<script src="<?php echo $cdnpublic?>layer/3.1.1/layer.min.js"></script>
<script>
function loadmsg() {
$.ajax({
type: "GET",
dataType: "json",
url: "/getshop.php",
data: {type: "alipay", trade_no: "<?php echo $order['trade_no']?>"},
success: function (data) {
if (data.code == 1) {
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.1,time: 15000});
setTimeout(window.location.href=data.backurl, 1000);
}else{
setTimeout("loadmsg()", 2000);
}
},
error: function () {
setTimeout("loadmsg()", 2000);
}
});
}
window.onload = function(){
setTimeout("loadmsg()", 5000);
}
</script>
</body>
</html>
+109
View File
@@ -0,0 +1,109 @@
<?php
// 微信H5支付页面
if (!defined('IN_PLUGIN'))
exit();
?>
<html lang="zh-cn">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<meta name="renderer" content="webkit" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>微信支付手机版</title>
<link href="<?php echo $cdnpublic ?>twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet" />
<link href="/assets/pay/css/mobile-style.css" rel="stylesheet" />
</head>
<body>
<div class="main">
<div class="bg-weixin"></div>
<div class="payment-logo">
<img src="/assets/pay/icon/wxpay-white.svg" alt="logo">
<span class="logo-tile">微信支付</span>
</div>
<div class="payment-content">
<div class="content-info">
<h1>¥<?php echo $order['realmoney'] ?></h1>
<ul class="nk-activity">
<li class="nk-activity-item">
<span>商品名称:<?php echo $order['name'] ?></span>
</li>
<li class="nk-activity-item">
<span>商户订单号:<?php echo $order['trade_no'] ?></span>
</li>
<li class="nk-activity-item">
<span>创建时间:<?php echo $order['addtime'] ?></span>
</li>
</ul>
</div>
<div class="content-footer">
<a href="javascript:;" id="openUrl" class="btn btn-success btn-block btn-lg">跳转到微信支付</a>
<a href="javascript:checkresult()" onclick="" class="btn btn-info btn-block btn-lg">检测支付状态</a>
</div>
</div>
</div>
<script src="<?php echo $cdnpublic ?>jquery/1.12.4/jquery.min.js"></script>
<script src="<?php echo $cdnpublic ?>layer/3.1.1/layer.min.js"></script>
<script>
var url_scheme = '<?php echo $code_url ?>';
function loadmsg() {
$.ajax({
type: "GET",
dataType: "json",
url: "/getshop.php",
data: { type: "wxpay", trade_no: "<?php echo $order['trade_no'] ?>" },
success: function (data) {
if (data.code == 1) {
layer.msg('支付成功,正在跳转中...', { icon: 16, shade: 0.01, time: 15000 });
setTimeout(window.location.href = data.backurl, 1000);
} else {
setTimeout("loadmsg()", 2000);
}
},
error: function () {
setTimeout("loadmsg()", 2000);
}
});
}
function checkresult() {
$.ajax({
type: "GET",
dataType: "json",
url: "/getshop.php",
data: { type: "wxpay", trade_no: "<?php echo $order['trade_no'] ?>" },
success: function (data) {
if (data.code == 1) {
layer.msg('支付成功,正在跳转中...', { icon: 16, shade: 0.01, time: 15000 });
setTimeout(window.location.href = data.backurl, 1000);
} else {
layer.msg('您还未完成付款,请继续付款', { shade: 0, time: 1500 });
}
},
error: function () {
layer.msg('服务器错误');
}
});
}
window.onload = function () {
window.onpopstate = function (e) {
if (e.state == 'forward' || confirm('是否取消支付并返回?')) {
window.history.back();
} else {
e.preventDefault();
window.history.pushState('forward', null, '');
}
};
window.history.pushState('forward', null, '');
document.getElementById("openUrl").href = url_scheme;
if (!url_scheme.startsWith('http://') && !url_scheme.startsWith('https://') && navigator.userAgent.indexOf('EdgA/') == -1) {
window.location.href = url_scheme;
}
setTimeout("loadmsg()", 3000);
}
</script>
</body>
</html>
+102
View File
@@ -0,0 +1,102 @@
<?php
// 微信公众号支付页面
if(!defined('IN_PLUGIN'))exit();
?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<title>微信支付手机版</title>
<style>
body{
margin: 0px !important;
}
</style>
</head>
<body style="background-color:#f6f6f6">
<div style="display: flex;justify-content: center; padding-top: 20px;border-radius: 15px;height: 100px;text-align: center;align-items: center;">
<span style="font-size: 15px;font-weight:800;color:#020202;"><?php echo $order['name']?><br>
<div style="display: flex;justify-content: center;">
<strong style="font-size: 22px;color: #000000;padding-top: 6px;margin-right: 3px;">¥</strong>
<strong style="font-size: 40px;color: #000000;"><?php echo $order['realmoney']?></strong>
</div>
</div>
</div>
<div style="background: #fff;padding: 16px;border-top: 1px solid #d8d8d8;border-bottom: 1px solid #d8d8d8;">
<div style="display: flex;">
<span style="font-weight: 400;color: #a1a1a1;width: 40px;">商家</span>
<span style="flex:1;text-align: right;color: black;font-weight: 600;font-size: 14px;">微信支付平台商户</span>
</div>
</div>
<div style="margin-top: 1px;border-radius: 1px;">
<div style="display: flex; justify-content: center; padding-top: 20px;">
<a class="immediate_pay" style="width:100%;max-width:600px;border-radius: 10px;margin: 0 4px;background: #05c160;padding: 12px 0px;text-align:center;color: #fff;" onclick="callpay()"><font size="4">立即支付</font></a>
</div>
<div style="position: fixed;width: 100%;text-align: center;color: #a1a1a1;bottom: 17px;font-size: 12px;">
支付安全由中国人民财产保险股份有限公司承保
</div>
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
<script src="<?php echo $cdnpublic?>layer/3.1.1/layer.min.js"></script>
<script>
document.body.addEventListener('touchmove', function (event) {
event.preventDefault();
},{ passive: false });
//调用微信JS api 支付
function jsApiCall()
{
WeixinJSBridge.invoke(
'getBrandWCPayRequest',
<?php echo $jsApiParameters; ?>,
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ) {
loadmsg();
}
//WeixinJSBridge.log(res.err_msg);
//alert(res.err_code+res.err_desc+res.err_msg);
}
);
}
function callpay()
{
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', jsApiCall);
document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
}
}else{
jsApiCall();
}
}
function loadmsg() {
$.ajax({
type: "GET",
dataType: "json",
url: "/getshop.php",
data: {type: "wxpay", trade_no: "<?php echo TRADE_NO?>"},
success: function (data) {
if (data.code == 1) {
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.01,time: 15000});
window.location.href=<?php echo $redirect_url?>;
}else{
setTimeout("loadmsg()", 2000);
}
},
error: function () {
setTimeout("loadmsg()", 2000);
}
});
}
window.onload = callpay();
</script>
</div>
</div>
</body>
</html>
+137
View File
@@ -0,0 +1,137 @@
<?php
// 微信扫码支付页面
if(!defined('IN_PLUGIN'))exit();
?>
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Language" content="zh-cn">
<meta name="renderer" content="webkit">
<title>微信扫码支付</title>
<link href="/assets/css/wechat_pay.css?v=2" rel="stylesheet" media="screen">
</head>
<body>
<div class="body">
<h1 class="mod-title">
<span class="ico-wechat"></span><span class="text">微信扫码支付</span>
</h1>
<div class="mod-ct">
<div class="order">
</div>
<div class="mobile-tip" style="display: none;">提示:二维码会风控,请复制下方链接支付</div>
<div class="amount">¥<?php echo $order['realmoney']?></div>
<div class="qr-image" id="qrcode">
</div>
<div class="mobile-btn" style="display: none;">
<div class="mobile-tip">操作流程:复制链接→打开微信搜索自己微信名→打开聊天对话框→粘贴链接→发送→点击发送出来的蓝色链接→进入付款页面→完成付款</div>
<a class="btn-copy-link" id="copy-btn" data-clipboard-text="<?php echo $code_url?>">点我复制链接</a>
</div>
<div class="detail" id="orderDetail">
<dl class="detail-ct" style="display: none;">
<dt>商家</dt>
<dd id="storeName"><?php echo $sitename?></dd>
<dt>购买物品</dt>
<dd id="productName"><?php echo $order['name']?></dd>
<dt>商户订单号</dt>
<dd id="billId"><?php echo $order['trade_no']?></dd>
<dt>创建时间</dt>
<dd id="createTime"><?php echo $order['addtime']?></dd>
</dl>
<a href="javascript:void(0)" class="arrow"><i class="ico-arrow"></i></a>
</div>
<div class="tip">
<span class="dec dec-left"></span>
<span class="dec dec-right"></span>
<div class="ico-scan"></div>
<div class="tip-text">
<p>请使用微信扫一扫</p>
<p>扫描二维码完成支付</p>
</div>
</div>
<div class="tip-text">
</div>
</div>
<div class="foot">
<div class="inner">
<p>手机用户可保存上方二维码到手机中</p>
<p>在微信扫一扫中选择“相册”即可</p>
</div>
</div>
</div>
<script src="<?php echo $cdnpublic?>jquery/1.12.4/jquery.min.js"></script>
<script src="<?php echo $cdnpublic?>layer/3.1.1/layer.min.js"></script>
<script src="<?php echo $cdnpublic?>jquery.qrcode/1.0/jquery.qrcode.min.js"></script>
<script src="<?php echo $cdnpublic?>clipboard.js/1.7.1/clipboard.min.js"></script>
<script>
var clipboard = new Clipboard('#copy-btn');
clipboard.on('success', function(e) {
layer.msg('复制成功,请到微信里面粘贴');
});
clipboard.on('error', function(e) {
layer.msg('复制失败');
});
var code_url = '<?php echo $code_url?>';
var code_type = code_url.indexOf('data:image/')>-1?1:0;
if(code_type == 0){
$('#qrcode').qrcode({
text: code_url,
width: 230,
height: 230,
foreground: "#000000",
background: "#ffffff",
typeNumber: -1
});
}else{
$('#qrcode').html('<img src="'+code_url+'"/>');
}
// 订单详情
$('#orderDetail .arrow').click(function (event) {
if ($('#orderDetail').hasClass('detail-open')) {
$('#orderDetail .detail-ct').slideUp(500, function () {
$('#orderDetail').removeClass('detail-open');
});
} else {
$('#orderDetail .detail-ct').slideDown(500, function () {
$('#orderDetail').addClass('detail-open');
});
}
});
function loadmsg() {
$.ajax({
type: "GET",
dataType: "json",
url: "/getshop.php",
data: {type: "wxpay", trade_no: "<?php echo $order['trade_no']?>"},
success: function (data) {
if (data.code == 1) {
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.1,time: 15000});
window.location.href=data.backurl;
}else{
setTimeout("loadmsg()", 2000);
}
},
error: function () {
setTimeout("loadmsg()", 2000);
}
});
}
var isMobile = function (){
var ua = navigator.userAgent;
var ipad = ua.match(/(iPad).*OS\s([\d_]+)/),
isIphone =!ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/),
isAndroid = ua.match(/(Android)\s+([\d.]+)/);
return isIphone || isAndroid;
}
window.onload = function(){
if(isMobile()){
$('.mobile-btn').show();
$('.mobile-tip').show();
}
setTimeout("loadmsg()", 2000);
}
</script>
</body>
</html>
+145
View File
@@ -0,0 +1,145 @@
<?php
// 微信手机扫码支付页面
if (!defined('IN_PLUGIN'))
exit();
?>
<html lang="zh-cn">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<meta name="renderer" content="webkit" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>微信支付手机版</title>
<link href="<?php echo $cdnpublic ?>twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet" />
<link href="/assets/pay/css/mobile-style.css?v=7" rel="stylesheet" />
</head>
<body>
<div class="main">
<div class="bg-weixin"></div>
<div class="payment-logo payment-logo-wxwap">
<img src="/assets/pay/icon/wxpay-white.svg" alt="logo">
<span class="logo-tile">微信支付</span>
</div>
<div class="payment-content">
<h1 style="margin-top: 8px;margin-bottom: 16px;">¥<?php echo $order['realmoney']; ?></h1>
<div class="scan-the-code">
<ul class="nav nav-group" role="tablist">
<li class="active">
<a href="JavaScript:;"><i class="icon-qrcode"></i>扫码支付</a>
</li>
<li>
<a href="JavaScript:downloadCanvas();"><i class="icon-download"></i>保存二维码</a>
</li>
</ul>
<div class="list-group">
<div class="list-group-item text-center">
<h5 class="qr-title">请使用微信APP扫描二维码支付</h5>
<div class="qr-image" id="qrcode"></div>
<div class="operate">
<a href="weixin://" class="btn btn-default">
<span class="glyphicon glyphicon-new-window" aria-hidden="true"></span>
<span>打开微信APP</span>
</a>
<a href="javascript:checkresult()" class="btn btn-default">
<span class="glyphicon glyphicon-repeat" aria-hidden="true"></span>
<span>检测支付状态</span>
</a>
</div>
</div>
<div class="list-group-item">
<span>二维码链接:<a href="<?php echo $code_url ?>"><?php echo $code_url ?></a></span>
<span><button id="copy-btn" data-clipboard-text="<?php echo $code_url ?>" class="btn btn-info btn-sm">复制</button></span>
</div>
<div class="list-group-item">
<small>提示:你可将以上二维码链接发到自己微信的聊天框(在微信顶部搜索框可以搜到自己的微信),点击即可进入支付!</small>
</div>
</div>
</div>
</div>
</div>
<script src="<?php echo $cdnpublic ?>jquery/1.12.4/jquery.min.js"></script>
<script src="<?php echo $cdnpublic ?>layer/3.1.1/layer.min.js"></script>
<script src="<?php echo $cdnpublic ?>jquery.qrcode/1.0/jquery.qrcode.min.js"></script>
<script src="<?php echo $cdnpublic ?>clipboard.js/1.7.1/clipboard.min.js"></script>
<script>
var clipboard = new Clipboard('#copy-btn');
clipboard.on('success', function (e) {
layer.msg('复制成功,请到微信里面粘贴');
});
clipboard.on('error', function (e) {
layer.msg('复制失败,请长按链接后手动复制');
});
$('#qrcode').qrcode({
text: "<?php echo $code_url ?>",
width: 230,
height: 230,
foreground: "#000000",
background: "#ffffff",
typeNumber: -1
});
function downloadCanvas() {
var canvas = document.getElementsByTagName('canvas')[0];
var url = canvas.toDataURL('image/png');
var a = document.createElement('a');
var event = new MouseEvent('click');
a.download = '微信支付二维码.png';
a.href = url;
a.dispatchEvent(event);
};
function loadmsg() {
$.ajax({
type: "GET",
dataType: "json",
url: "/getshop.php",
data: {type: "wxpay", trade_no: "<?php echo $order['trade_no'] ?>"},
success: function (data) {
if (data.code == 1) {
layer.msg('支付成功,正在跳转中...', {icon: 16,shade: 0.1,time: 15000});
setTimeout(window.location.href=data.backurl, 1000);
}else{
setTimeout("loadmsg()", 2000);
}
},
error: function () {
setTimeout("loadmsg()", 2000);
}
});
}
function checkresult() {
$.ajax({
type: "GET",
dataType: "json",
url: "/getshop.php",
data: { type: "wxpay", trade_no: "<?php echo $order['trade_no'] ?>" },
success: function (data) {
if (data.code == 1) {
layer.msg('支付成功,正在跳转中...', { icon: 16, shade: 0.1, time: 15000 });
setTimeout(window.location.href = data.backurl, 1000);
} else {
layer.msg('您还未完成付款,请继续付款', { shade: 0, time: 1500 });
}
},
error: function () {
layer.msg('服务器错误');
}
});
}
window.onload = function () {
window.onpopstate = function (e) {
if (e.state == 'forward' || confirm('是否取消支付并返回?')) {
window.history.back();
} else {
e.preventDefault();
window.history.pushState('forward', null, '');
}
};
window.history.pushState('forward', null, '');
setTimeout("loadmsg()", 3000);
}
</script>
</body>
</html>
@@ -0,0 +1,96 @@
<?php
/*
* Copyright 2009 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Zxing;
use Zxing\Common\BitArray;
use Zxing\Common\BitMatrix;
/**
* This class hierarchy provides a set of methods to convert luminance data to 1 bit data.
* It allows the algorithm to vary polymorphically, for example allowing a very expensive
* thresholding technique for servers and a fast one for mobile. It also permits the implementation
* to vary, e.g. a JNI version for Android and a Java fallback version for other platforms.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
abstract class Binarizer
{
private $source;
protected function __construct($source)
{
$this->source = $source;
}
/**
* @return LuminanceSource
*/
public final function getLuminanceSource()
{
return $this->source;
}
/**
* Converts one row of luminance data to 1 bit data. May actually do the conversion, or return
* cached data. Callers should assume this method is expensive and call it as seldom as possible.
* This method is intended for decoding 1D barcodes and may choose to apply sharpening.
* For callers which only examine one row of pixels at a time, the same BitArray should be reused
* and passed in with each call for performance. However it is legal to keep more than one row
* at a time if needed.
*
* @param y The row to fetch, which must be in [0, bitmap height)
* @param row An optional preallocated array. If null or too small, it will be ignored.
* If used, the Binarizer will call BitArray.clear(). Always use the returned object.
*
* @return array The array of bits for this row (true means black).
* @throws NotFoundException if row can't be binarized
*/
public abstract function getBlackRow($y, $row);
/**
* Converts a 2D array of luminance data to 1 bit data. As above, assume this method is expensive
* and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or
* may not apply sharpening. Therefore, a row from this matrix may not be identical to one
* fetched using getBlackRow(), so don't mix and match between them.
*
* @return BitMatrix The 2D array of bits for the image (true means black).
* @throws NotFoundException if image can't be binarized to make a matrix
*/
public abstract function getBlackMatrix();
/**
* Creates a new object with the same type as this Binarizer implementation, but with pristine
* state. This is needed because Binarizer implementations may be stateful, e.g. keeping a cache
* of 1 bit data. See Effective Java for why we can't use Java's clone() method.
*
* @param source The LuminanceSource this Binarizer will operate on.
*
* @return Binarizer A new concrete Binarizer implementation object.
*/
public abstract function createBinarizer($source);
public final function getWidth()
{
return $this->source->getWidth();
}
public final function getHeight()
{
return $this->source->getHeight();
}
}
@@ -0,0 +1,166 @@
<?php
/*
* Copyright 2009 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Zxing;
use Zxing\Common\BitMatrix;
/**
* This class is the core bitmap class used by ZXing to represent 1 bit data. Reader objects
* accept a BinaryBitmap and attempt to decode it.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
final class BinaryBitmap
{
private $binarizer;
private $matrix;
public function __construct(Binarizer $binarizer)
{
if ($binarizer === null) {
throw new \InvalidArgumentException("Binarizer must be non-null.");
}
$this->binarizer = $binarizer;
}
/**
* @return int The width of the bitmap.
*/
public function getWidth()
{
return $this->binarizer->getWidth();
}
/**
* @return int The height of the bitmap.
*/
public function getHeight()
{
return $this->binarizer->getHeight();
}
/**
* Converts one row of luminance data to 1 bit data. May actually do the conversion, or return
* cached data. Callers should assume this method is expensive and call it as seldom as possible.
* This method is intended for decoding 1D barcodes and may choose to apply sharpening.
*
* @param y The row to fetch, which must be in [0, bitmap height)
* @param row An optional preallocated array. If null or too small, it will be ignored.
* If used, the Binarizer will call BitArray.clear(). Always use the returned object.
*
* @return array The array of bits for this row (true means black).
* @throws NotFoundException if row can't be binarized
*/
public function getBlackRow($y, $row)
{
return $this->binarizer->getBlackRow($y, $row);
}
/**
* @return bool Whether this bitmap can be cropped.
*/
public function isCropSupported()
{
return $this->binarizer->getLuminanceSource()->isCropSupported();
}
/**
* Returns a new object with cropped image data. Implementations may keep a reference to the
* original data rather than a copy. Only callable if isCropSupported() is true.
*
* @param left The left coordinate, which must be in [0,getWidth())
* @param top The top coordinate, which must be in [0,getHeight())
* @param width The width of the rectangle to crop.
* @param height The height of the rectangle to crop.
*
* @return BinaryBitmap A cropped version of this object.
*/
public function crop($left, $top, $width, $height)
{
$newSource = $this->binarizer->getLuminanceSource()->crop($left, $top, $width, $height);
return new BinaryBitmap($this->binarizer->createBinarizer($newSource));
}
/**
* @return Whether this bitmap supports counter-clockwise rotation.
*/
public function isRotateSupported()
{
return $this->binarizer->getLuminanceSource()->isRotateSupported();
}
/**
* Returns a new object with rotated image data by 90 degrees counterclockwise.
* Only callable if {@link #isRotateSupported()} is true.
*
* @return BinaryBitmap A rotated version of this object.
*/
public function rotateCounterClockwise()
{
$newSource = $this->binarizer->getLuminanceSource()->rotateCounterClockwise();
return new BinaryBitmap($this->binarizer->createBinarizer($newSource));
}
/**
* Returns a new object with rotated image data by 45 degrees counterclockwise.
* Only callable if {@link #isRotateSupported()} is true.
*
* @return BinaryBitmap A rotated version of this object.
*/
public function rotateCounterClockwise45()
{
$newSource = $this->binarizer->getLuminanceSource()->rotateCounterClockwise45();
return new BinaryBitmap($this->binarizer->createBinarizer($newSource));
}
public function toString()
{
try {
return $this->getBlackMatrix()->toString();
} catch (NotFoundException $e) {
}
return '';
}
/**
* Converts a 2D array of luminance data to 1 bit. As above, assume this method is expensive
* and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or
* may not apply sharpening. Therefore, a row from this matrix may not be identical to one
* fetched using getBlackRow(), so don't mix and match between them.
*
* @return BitMatrix The 2D array of bits for the image (true means black).
* @throws NotFoundException if image can't be binarized to make a matrix
*/
public function getBlackMatrix()
{
// The matrix is created on demand the first time it is requested, then cached. There are two
// reasons for this:
// 1. This work will never be done if the caller only installs 1D Reader objects, or if a
// 1D Reader finds a barcode before the 2D Readers run.
// 2. This work will only be done once even if the caller installs multiple 2D Readers.
if ($this->matrix === null) {
$this->matrix = $this->binarizer->getBlackMatrix();
}
return $this->matrix;
}
}
@@ -0,0 +1,42 @@
<?php
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Zxing;
/**
* Thrown when a barcode was successfully detected and decoded, but
* was not returned because its checksum feature failed.
*
* @author Sean Owen
*/
final class ChecksumException extends ReaderException
{
private static $instance;
public static function getChecksumInstance($cause = null)
{
if (self::$isStackTrace) {
return new ChecksumException($cause);
} else {
if (!self::$instance) {
self::$instance = new ChecksumException($cause);
}
return self::$instance;
}
}
}
@@ -0,0 +1,104 @@
<?php
namespace Zxing\Common;
use \Zxing\NotFoundException;
use ReflectionClass;
/**
* A general enum implementation until we got SplEnum.
*/
final class AbstractEnum
{
/**
* Default value.
*/
const __default = null;
/**
* Current value.
*
* @var mixed
*/
protected $value;
/**
* Cache of constants.
*
* @var array
*/
protected $constants;
/**
* Whether to handle values strict or not.
*
* @var boolean
*/
protected $strict;
/**
* Creates a new enum.
*
* @param mixed $initialValue
* @param boolean $strict
*/
public function __construct($initialValue = null, $strict = false)
{
$this->strict = $strict;
$this->change($initialValue);
}
/**
* Changes the value of the enum.
*
* @param mixed $value
*
* @return void
*/
public function change($value)
{
if (!in_array($value, $this->getConstList(), $this->strict)) {
throw new \UnexpectedValueException('Value not a const in enum ' . get_class($this));
}
$this->value = $value;
}
/**
* Gets all constants (possible values) as an array.
*
* @param boolean $includeDefault
*
* @return array
*/
public function getConstList($includeDefault = true)
{
if ($this->constants === null) {
$reflection = new ReflectionClass($this);
$this->constants = $reflection->getConstants();
}
if ($includeDefault) {
return $this->constants;
}
$constants = $this->constants;
unset($constants['__default']);
return $constants;
}
/**
* Gets current value.
*
* @return mixed
*/
public function get()
{
return $this->value;
}
/**
* Gets the name of the enum.
*
* @return string
*/
public function __toString()
{
return (string)array_search($this->value, $this->getConstList());
}
}
@@ -0,0 +1,422 @@
<?php
/**
* Created by PhpStorm.
* User: Ashot
* Date: 3/25/15
* Time: 11:51
*/
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Zxing\Common;
/**
* <p>A simple, fast array of bits, represented compactly by an array of ints internally.</p>
*
* @author Sean Owen
*/
final class BitArray
{
private $bits;
private $size;
public function __construct($bits = [], $size = 0)
{
if (!$bits && !$size) {
$this->$size = 0;
$this->bits = [];
} elseif ($bits && !$size) {
$this->size = $bits;
$this->bits = $this->makeArray($bits);
} else {
$this->bits = $bits;
$this->size = $size;
}
}
private static function makeArray($size)
{
return [];
}
public function getSize()
{
return $this->size;
}
public function getSizeInBytes()
{
return ($this->size + 7) / 8;
}
/**
* Sets bit i.
*
* @param i bit to set
*/
public function set($i)
{
$this->bits[(int)($i / 32)] |= 1 << ($i & 0x1F);
$this->bits[(int)($i / 32)] = ($this->bits[(int)($i / 32)]);
}
/**
* Flips bit i.
*
* @param i bit to set
*/
public function flip($i)
{
$this->bits[(int)($i / 32)] ^= 1 << ($i & 0x1F);
$this->bits[(int)($i / 32)] = ($this->bits[(int)($i / 32)]);
}
/**
* @param from first bit to check
*
* @return index of first bit that is set, starting from the given index, or size if none are set
* at or beyond this given index
* @see #getNextUnset(int)
*/
public function getNextSet($from)
{
if ($from >= $this->size) {
return $this->size;
}
$bitsOffset = (int)($from / 32);
$currentBits = (int)$this->bits[$bitsOffset];
// mask off lesser bits first
$currentBits &= ~((1 << ($from & 0x1F)) - 1);
while ($currentBits == 0) {
if (++$bitsOffset == count($this->bits)) {
return $this->size;
}
$currentBits = $this->bits[$bitsOffset];
}
$result = ($bitsOffset * 32) + numberOfTrailingZeros($currentBits); //numberOfTrailingZeros
return $result > $this->size ? $this->size : $result;
}
/**
* @param from index to start looking for unset bit
*
* @return index of next unset bit, or {@code size} if none are unset until the end
* @see #getNextSet(int)
*/
public function getNextUnset($from)
{
if ($from >= $this->size) {
return $this->size;
}
$bitsOffset = (int)($from / 32);
$currentBits = ~$this->bits[$bitsOffset];
// mask off lesser bits first
$currentBits &= ~((1 << ($from & 0x1F)) - 1);
while ($currentBits == 0) {
if (++$bitsOffset == count($this->bits)) {
return $this->size;
}
$currentBits = (~$this->bits[$bitsOffset]);
}
$result = ($bitsOffset * 32) + numberOfTrailingZeros($currentBits);
return $result > $this->size ? $this->size : $result;
}
/**
* Sets a block of 32 bits, starting at bit i.
*
* @param i first bit to set
* @param newBits the new value of the next 32 bits. Note again that the least-significant bit
* corresponds to bit i, the next-least-significant to i+1, and so on.
*/
public function setBulk($i, $newBits)
{
$this->bits[(int)($i / 32)] = $newBits;
}
/**
* Sets a range of bits.
*
* @param start start of range, inclusive.
* @param end end of range, exclusive
*/
public function setRange($start, $end)
{
if ($end < $start) {
throw new \InvalidArgumentException();
}
if ($end == $start) {
return;
}
$end--; // will be easier to treat this as the last actually set bit -- inclusive
$firstInt = (int)($start / 32);
$lastInt = (int)($end / 32);
for ($i = $firstInt; $i <= $lastInt; $i++) {
$firstBit = $i > $firstInt ? 0 : $start & 0x1F;
$lastBit = $i < $lastInt ? 31 : $end & 0x1F;
$mask = 0;
if ($firstBit == 0 && $lastBit == 31) {
$mask = -1;
} else {
$mask = 0;
for ($j = $firstBit; $j <= $lastBit; $j++) {
$mask |= 1 << $j;
}
}
$this->bits[$i] = ($this->bits[$i] | $mask);
}
}
/**
* Clears all bits (sets to false).
*/
public function clear()
{
$max = count($this->bits);
for ($i = 0; $i < $max; $i++) {
$this->bits[$i] = 0;
}
}
/**
* Efficient method to check if a range of bits is set, or not set.
*
* @param start start of range, inclusive.
* @param end end of range, exclusive
* @param value if true, checks that bits in range are set, otherwise checks that they are not set
*
* @return true iff all bits are set or not set in range, according to value argument
* @throws InvalidArgumentException if end is less than or equal to start
*/
public function isRange($start, $end, $value)
{
if ($end < $start) {
throw new \InvalidArgumentException();
}
if ($end == $start) {
return true; // empty range matches
}
$end--; // will be easier to treat this as the last actually set bit -- inclusive
$firstInt = (int)($start / 32);
$lastInt = (int)($end / 32);
for ($i = $firstInt; $i <= $lastInt; $i++) {
$firstBit = $i > $firstInt ? 0 : $start & 0x1F;
$lastBit = $i < $lastInt ? 31 : $end & 0x1F;
$mask = 0;
if ($firstBit == 0 && $lastBit == 31) {
$mask = -1;
} else {
$mask = 0;
for ($j = $firstBit; $j <= $lastBit; $j++) {
$mask = ($mask | (1 << $j));
}
}
// Return false if we're looking for 1s and the masked bits[i] isn't all 1s (that is,
// equals the mask, or we're looking for 0s and the masked portion is not all 0s
if (($this->bits[$i] & $mask) != ($value ? $mask : 0)) {
return false;
}
}
return true;
}
/**
* Appends the least-significant bits, from value, in order from most-significant to
* least-significant. For example, appending 6 bits from 0x000001E will append the bits
* 0, 1, 1, 1, 1, 0 in that order.
*
* @param value {@code int} containing bits to append
* @param numBits bits from value to append
*/
public function appendBits($value, $numBits)
{
if ($numBits < 0 || $numBits > 32) {
throw new \InvalidArgumentException("Num bits must be between 0 and 32");
}
$this->ensureCapacity($this->size + $numBits);
for ($numBitsLeft = $numBits; $numBitsLeft > 0; $numBitsLeft--) {
$this->appendBit((($value >> ($numBitsLeft - 1)) & 0x01) == 1);
}
}
private function ensureCapacity($size)
{
if ($size > count($this->bits) * 32) {
$newBits = $this->makeArray($size);
$newBits = arraycopy($this->bits, 0, $newBits, 0, count($this->bits));
$this->bits = $newBits;
}
}
public function appendBit($bit)
{
$this->ensureCapacity($this->size + 1);
if ($bit) {
$this->bits[(int)($this->size / 32)] |= 1 << ($this->size & 0x1F);
}
$this->size++;
}
public function appendBitArray($other)
{
$otherSize = $other->size;
$this->ensureCapacity($this->size + $otherSize);
for ($i = 0; $i < $otherSize; $i++) {
$this->appendBit($other->get($i));
}
}
public function _xor($other)
{
if (count($this->bits) !== count($other->bits)) {
throw new \InvalidArgumentException("Sizes don't match");
}
$count = count($this->bits);
for ($i = 0; $i < $count; $i++) {
// The last byte could be incomplete (i.e. not have 8 bits in
// it) but there is no problem since 0 XOR 0 == 0.
$this->bits[$i] ^= $other->bits[$i];
}
}
/**
*
* @param bitOffset first bit to start writing
* @param array array to write into. Bytes are written most-significant byte first. This is the opposite
* of the internal representation, which is exposed by {@link #getBitArray()}
* @param offset position in array to start writing
* @param numBytes how many bytes to write
*/
public function toBytes($bitOffset, &$array, $offset, $numBytes)
{
for ($i = 0; $i < $numBytes; $i++) {
$theByte = 0;
for ($j = 0; $j < 8; $j++) {
if ($this->get($bitOffset)) {
$theByte |= 1 << (7 - $j);
}
$bitOffset++;
}
$array[(int)($offset + $i)] = $theByte;
}
}
/**
* @param $i ; bit to get
*
* @return true iff bit i is set
*/
public function get($i)
{
$key = (int)($i / 32);
return ($this->bits[$key] & (1 << ($i & 0x1F))) != 0;
}
/**
* @return array underlying array of ints. The first element holds the first 32 bits, and the least
* significant bit is bit 0.
*/
public function getBitArray()
{
return $this->bits;
}
/**
* Reverses all bits in the array.
*/
public function reverse()
{
$newBits = [];
// reverse all int's first
$len = (($this->size - 1) / 32);
$oldBitsLen = $len + 1;
for ($i = 0; $i < $oldBitsLen; $i++) {
$x = $this->bits[$i];/*
$x = (($x >> 1) & 0x55555555L) | (($x & 0x55555555L) << 1);
$x = (($x >> 2) & 0x33333333L) | (($x & 0x33333333L) << 2);
$x = (($x >> 4) & 0x0f0f0f0fL) | (($x & 0x0f0f0f0fL) << 4);
$x = (($x >> 8) & 0x00ff00ffL) | (($x & 0x00ff00ffL) << 8);
$x = (($x >> 16) & 0x0000ffffL) | (($x & 0x0000ffffL) << 16);*/
$x = (($x >> 1) & 0x55555555) | (($x & 0x55555555) << 1);
$x = (($x >> 2) & 0x33333333) | (($x & 0x33333333) << 2);
$x = (($x >> 4) & 0x0f0f0f0f) | (($x & 0x0f0f0f0f) << 4);
$x = (($x >> 8) & 0x00ff00ff) | (($x & 0x00ff00ff) << 8);
$x = (($x >> 16) & 0x0000ffff) | (($x & 0x0000ffff) << 16);
$newBits[(int)$len - $i] = (int)$x;
}
// now correct the int's if the bit size isn't a multiple of 32
if ($this->size != $oldBitsLen * 32) {
$leftOffset = $oldBitsLen * 32 - $this->size;
$mask = 1;
for ($i = 0; $i < 31 - $leftOffset; $i++) {
$mask = ($mask << 1) | 1;
}
$currentInt = ($newBits[0] >> $leftOffset) & $mask;
for ($i = 1; $i < $oldBitsLen; $i++) {
$nextInt = $newBits[$i];
$currentInt |= $nextInt << (32 - $leftOffset);
$newBits[(int)($i) - 1] = $currentInt;
$currentInt = ($nextInt >> $leftOffset) & $mask;
}
$newBits[(int)($oldBitsLen) - 1] = $currentInt;
}
// $bits = $newBits;
}
public function equals($o)
{
if (!($o instanceof BitArray)) {
return false;
}
$other = $o;
return $this->size == $other->size && $this->bits === $other->bits;
}
public function hashCode()
{
return 31 * $this->size + hashCode($this->bits);
}
public function toString()
{
$result = '';
for ($i = 0; $i < $this->size; $i++) {
if (($i & 0x07) == 0) {
$result .= ' ';
}
$result .= ($this->get($i) ? 'X' : '.');
}
return (string)$result;
}
public function _clone()
{
return new BitArray($this->bits, $this->size);
}
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,118 @@
<?php
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Zxing\Common;
/**
* <p>This provides an easy abstraction to read bits at a time from a sequence of bytes, where the
* number of bits read is not often a multiple of 8.</p>
*
* <p>This class is thread-safe but not reentrant -- unless the caller modifies the bytes array
* it passed in, in which case all bets are off.</p>
*
* @author Sean Owen
*/
final class BitSource
{
private $bytes;
private $byteOffset = 0;
private $bitOffset = 0;
/**
* @param bytes bytes from which this will read bits. Bits will be read from the first byte first.
* Bits are read within a byte from most-significant to least-significant bit.
*/
public function __construct($bytes)
{
$this->bytes = $bytes;
}
/**
* @return index of next bit in current byte which would be read by the next call to {@link #readBits(int)}.
*/
public function getBitOffset()
{
return $this->bitOffset;
}
/**
* @return index of next byte in input byte array which would be read by the next call to {@link #readBits(int)}.
*/
public function getByteOffset()
{
return $this->byteOffset;
}
/**
* @param numBits number of bits to read
*
* @return int representing the bits read. The bits will appear as the least-significant
* bits of the int
* @throws InvalidArgumentException if numBits isn't in [1,32] or more than is available
*/
public function readBits($numBits)
{
if ($numBits < 1 || $numBits > 32 || $numBits > $this->available()) {
throw new \InvalidArgumentException(strval($numBits));
}
$result = 0;
// First, read remainder from current byte
if ($this->bitOffset > 0) {
$bitsLeft = 8 - $this->bitOffset;
$toRead = $numBits < $bitsLeft ? $numBits : $bitsLeft;
$bitsToNotRead = $bitsLeft - $toRead;
$mask = (0xFF >> (8 - $toRead)) << $bitsToNotRead;
$result = ($this->bytes[$this->byteOffset] & $mask) >> $bitsToNotRead;
$numBits -= $toRead;
$this->bitOffset += $toRead;
if ($this->bitOffset == 8) {
$this->bitOffset = 0;
$this->byteOffset++;
}
}
// Next read whole bytes
if ($numBits > 0) {
while ($numBits >= 8) {
$result = ($result << 8) | ($this->bytes[$this->byteOffset] & 0xFF);
$this->byteOffset++;
$numBits -= 8;
}
// Finally read a partial byte
if ($numBits > 0) {
$bitsToNotRead = 8 - $numBits;
$mask = (0xFF >> $bitsToNotRead) << $bitsToNotRead;
$result = ($result << $numBits) | (($this->bytes[$this->byteOffset] & $mask) >> $bitsToNotRead);
$this->bitOffset += $numBits;
}
}
return $result;
}
/**
* @return number of bits that can be read successfully
*/
public function available()
{
return 8 * (count($this->bytes) - $this->byteOffset) - $this->bitOffset;
}
}
@@ -0,0 +1,158 @@
<?php
namespace Zxing\Common;
/**
* Encapsulates a Character Set ECI, according to "Extended Channel
* Interpretations" 5.3.1.1 of ISO 18004.
*/
final class CharacterSetECI
{
/**#@+
* Character set constants.
*/
const CP437 = 0;
const ISO8859_1 = 1;
const ISO8859_2 = 4;
const ISO8859_3 = 5;
const ISO8859_4 = 6;
const ISO8859_5 = 7;
const ISO8859_6 = 8;
const ISO8859_7 = 9;
const ISO8859_8 = 10;
const ISO8859_9 = 11;
const ISO8859_10 = 12;
const ISO8859_11 = 13;
const ISO8859_12 = 14;
const ISO8859_13 = 15;
const ISO8859_14 = 16;
const ISO8859_15 = 17;
const ISO8859_16 = 18;
const SJIS = 20;
const CP1250 = 21;
const CP1251 = 22;
const CP1252 = 23;
const CP1256 = 24;
const UNICODE_BIG_UNMARKED = 25;
const UTF8 = 26;
const ASCII = 27;
const BIG5 = 28;
const GB18030 = 29;
const EUC_KR = 30;
/**
* Map between character names and their ECI values.
*
* @var array
*/
protected static $nameToEci = [
'ISO-8859-1' => self::ISO8859_1,
'ISO-8859-2' => self::ISO8859_2,
'ISO-8859-3' => self::ISO8859_3,
'ISO-8859-4' => self::ISO8859_4,
'ISO-8859-5' => self::ISO8859_5,
'ISO-8859-6' => self::ISO8859_6,
'ISO-8859-7' => self::ISO8859_7,
'ISO-8859-8' => self::ISO8859_8,
'ISO-8859-9' => self::ISO8859_9,
'ISO-8859-10' => self::ISO8859_10,
'ISO-8859-11' => self::ISO8859_11,
'ISO-8859-12' => self::ISO8859_12,
'ISO-8859-13' => self::ISO8859_13,
'ISO-8859-14' => self::ISO8859_14,
'ISO-8859-15' => self::ISO8859_15,
'ISO-8859-16' => self::ISO8859_16,
'SHIFT-JIS' => self::SJIS,
'WINDOWS-1250' => self::CP1250,
'WINDOWS-1251' => self::CP1251,
'WINDOWS-1252' => self::CP1252,
'WINDOWS-1256' => self::CP1256,
'UTF-16BE' => self::UNICODE_BIG_UNMARKED,
'UTF-8' => self::UTF8,
'ASCII' => self::ASCII,
'GBK' => self::GB18030,
'EUC-KR' => self::EUC_KR,
];
/**#@-*/
/**
* Additional possible values for character sets.
*
* @var array
*/
protected static $additionalValues = [
self::CP437 => 2,
self::ASCII => 170,
];
private static $name = null;
/**
* Gets character set ECI by value.
*
* @param string $name
*
* @return CharacterSetEci|null
*/
public static function getCharacterSetECIByValue($value)
{
if ($value < 0 || $value >= 900) {
throw new \InvalidArgumentException('Value must be between 0 and 900');
}
if (false !== ($key = array_search($value, self::$additionalValues))) {
$value = $key;
}
array_search($value, self::$nameToEci);
try {
self::setName($value);
return new self($value);
} catch (\UnexpectedValueException $e) {
return null;
}
}
private static function setName($value)
{
foreach (self::$nameToEci as $name => $key) {
if ($key == $value) {
self::$name = $name;
return true;
}
}
if (self::$name == null) {
foreach (self::$additionalValues as $name => $key) {
if ($key == $value) {
self::$name = $name;
return true;
}
}
}
}
/**
* Gets character set ECI name.
*
* @return character set ECI name|null
*/
public static function name()
{
return self::$name;
}
/**
* Gets character set ECI by name.
*
* @param string $name
*
* @return CharacterSetEci|null
*/
public static function getCharacterSetECIByName($name)
{
$name = strtoupper($name);
if (isset(self::$nameToEci[$name])) {
return new self(self::$nameToEci[$name]);
}
return null;
}
}
@@ -0,0 +1,122 @@
<?php
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Zxing\Common;
/**
* <p>Encapsulates the result of decoding a matrix of bits. This typically
* applies to 2D barcode formats. For now it contains the raw bytes obtained,
* as well as a String interpretation of those bytes, if applicable.</p>
*
* @author Sean Owen
*/
final class DecoderResult
{
private $rawBytes;
private $text;
private $byteSegments;
private $ecLevel;
private $errorsCorrected;
private $erasures;
private $other;
private $structuredAppendParity;
private $structuredAppendSequenceNumber;
public function __construct(
$rawBytes,
$text,
$byteSegments,
$ecLevel,
$saSequence = -1,
$saParity = -1
) {
$this->rawBytes = $rawBytes;
$this->text = $text;
$this->byteSegments = $byteSegments;
$this->ecLevel = $ecLevel;
$this->structuredAppendParity = $saParity;
$this->structuredAppendSequenceNumber = $saSequence;
}
public function getRawBytes()
{
return $this->rawBytes;
}
public function getText()
{
return $this->text;
}
public function getByteSegments()
{
return $this->byteSegments;
}
public function getECLevel()
{
return $this->ecLevel;
}
public function getErrorsCorrected()
{
return $this->errorsCorrected;
}
public function setErrorsCorrected($errorsCorrected)
{
$this->errorsCorrected = $errorsCorrected;
}
public function getErasures()
{
return $this->erasures;
}
public function setErasures($erasures)
{
$this->erasures = $erasures;
}
public function getOther()
{
return $this->other;
}
public function setOther($other)
{
$this->other = $other;
}
public function hasStructuredAppend()
{
return $this->structuredAppendParity >= 0 && $this->structuredAppendSequenceNumber >= 0;
}
public function getStructuredAppendParity()
{
return $this->structuredAppendParity;
}
public function getStructuredAppendSequenceNumber()
{
return $this->structuredAppendSequenceNumber;
}
}
@@ -0,0 +1,93 @@
<?php
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Zxing\Common;
use Zxing\NotFoundException;
/**
* @author Sean Owen
*/
final class DefaultGridSampler extends GridSampler
{
//@Override
public function sampleGrid(
$image,
$dimensionX,
$dimensionY,
$p1ToX, $p1ToY,
$p2ToX, $p2ToY,
$p3ToX, $p3ToY,
$p4ToX, $p4ToY,
$p1FromX, $p1FromY,
$p2FromX, $p2FromY,
$p3FromX, $p3FromY,
$p4FromX, $p4FromY
) {
$transform = PerspectiveTransform::quadrilateralToQuadrilateral(
$p1ToX, $p1ToY, $p2ToX, $p2ToY, $p3ToX, $p3ToY, $p4ToX, $p4ToY,
$p1FromX, $p1FromY, $p2FromX, $p2FromY, $p3FromX, $p3FromY, $p4FromX, $p4FromY);
return $this->sampleGrid_($image, $dimensionX, $dimensionY, $transform);
}
//@Override
public function sampleGrid_(
$image,
$dimensionX,
$dimensionY,
$transform
) {
if ($dimensionX <= 0 || $dimensionY <= 0) {
throw NotFoundException::getNotFoundInstance();
}
$bits = new BitMatrix($dimensionX, $dimensionY);
$points = fill_array(0, 2 * $dimensionX, 0.0);
for ($y = 0; $y < $dimensionY; $y++) {
$max = count($points);
$iValue = (float)$y + 0.5;
for ($x = 0; $x < $max; $x += 2) {
$points[$x] = (float)($x / 2) + 0.5;
$points[$x + 1] = $iValue;
}
$transform->transformPoints($points);
// Quick check to see if points transformed to something inside the image;
// sufficient to check the endpoints
$this->checkAndNudgePoints($image, $points);
try {
for ($x = 0; $x < $max; $x += 2) {
if ($image->get((int)$points[$x], (int)$points[$x + 1])) {
// Black(-ish) pixel
$bits->set($x / 2, $y);
}
}
} catch (\Exception $aioobe) {//ArrayIndexOutOfBoundsException
// This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting
// transform gets "twisted" such that it maps a straight line of points to a set of points
// whose endpoints are in bounds, but others are not. There is probably some mathematical
// way to detect this about the transformation that I don't know yet.
// This results in an ugly runtime exception despite our clever checks above -- can't have
// that. We could check each point's coordinates but that feels duplicative. We settle for
// catching and wrapping ArrayIndexOutOfBoundsException.
throw NotFoundException::getNotFoundInstance();
}
}
return $bits;
}
}
@@ -0,0 +1,49 @@
<?php
/*
* Copyright 2012 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Zxing\Common\Detector;
final class MathUtils
{
private function __construct()
{
}
/**
* Ends up being a bit faster than {@link Math#round(float)}. This merely rounds its
* argument to the nearest int, where x.5 rounds up to x+1. Semantics of this shortcut
* differ slightly from {@link Math#round(float)} in that half rounds down for negative
* values. -2.5 rounds to -3, not -2. For purposes here it makes no difference.
*
* @param float $d real value to round
*
* @return int $nearest {@code int}
*/
public static function round($d)
{
return (int)($d + ($d < 0.0 ? -0.5 : 0.5));
}
public static function distance($aX, $aY, $bX, $bY)
{
$xDiff = $aX - $bX;
$yDiff = $aY - $bY;
return (float)sqrt($xDiff * $xDiff + $yDiff * $yDiff);
}
}
@@ -0,0 +1,234 @@
<?php
/**
* Created by PhpStorm.
* User: Ashot
* Date: 3/24/15
* Time: 21:23
*/
namespace Zxing\Common\Detector;
use Zxing\BinaryBitmap;
use \Zxing\NotFoundException;
use \Zxing\ResultPoint;
/*
*
*
import com.google.zxing.NotFoundException;
import com.google.zxing.ResultPoint;
import com.google.zxing.common.BitMatrix;
*/
//require_once('./lib/NotFoundException.php');
//require_once('./lib/ResultPoint.php');
//require_once('./lib/common/BitMatrix.php');
/**
* <p>A somewhat generic detector that looks for a barcode-like rectangular region within an image.
* It looks within a mostly white region of an image for a region of black and white, but mostly
* black. It returns the four corners of the region, as best it can determine.</p>
*
* @author Sean Owen
* @port Ashot Khanamiryan
*/
class MonochromeRectangleDetector
{
private static $MAX_MODULES = 32;
private $image;
public function __construct(BinaryBitmap $image)
{
$this->image = $image;
}
/**
* <p>Detects a rectangular region of black and white -- mostly black -- with a region of mostly
* white, in an image.</p>
*
* @return {@link ResultPoint}[] describing the corners of the rectangular region. The first and
* last points are opposed on the diagonal, as are the second and third. The first point will be
* the topmost point and the last, the bottommost. The second point will be leftmost and the
* third, the rightmost
* @throws NotFoundException if no Data Matrix Code can be found
*/
public function detect()
{
$height = $this->image->getHeight();
$width = $this->image->getWidth();
$halfHeight = $height / 2;
$halfWidth = $width / 2;
$deltaY = max(1, $height / (self::$MAX_MODULES * 8));
$deltaX = max(1, $width / (self::$MAX_MODULES * 8));
$top = 0;
$bottom = $height;
$left = 0;
$right = $width;
$pointA = $this->findCornerFromCenter($halfWidth, 0, $left, $right,
$halfHeight, -$deltaY, $top, $bottom, $halfWidth / 2);
$top = (int)$pointA->getY() - 1;
$pointB = $this->findCornerFromCenter($halfWidth, -$deltaX, $left, $right,
$halfHeight, 0, $top, $bottom, $halfHeight / 2);
$left = (int)$pointB->getX() - 1;
$pointC = $this->findCornerFromCenter($halfWidth, $deltaX, $left, $right,
$halfHeight, 0, $top, $bottom, $halfHeight / 2);
$right = (int)$pointC->getX() + 1;
$pointD = $this->findCornerFromCenter($halfWidth, 0, $left, $right,
$halfHeight, $deltaY, $top, $bottom, $halfWidth / 2);
$bottom = (int)$pointD->getY() + 1;
// Go try to find po$A again with better information -- might have been off at first.
$pointA = $this->findCornerFromCenter($halfWidth, 0, $left, $right,
$halfHeight, -$deltaY, $top, $bottom, $halfWidth / 4);
return new ResultPoint($pointA, $pointB, $pointC, $pointD);
}
/**
* Attempts to locate a corner of the barcode by scanning up, down, left or right from a center
* point which should be within the barcode.
*
* @param float $centerX center's x component (horizontal)
* @param float $deltaX same as deltaY but change in x per step instead
* @param float $left minimum value of x
* @param float $right maximum value of x
* @param float $centerY center's y component (vertical)
* @param float $deltaY change in y per step. If scanning up this is negative; down, positive;
* left or right, 0
* @param float $top minimum value of y to search through (meaningless when di == 0)
* @param float $bottom maximum value of y
* @param float $maxWhiteRun maximum run of white pixels that can still be considered to be within
* the barcode
*
* @return ResultPoint $a {@link com.google.zxing.ResultPoint} encapsulating the corner that was found
* @throws NotFoundException if such a point cannot be found
*/
private function findCornerFromCenter($centerX,
$deltaX,
$left,
$right,
$centerY,
$deltaY,
$top,
$bottom,
$maxWhiteRun)
{
$lastRange = null;
for ($y = $centerY, $x = $centerX;
$y < $bottom && $y >= $top && $x < $right && $x >= $left;
$y += $deltaY, $x += $deltaX) {
$range = 0;
if ($deltaX == 0) {
// horizontal slices, up and down
$range = $this->blackWhiteRange($y, $maxWhiteRun, $left, $right, true);
} else {
// vertical slices, left and right
$range = $this->blackWhiteRange($x, $maxWhiteRun, $top, $bottom, false);
}
if ($range == null) {
if ($lastRange == null) {
throw NotFoundException::getNotFoundInstance();
}
// lastRange was found
if ($deltaX == 0) {
$lastY = $y - $deltaY;
if ($lastRange[0] < $centerX) {
if ($lastRange[1] > $centerX) {
// straddle, choose one or the other based on direction
return new ResultPoint($deltaY > 0 ? $lastRange[0] : $lastRange[1], $lastY);
}
return new ResultPoint($lastRange[0], $lastY);
} else {
return new ResultPoint($lastRange[1], $lastY);
}
} else {
$lastX = $x - $deltaX;
if ($lastRange[0] < $centerY) {
if ($lastRange[1] > $centerY) {
return new ResultPoint($lastX, $deltaX < 0 ? $lastRange[0] : $lastRange[1]);
}
return new ResultPoint($lastX, $lastRange[0]);
} else {
return new ResultPoint($lastX, $lastRange[1]);
}
}
}
$lastRange = $range;
}
throw NotFoundException::getNotFoundInstance();
}
/**
* Computes the start and end of a region of pixels, either horizontally or vertically, that could
* be part of a Data Matrix barcode.
*
* @param fixedDimension if scanning horizontally, this is the row (the fixed vertical location)
* where we are scanning. If scanning vertically it's the column, the fixed horizontal location
* @param maxWhiteRun largest run of white pixels that can still be considered part of the
* barcode region
* @param minDim minimum pixel location, horizontally or vertically, to consider
* @param maxDim maximum pixel location, horizontally or vertically, to consider
* @param horizontal if true, we're scanning left-right, instead of up-down
*
* @return int[] with start and end of found range, or null if no such range is found
* (e.g. only white was found)
*/
private function blackWhiteRange($fixedDimension, $maxWhiteRun, $minDim, $maxDim, $horizontal)
{
$center = ($minDim + $maxDim) / 2;
// Scan left/up first
$start = $center;
while ($start >= $minDim) {
if ($horizontal ? $this->image->get($start, $fixedDimension) : $this->image->get($fixedDimension, $start)) {
$start--;
} else {
$whiteRunStart = $start;
do {
$start--;
} while ($start >= $minDim && !($horizontal ? $this->image->get($start, $fixedDimension) :
$this->image->get($fixedDimension, $start)));
$whiteRunSize = $whiteRunStart - $start;
if ($start < $minDim || $whiteRunSize > $maxWhiteRun) {
$start = $whiteRunStart;
break;
}
}
}
$start++;
// Then try right/down
$end = $center;
while ($end < $maxDim) {
if ($horizontal ? $this->image->get($end, $fixedDimension) : $this->image->get($fixedDimension, $end)) {
$end++;
} else {
$whiteRunStart = $end;
do {
$end++;
} while ($end < $maxDim && !($horizontal ? $this->image->get($end, $fixedDimension) :
$this->image->get($fixedDimension, $end)));
$whiteRunSize = $end - $whiteRunStart;
if ($end >= $maxDim || $whiteRunSize > $maxWhiteRun) {
$end = $whiteRunStart;
break;
}
}
}
$end--;
return $end > $start ? [$start, $end] : null;
}
}
@@ -0,0 +1,47 @@
<?php
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Zxing\Common;
/**
* <p>Encapsulates the result of detecting a barcode in an image. This includes the raw
* matrix of black/white pixels corresponding to the barcode, and possibly points of interest
* in the image, like the location of finder patterns or corners of the barcode in the image.</p>
*
* @author Sean Owen
*/
class DetectorResult
{
private $bits;
private $points;
public function __construct($bits, $points)
{
$this->bits = $bits;
$this->points = $points;
}
public final function getBits()
{
return $this->bits;
}
public final function getPoints()
{
return $this->points;
}
}
@@ -0,0 +1,207 @@
<?php
/*
* Copyright 2009 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Zxing\Common;
use Zxing\Binarizer;
use Zxing\LuminanceSource;
use Zxing\NotFoundException;
/**
* This Binarizer implementation uses the old ZXing global histogram approach. It is suitable
* for low-end mobile devices which don't have enough CPU or memory to use a local thresholding
* algorithm. However, because it picks a global black point, it cannot handle difficult shadows
* and gradients.
*
* Faster mobile devices and all desktop applications should probably use HybridBinarizer instead.
*
* @author dswitkin@google.com (Daniel Switkin)
* @author Sean Owen
*/
class GlobalHistogramBinarizer extends Binarizer
{
private static $LUMINANCE_BITS = 5;
private static $LUMINANCE_SHIFT = 3;
private static $LUMINANCE_BUCKETS = 32;
private static $EMPTY = [];
private $luminances = [];
private $buckets = [];
private $source = [];
public function __construct($source)
{
self::$LUMINANCE_SHIFT = 8 - self::$LUMINANCE_BITS;
self::$LUMINANCE_BUCKETS = 1 << self::$LUMINANCE_BITS;
parent::__construct($source);
$this->luminances = self::$EMPTY;
$this->buckets = fill_array(0, self::$LUMINANCE_BUCKETS, 0);
$this->source = $source;
}
// Applies simple sharpening to the row data to improve performance of the 1D Readers.
public function getBlackRow($y, $row = null)
{
$this->source = $this->getLuminanceSource();
$width = $this->source->getWidth();
if ($row == null || $row->getSize() < $width) {
$row = new BitArray($width);
} else {
$row->clear();
}
$this->initArrays($width);
$localLuminances = $this->source->getRow($y, $this->luminances);
$localBuckets = $this->buckets;
for ($x = 0; $x < $width; $x++) {
$pixel = $localLuminances[$x] & 0xff;
$localBuckets[$pixel >> self::$LUMINANCE_SHIFT]++;
}
$blackPoint = self::estimateBlackPoint($localBuckets);
$left = $localLuminances[0] & 0xff;
$center = $localLuminances[1] & 0xff;
for ($x = 1; $x < $width - 1; $x++) {
$right = $localLuminances[$x + 1] & 0xff;
// A simple -1 4 -1 box filter with a weight of 2.
$luminance = (($center * 4) - $left - $right) / 2;
if ($luminance < $blackPoint) {
$row->set($x);
}
$left = $center;
$center = $right;
}
return $row;
}
// Does not sharpen the data, as this call is intended to only be used by 2D Readers.
private function initArrays($luminanceSize)
{
if (count($this->luminances) < $luminanceSize) {
$this->luminances = [];
}
for ($x = 0; $x < self::$LUMINANCE_BUCKETS; $x++) {
$this->buckets[$x] = 0;
}
}
private static function estimateBlackPoint($buckets)
{
// Find the tallest peak in the histogram.
$numBuckets = count($buckets);
$maxBucketCount = 0;
$firstPeak = 0;
$firstPeakSize = 0;
for ($x = 0; $x < $numBuckets; $x++) {
if ($buckets[$x] > $firstPeakSize) {
$firstPeak = $x;
$firstPeakSize = $buckets[$x];
}
if ($buckets[$x] > $maxBucketCount) {
$maxBucketCount = $buckets[$x];
}
}
// Find the second-tallest peak which is somewhat far from the tallest peak.
$secondPeak = 0;
$secondPeakScore = 0;
for ($x = 0; $x < $numBuckets; $x++) {
$distanceToBiggest = $x - $firstPeak;
// Encourage more distant second peaks by multiplying by square of distance.
$score = $buckets[$x] * $distanceToBiggest * $distanceToBiggest;
if ($score > $secondPeakScore) {
$secondPeak = $x;
$secondPeakScore = $score;
}
}
// Make sure firstPeak corresponds to the black peak.
if ($firstPeak > $secondPeak) {
$temp = $firstPeak;
$firstPeak = $secondPeak;
$secondPeak = $temp;
}
// If there is too little contrast in the image to pick a meaningful black point, throw rather
// than waste time trying to decode the image, and risk false positives.
if ($secondPeak - $firstPeak <= $numBuckets / 16) {
throw NotFoundException::getNotFoundInstance();
}
// Find a valley between them that is low and closer to the white peak.
$bestValley = $secondPeak - 1;
$bestValleyScore = -1;
for ($x = $secondPeak - 1; $x > $firstPeak; $x--) {
$fromFirst = $x - $firstPeak;
$score = $fromFirst * $fromFirst * ($secondPeak - $x) * ($maxBucketCount - $buckets[$x]);
if ($score > $bestValleyScore) {
$bestValley = $x;
$bestValleyScore = $score;
}
}
return ($bestValley << self::$LUMINANCE_SHIFT);
}
public function getBlackMatrix()
{
$source = $this->getLuminanceSource();
$width = $source->getWidth();
$height = $source->getHeight();
$matrix = new BitMatrix($width, $height);
// Quickly calculates the histogram by sampling four rows from the image. This proved to be
// more robust on the blackbox tests than sampling a diagonal as we used to do.
$this->initArrays($width);
$localBuckets = $this->buckets;
for ($y = 1; $y < 5; $y++) {
$row = (int)($height * $y / 5);
$localLuminances = $source->getRow($row, $this->luminances);
$right = (int)(($width * 4) / 5);
for ($x = (int)($width / 5); $x < $right; $x++) {
$pixel = ($localLuminances[(int)($x)] & 0xff);
$localBuckets[($pixel >> self::$LUMINANCE_SHIFT)]++;
}
}
$blackPoint = self::estimateBlackPoint($localBuckets);
// We delay reading the entire image luminance until the black point estimation succeeds.
// Although we end up reading four rows twice, it is consistent with our motto of
// "fail quickly" which is necessary for continuous scanning.
$localLuminances = $source->getMatrix();
for ($y = 0; $y < $height; $y++) {
$offset = $y * $width;
for ($x = 0; $x < $width; $x++) {
$pixel = (int)($localLuminances[$offset + $x] & 0xff);
if ($pixel < $blackPoint) {
$matrix->set($x, $y);
}
}
}
return $matrix;
}
public function createBinarizer($source)
{
return new GlobalHistogramBinarizer($source);
}
}
@@ -0,0 +1,188 @@
<?php
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Zxing\Common;
use Zxing\NotFoundException;
/**
* Implementations of this class can, given locations of finder patterns for a QR code in an
* image, sample the right points in the image to reconstruct the QR code, accounting for
* perspective distortion. It is abstracted since it is relatively expensive and should be allowed
* to take advantage of platform-specific optimized implementations, like Sun's Java Advanced
* Imaging library, but which may not be available in other environments such as J2ME, and vice
* versa.
*
* The implementation used can be controlled by calling {@link #setGridSampler(GridSampler)}
* with an instance of a class which implements this interface.
*
* @author Sean Owen
*/
abstract class GridSampler
{
private static $gridSampler;
/**
* Sets the implementation of GridSampler used by the library. One global
* instance is stored, which may sound problematic. But, the implementation provided
* ought to be appropriate for the entire platform, and all uses of this library
* in the whole lifetime of the JVM. For instance, an Android activity can swap in
* an implementation that takes advantage of native platform libraries.
*
* @param newGridSampler The platform-specific object to install.
*/
public static function setGridSampler($newGridSampler)
{
self::$gridSampler = $newGridSampler;
}
/**
* @return the current implementation of GridSampler
*/
public static function getInstance()
{
if (!self::$gridSampler) {
self::$gridSampler = new DefaultGridSampler();
}
return self::$gridSampler;
}
/**
* <p>Checks a set of points that have been transformed to sample points on an image against
* the image's dimensions to see if the point are even within the image.</p>
*
* <p>This method will actually "nudge" the endpoints back onto the image if they are found to be
* barely (less than 1 pixel) off the image. This accounts for imperfect detection of finder
* patterns in an image where the QR Code runs all the way to the image border.</p>
*
* <p>For efficiency, the method will check points from either end of the line until one is found
* to be within the image. Because the set of points are assumed to be linear, this is valid.</p>
*
* @param image image into which the points should map
* @param points actual points in x1,y1,...,xn,yn form
*
* @throws NotFoundException if an endpoint is lies outside the image boundaries
*/
protected static function checkAndNudgePoints(
$image,
$points
) {
$width = $image->getWidth();
$height = $image->getHeight();
// Check and nudge points from start until we see some that are OK:
$nudged = true;
for ($offset = 0; $offset < count($points) && $nudged; $offset += 2) {
$x = (int)$points[$offset];
$y = (int)$points[$offset + 1];
if ($x < -1 || $x > $width || $y < -1 || $y > $height) {
throw NotFoundException::getNotFoundInstance();
}
$nudged = false;
if ($x == -1) {
$points[$offset] = 0.0;
$nudged = true;
} else if ($x == $width) {
$points[$offset] = $width - 1;
$nudged = true;
}
if ($y == -1) {
$points[$offset + 1] = 0.0;
$nudged = true;
} else if ($y == $height) {
$points[$offset + 1] = $height - 1;
$nudged = true;
}
}
// Check and nudge points from end:
$nudged = true;
for ($offset = count($points) - 2; $offset >= 0 && $nudged; $offset -= 2) {
$x = (int)$points[$offset];
$y = (int)$points[$offset + 1];
if ($x < -1 || $x > $width || $y < -1 || $y > $height) {
throw NotFoundException::getNotFoundInstance();
}
$nudged = false;
if ($x == -1) {
$points[$offset] = 0.0;
$nudged = true;
} else if ($x == $width) {
$points[$offset] = $width - 1;
$nudged = true;
}
if ($y == -1) {
$points[$offset + 1] = 0.0;
$nudged = true;
} else if ($y == $height) {
$points[$offset + 1] = $height - 1;
$nudged = true;
}
}
}
/**
* Samples an image for a rectangular matrix of bits of the given dimension. The sampling
* transformation is determined by the coordinates of 4 points, in the original and transformed
* image space.
*
* @param image image to sample
* @param dimensionX width of {@link BitMatrix} to sample from image
* @param dimensionY height of {@link BitMatrix} to sample from image
* @param p1ToX point 1 preimage X
* @param p1ToY point 1 preimage Y
* @param p2ToX point 2 preimage X
* @param p2ToY point 2 preimage Y
* @param p3ToX point 3 preimage X
* @param p3ToY point 3 preimage Y
* @param p4ToX point 4 preimage X
* @param p4ToY point 4 preimage Y
* @param p1FromX point 1 image X
* @param p1FromY point 1 image Y
* @param p2FromX point 2 image X
* @param p2FromY point 2 image Y
* @param p3FromX point 3 image X
* @param p3FromY point 3 image Y
* @param p4FromX point 4 image X
* @param p4FromY point 4 image Y
*
* @return {@link BitMatrix} representing a grid of points sampled from the image within a region
* defined by the "from" parameters
* @throws NotFoundException if image can't be sampled, for example, if the transformation defined
* by the given points is invalid or results in sampling outside the image boundaries
*/
public abstract function sampleGrid(
$image,
$dimensionX,
$dimensionY,
$p1ToX, $p1ToY,
$p2ToX, $p2ToY,
$p3ToX, $p3ToY,
$p4ToX, $p4ToY,
$p1FromX, $p1FromY,
$p2FromX, $p2FromY,
$p3FromX, $p3FromY,
$p4FromX, $p4FromY
);
public abstract function sampleGrid_(
$image,
$dimensionX,
$dimensionY,
$transform
);
}
@@ -0,0 +1,264 @@
<?php
/*
* Copyright 2009 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Zxing\Common;
use Zxing\Binarizer;
use Zxing\LuminanceSource;
use Zxing\NotFoundException;
/**
* This class implements a local thresholding algorithm, which while slower than the
* GlobalHistogramBinarizer, is fairly efficient for what it does. It is designed for
* high frequency images of barcodes with black data on white backgrounds. For this application,
* it does a much better job than a global blackpoint with severe shadows and gradients.
* However it tends to produce artifacts on lower frequency images and is therefore not
* a good general purpose binarizer for uses outside ZXing.
*
* This class extends GlobalHistogramBinarizer, using the older histogram approach for 1D readers,
* and the newer local approach for 2D readers. 1D decoding using a per-row histogram is already
* inherently local, and only fails for horizontal gradients. We can revisit that problem later,
* but for now it was not a win to use local blocks for 1D.
*
* This Binarizer is the default for the unit tests and the recommended class for library users.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
final class HybridBinarizer extends GlobalHistogramBinarizer
{
// This class uses 5x5 blocks to compute local luminance, where each block is 8x8 pixels.
// So this is the smallest dimension in each axis we can accept.
private static $BLOCK_SIZE_POWER = 3;
private static $BLOCK_SIZE = 8; // ...0100...00
private static $BLOCK_SIZE_MASK = 7; // ...0011...11
private static $MINIMUM_DIMENSION = 40;
private static $MIN_DYNAMIC_RANGE = 24;
private $matrix;
public function __construct($source)
{
parent::__construct($source);
self::$BLOCK_SIZE_POWER = 3;
self::$BLOCK_SIZE = 1 << self::$BLOCK_SIZE_POWER; // ...0100...00
self::$BLOCK_SIZE_MASK = self::$BLOCK_SIZE - 1; // ...0011...11
self::$MINIMUM_DIMENSION = self::$BLOCK_SIZE * 5;
self::$MIN_DYNAMIC_RANGE = 24;
}
/**
* Calculates the final BitMatrix once for all requests. This could be called once from the
* constructor instead, but there are some advantages to doing it lazily, such as making
* profiling easier, and not doing heavy lifting when callers don't expect it.
*/
public function getBlackMatrix()
{
if ($this->matrix !== null) {
return $this->matrix;
}
$source = $this->getLuminanceSource();
$width = $source->getWidth();
$height = $source->getHeight();
if ($width >= self::$MINIMUM_DIMENSION && $height >= self::$MINIMUM_DIMENSION) {
$luminances = $source->getMatrix();
$subWidth = $width >> self::$BLOCK_SIZE_POWER;
if (($width & self::$BLOCK_SIZE_MASK) != 0) {
$subWidth++;
}
$subHeight = $height >> self::$BLOCK_SIZE_POWER;
if (($height & self::$BLOCK_SIZE_MASK) != 0) {
$subHeight++;
}
$blackPoints = self::calculateBlackPoints($luminances, $subWidth, $subHeight, $width, $height);
$newMatrix = new BitMatrix($width, $height);
self::calculateThresholdForBlock($luminances, $subWidth, $subHeight, $width, $height, $blackPoints, $newMatrix);
$this->matrix = $newMatrix;
} else {
// If the image is too small, fall back to the global histogram approach.
$this->matrix = parent::getBlackMatrix();
}
return $this->matrix;
}
/**
* Calculates a single black point for each block of pixels and saves it away.
* See the following thread for a discussion of this algorithm:
* http://groups.google.com/group/zxing/browse_thread/thread/d06efa2c35a7ddc0
*/
private static function calculateBlackPoints(
$luminances,
$subWidth,
$subHeight,
$width,
$height
) {
$blackPoints = fill_array(0, $subHeight, 0);
foreach ($blackPoints as $key => $point) {
$blackPoints[$key] = fill_array(0, $subWidth, 0);
}
for ($y = 0; $y < $subHeight; $y++) {
$yoffset = ($y << self::$BLOCK_SIZE_POWER);
$maxYOffset = $height - self::$BLOCK_SIZE;
if ($yoffset > $maxYOffset) {
$yoffset = $maxYOffset;
}
for ($x = 0; $x < $subWidth; $x++) {
$xoffset = ($x << self::$BLOCK_SIZE_POWER);
$maxXOffset = $width - self::$BLOCK_SIZE;
if ($xoffset > $maxXOffset) {
$xoffset = $maxXOffset;
}
$sum = 0;
$min = 0xFF;
$max = 0;
for ($yy = 0, $offset = $yoffset * $width + $xoffset; $yy < self::$BLOCK_SIZE; $yy++, $offset += $width) {
for ($xx = 0; $xx < self::$BLOCK_SIZE; $xx++) {
$pixel = ((int)($luminances[(int)($offset + $xx)]) & 0xFF);
$sum += $pixel;
// still looking for good contrast
if ($pixel < $min) {
$min = $pixel;
}
if ($pixel > $max) {
$max = $pixel;
}
}
// short-circuit min/max tests once dynamic range is met
if ($max - $min > self::$MIN_DYNAMIC_RANGE) {
// finish the rest of the rows quickly
for ($yy++, $offset += $width; $yy < self::$BLOCK_SIZE; $yy++, $offset += $width) {
for ($xx = 0; $xx < self::$BLOCK_SIZE; $xx++) {
$sum += ($luminances[$offset + $xx] & 0xFF);
}
}
}
}
// The default estimate is the average of the values in the block.
$average = ($sum >> (self::$BLOCK_SIZE_POWER * 2));
if ($max - $min <= self::$MIN_DYNAMIC_RANGE) {
// If variation within the block is low, assume this is a block with only light or only
// dark pixels. In that case we do not want to use the average, as it would divide this
// low contrast area into black and white pixels, essentially creating data out of noise.
//
// The default assumption is that the block is light/background. Since no estimate for
// the level of dark pixels exists locally, use half the min for the block.
$average = (int)($min / 2);
if ($y > 0 && $x > 0) {
// Correct the "white background" assumption for blocks that have neighbors by comparing
// the pixels in this block to the previously calculated black points. This is based on
// the fact that dark barcode symbology is always surrounded by some amount of light
// background for which reasonable black point estimates were made. The bp estimated at
// the boundaries is used for the interior.
// The (min < bp) is arbitrary but works better than other heuristics that were tried.
$averageNeighborBlackPoint =
(int)(($blackPoints[$y - 1][$x] + (2 * $blackPoints[$y][$x - 1]) + $blackPoints[$y - 1][$x - 1]) / 4);
if ($min < $averageNeighborBlackPoint) {
$average = $averageNeighborBlackPoint;
}
}
}
$blackPoints[$y][$x] = (int)($average);
}
}
return $blackPoints;
}
/**
* For each block in the image, calculate the average black point using a 5x5 grid
* of the blocks around it. Also handles the corner cases (fractional blocks are computed based
* on the last pixels in the row/column which are also used in the previous block).
*/
private static function calculateThresholdForBlock(
$luminances,
$subWidth,
$subHeight,
$width,
$height,
$blackPoints,
$matrix
) {
for ($y = 0; $y < $subHeight; $y++) {
$yoffset = ($y << self::$BLOCK_SIZE_POWER);
$maxYOffset = $height - self::$BLOCK_SIZE;
if ($yoffset > $maxYOffset) {
$yoffset = $maxYOffset;
}
for ($x = 0; $x < $subWidth; $x++) {
$xoffset = ($x << self::$BLOCK_SIZE_POWER);
$maxXOffset = $width - self::$BLOCK_SIZE;
if ($xoffset > $maxXOffset) {
$xoffset = $maxXOffset;
}
$left = self::cap($x, 2, $subWidth - 3);
$top = self::cap($y, 2, $subHeight - 3);
$sum = 0;
for ($z = -2; $z <= 2; $z++) {
$blackRow = $blackPoints[$top + $z];
$sum += $blackRow[$left - 2] + $blackRow[$left - 1] + $blackRow[$left] + $blackRow[$left + 1] + $blackRow[$left + 2];
}
$average = (int)($sum / 25);
self::thresholdBlock($luminances, $xoffset, $yoffset, $average, $width, $matrix);
}
}
}
private static function cap($value, $min, $max)
{
if ($value < $min) {
return $min;
} elseif ($value > $max) {
return $max;
} else {
return $value;
}
}
/**
* Applies a single threshold to a block of pixels.
*/
private static function thresholdBlock(
$luminances,
$xoffset,
$yoffset,
$threshold,
$stride,
$matrix
) {
for ($y = 0, $offset = $yoffset * $stride + $xoffset; $y < self::$BLOCK_SIZE; $y++, $offset += $stride) {
for ($x = 0; $x < self::$BLOCK_SIZE; $x++) {
// Comparison needs to be <= so that black == 0 pixels are black even if the threshold is 0.
if (($luminances[$offset + $x] & 0xFF) <= $threshold) {
$matrix->set($xoffset + $x, $yoffset + $y);
}
}
}
}
public function createBinarizer($source)
{
return new HybridBinarizer($source);
}
}
@@ -0,0 +1,175 @@
<?php
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Zxing\Common;
/**
* <p>This class implements a perspective transform in two dimensions. Given four source and four
* destination points, it will compute the transformation implied between them. The code is based
* directly upon section 3.4.2 of George Wolberg's "Digital Image Warping"; see pages 54-56.</p>
*
* @author Sean Owen
*/
final class PerspectiveTransform
{
private $a11;
private $a12;
private $a13;
private $a21;
private $a22;
private $a23;
private $a31;
private $a32;
private $a33;
private function __construct(
$a11, $a21, $a31,
$a12, $a22, $a32,
$a13, $a23, $a33
) {
$this->a11 = $a11;
$this->a12 = $a12;
$this->a13 = $a13;
$this->a21 = $a21;
$this->a22 = $a22;
$this->a23 = $a23;
$this->a31 = $a31;
$this->a32 = $a32;
$this->a33 = $a33;
}
public static function quadrilateralToQuadrilateral(
$x0, $y0,
$x1, $y1,
$x2, $y2,
$x3, $y3,
$x0p, $y0p,
$x1p, $y1p,
$x2p, $y2p,
$x3p, $y3p
) {
$qToS = self::quadrilateralToSquare($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3);
$sToQ = self::squareToQuadrilateral($x0p, $y0p, $x1p, $y1p, $x2p, $y2p, $x3p, $y3p);
return $sToQ->times($qToS);
}
public static function quadrilateralToSquare(
$x0, $y0,
$x1, $y1,
$x2, $y2,
$x3, $y3
) {
// Here, the adjoint serves as the inverse:
return self::squareToQuadrilateral($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)->buildAdjoint();
}
public function buildAdjoint()
{
// Adjoint is the transpose of the cofactor matrix:
return new PerspectiveTransform($this->a22 * $this->a33 - $this->a23 * $this->a32,
$this->a23 * $this->a31 - $this->a21 * $this->a33,
$this->a21 * $this->a32 - $this->a22 * $this->a31,
$this->a13 * $this->a32 - $this->a12 * $this->a33,
$this->a11 * $this->a33 - $this->a13 * $this->a31,
$this->a12 * $this->a31 - $this->a11 * $this->a32,
$this->a12 * $this->a23 - $this->a13 * $this->a22,
$this->a13 * $this->a21 - $this->a11 * $this->a23,
$this->a11 * $this->a22 - $this->a12 * $this->a21);
}
public static function squareToQuadrilateral(
$x0, $y0,
$x1, $y1,
$x2, $y2,
$x3, $y3
) {
$dx3 = $x0 - $x1 + $x2 - $x3;
$dy3 = $y0 - $y1 + $y2 - $y3;
if ($dx3 == 0.0 && $dy3 == 0.0) {
// Affine
return new PerspectiveTransform($x1 - $x0, $x2 - $x1, $x0,
$y1 - $y0, $y2 - $y1, $y0,
0.0, 0.0, 1.0);
} else {
$dx1 = $x1 - $x2;
$dx2 = $x3 - $x2;
$dy1 = $y1 - $y2;
$dy2 = $y3 - $y2;
$denominator = $dx1 * $dy2 - $dx2 * $dy1;
$a13 = ($dx3 * $dy2 - $dx2 * $dy3) / $denominator;
$a23 = ($dx1 * $dy3 - $dx3 * $dy1) / $denominator;
return new PerspectiveTransform($x1 - $x0 + $a13 * $x1, $x3 - $x0 + $a23 * $x3, $x0,
$y1 - $y0 + $a13 * $y1, $y3 - $y0 + $a23 * $y3, $y0,
$a13, $a23, 1.0);
}
}
public function times($other)
{
return new PerspectiveTransform($this->a11 * $other->a11 + $this->a21 * $other->a12 + $this->a31 * $other->a13,
$this->a11 * $other->a21 + $this->a21 * $other->a22 + $this->a31 * $other->a23,
$this->a11 * $other->a31 + $this->a21 * $other->a32 + $this->a31 * $other->a33,
$this->a12 * $other->a11 + $this->a22 * $other->a12 + $this->a32 * $other->a13,
$this->a12 * $other->a21 + $this->a22 * $other->a22 + $this->a32 * $other->a23,
$this->a12 * $other->a31 + $this->a22 * $other->a32 + $this->a32 * $other->a33,
$this->a13 * $other->a11 + $this->a23 * $other->a12 + $this->a33 * $other->a13,
$this->a13 * $other->a21 + $this->a23 * $other->a22 + $this->a33 * $other->a23,
$this->a13 * $other->a31 + $this->a23 * $other->a32 + $this->a33 * $other->a33);
}
public function transformPoints(&$points, &$yValues = 0)
{
if ($yValues) {
$this->transformPoints_($points, $yValues);
return;
}
$max = count($points);
$a11 = $this->a11;
$a12 = $this->a12;
$a13 = $this->a13;
$a21 = $this->a21;
$a22 = $this->a22;
$a23 = $this->a23;
$a31 = $this->a31;
$a32 = $this->a32;
$a33 = $this->a33;
for ($i = 0; $i < $max; $i += 2) {
$x = $points[$i];
$y = $points[$i + 1];
$denominator = $a13 * $x + $a23 * $y + $a33;
$points[$i] = ($a11 * $x + $a21 * $y + $a31) / $denominator;
$points[$i + 1] = ($a12 * $x + $a22 * $y + $a32) / $denominator;
}
}
public function transformPoints_(&$xValues, &$yValues)
{
$n = count($xValues);
for ($i = 0; $i < $n; $i++) {
$x = $xValues[$i];
$y = $yValues[$i];
$denominator = $this->a13 * $x + $this->a23 * $y + $this->a33;
$xValues[$i] = ($this->a11 * $x + $this->a21 * $y + $this->a31) / $denominator;
$yValues[$i] = ($this->a12 * $x + $this->a22 * $y + $this->a32) / $denominator;
}
}
}
@@ -0,0 +1,198 @@
<?php
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Zxing\Common\Reedsolomon;
/**
* <p>This class contains utility methods for performing mathematical operations over
* the Galois Fields. Operations use a given primitive polynomial in calculations.</p>
*
* <p>Throughout this package, elements of the GF are represented as an {@code int}
* for convenience and speed (but at the cost of memory).
* </p>
*
* @author Sean Owen
* @author David Olivier
*/
final class GenericGF
{
public static $AZTEC_DATA_12;
public static $AZTEC_DATA_10;
public static $AZTEC_DATA_6;
public static $AZTEC_PARAM;
public static $QR_CODE_FIELD_256;
public static $DATA_MATRIX_FIELD_256;
public static $AZTEC_DATA_8;
public static $MAXICODE_FIELD_64;
private $expTable;
private $logTable;
private $zero;
private $one;
private $size;
private $primitive;
private $generatorBase;
/**
* Create a representation of GF(size) using the given primitive polynomial.
*
* @param primitive irreducible polynomial whose coefficients are represented by
* the bits of an int, where the least-significant bit represents the constant
* coefficient
* @param size the size of the field
* @param b the factor b in the generator polynomial can be 0- or 1-based
* (g(x) = (x+a^b)(x+a^(b+1))...(x+a^(b+2t-1))).
* In most cases it should be 1, but for QR code it is 0.
*/
public function __construct($primitive, $size, $b)
{
$this->primitive = $primitive;
$this->size = $size;
$this->generatorBase = $b;
$this->expTable = [];
$this->logTable = [];
$x = 1;
for ($i = 0; $i < $size; $i++) {
$this->expTable[$i] = $x;
$x *= 2; // we're assuming the generator alpha is 2
if ($x >= $size) {
$x ^= $primitive;
$x &= $size - 1;
}
}
for ($i = 0; $i < $size - 1; $i++) {
$this->logTable[$this->expTable[$i]] = $i;
}
// logTable[0] == 0 but this should never be used
$this->zero = new GenericGFPoly($this, [0]);
$this->one = new GenericGFPoly($this, [1]);
}
public static function Init()
{
self::$AZTEC_DATA_12 = new GenericGF(0x1069, 4096, 1); // x^12 + x^6 + x^5 + x^3 + 1
self::$AZTEC_DATA_10 = new GenericGF(0x409, 1024, 1); // x^10 + x^3 + 1
self::$AZTEC_DATA_6 = new GenericGF(0x43, 64, 1); // x^6 + x + 1
self::$AZTEC_PARAM = new GenericGF(0x13, 16, 1); // x^4 + x + 1
self::$QR_CODE_FIELD_256 = new GenericGF(0x011D, 256, 0); // x^8 + x^4 + x^3 + x^2 + 1
self::$DATA_MATRIX_FIELD_256 = new GenericGF(0x012D, 256, 1); // x^8 + x^5 + x^3 + x^2 + 1
self::$AZTEC_DATA_8 = self::$DATA_MATRIX_FIELD_256;
self::$MAXICODE_FIELD_64 = self::$AZTEC_DATA_6;
}
/**
* Implements both addition and subtraction -- they are the same in GF(size).
*
* @return sum/difference of a and b
*/
public static function addOrSubtract($a, $b)
{
return $a ^ $b;
}
public function getZero()
{
return $this->zero;
}
public function getOne()
{
return $this->one;
}
/**
* @return the monomial representing coefficient * x^degree
*/
public function buildMonomial($degree, $coefficient)
{
if ($degree < 0) {
throw new \InvalidArgumentException();
}
if ($coefficient == 0) {
return $this->zero;
}
$coefficients = fill_array(0, $degree + 1, 0);//new int[degree + 1];
$coefficients[0] = $coefficient;
return new GenericGFPoly($this, $coefficients);
}
/**
* @return 2 to the power of a in GF(size)
*/
public function exp($a)
{
return $this->expTable[$a];
}
/**
* @return base 2 log of a in GF(size)
*/
public function log($a)
{
if ($a == 0) {
throw new \InvalidArgumentException();
}
return $this->logTable[$a];
}
/**
* @return multiplicative inverse of a
*/
public function inverse($a)
{
if ($a == 0) {
throw new \Exception();
}
return $this->expTable[$this->size - $this->logTable[$a] - 1];
}
/**
* @return int product of a and b in GF(size)
*/
public function multiply($a, $b)
{
if ($a == 0 || $b == 0) {
return 0;
}
return $this->expTable[($this->logTable[$a] + $this->logTable[$b]) % ($this->size - 1)];
}
public function getSize()
{
return $this->size;
}
public function getGeneratorBase()
{
return $this->generatorBase;
}
// @Override
public function toString()
{
return "GF(0x" . dechex((int)($this->primitive)) . ',' . $this->size . ')';
}
}
GenericGF::Init();
@@ -0,0 +1,289 @@
<?php
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Zxing\Common\Reedsolomon;
/**
* <p>Represents a polynomial whose coefficients are elements of a GF.
* Instances of this class are immutable.</p>
*
* <p>Much credit is due to William Rucklidge since portions of this code are an indirect
* port of his C++ Reed-Solomon implementation.</p>
*
* @author Sean Owen
*/
final class GenericGFPoly
{
private $field;
private $coefficients;
/**
* @param field the {@link GenericGF} instance representing the field to use
* to perform computations
* @param coefficients array coefficients as ints representing elements of GF(size), arranged
* from most significant (highest-power term) coefficient to least significant
*
* @throws InvalidArgumentException if argument is null or empty,
* or if leading coefficient is 0 and this is not a
* constant polynomial (that is, it is not the monomial "0")
*/
public function __construct($field, $coefficients)
{
if (count($coefficients) == 0) {
throw new \InvalidArgumentException();
}
$this->field = $field;
$coefficientsLength = count($coefficients);
if ($coefficientsLength > 1 && $coefficients[0] == 0) {
// Leading term must be non-zero for anything except the constant polynomial "0"
$firstNonZero = 1;
while ($firstNonZero < $coefficientsLength && $coefficients[$firstNonZero] == 0) {
$firstNonZero++;
}
if ($firstNonZero == $coefficientsLength) {
$this->coefficients = [0];
} else {
$this->coefficients = fill_array(0, $coefficientsLength - $firstNonZero, 0);
$this->coefficients = arraycopy($coefficients,
$firstNonZero,
$this->coefficients,
0,
count($this->coefficients));
}
} else {
$this->coefficients = $coefficients;
}
}
public function getCoefficients()
{
return $this->coefficients;
}
/**
* @return evaluation of this polynomial at a given point
*/
public function evaluateAt($a)
{
if ($a == 0) {
// Just return the x^0 coefficient
return $this->getCoefficient(0);
}
$size = count($this->coefficients);
if ($a == 1) {
// Just the sum of the coefficients
$result = 0;
foreach ($this->coefficients as $coefficient) {
$result = GenericGF::addOrSubtract($result, $coefficient);
}
return $result;
}
$result = $this->coefficients[0];
for ($i = 1; $i < $size; $i++) {
$result = GenericGF::addOrSubtract($this->field->multiply($a, $result), $this->coefficients[$i]);
}
return $result;
}
/**
* @return coefficient of x^degree term in this polynomial
*/
public function getCoefficient($degree)
{
return $this->coefficients[count($this->coefficients) - 1 - $degree];
}
public function multiply($other)
{
if (is_int($other)) {
return $this->multiply_($other);
}
if ($this->field !== $other->field) {
throw new \InvalidArgumentException("GenericGFPolys do not have same GenericGF field");
}
if ($this->isZero() || $other->isZero()) {
return $this->field->getZero();
}
$aCoefficients = $this->coefficients;
$aLength = count($aCoefficients);
$bCoefficients = $other->coefficients;
$bLength = count($bCoefficients);
$product = fill_array(0, $aLength + $bLength - 1, 0);
for ($i = 0; $i < $aLength; $i++) {
$aCoeff = $aCoefficients[$i];
for ($j = 0; $j < $bLength; $j++) {
$product[$i + $j] = GenericGF::addOrSubtract($product[$i + $j],
$this->field->multiply($aCoeff, $bCoefficients[$j]));
}
}
return new GenericGFPoly($this->field, $product);
}
public function multiply_($scalar)
{
if ($scalar == 0) {
return $this->field->getZero();
}
if ($scalar == 1) {
return $this;
}
$size = count($this->coefficients);
$product = fill_array(0, $size, 0);
for ($i = 0; $i < $size; $i++) {
$product[$i] = $this->field->multiply($this->coefficients[$i], $scalar);
}
return new GenericGFPoly($this->field, $product);
}
/**
* @return true iff this polynomial is the monomial "0"
*/
public function isZero()
{
return $this->coefficients[0] == 0;
}
public function multiplyByMonomial($degree, $coefficient)
{
if ($degree < 0) {
throw new \InvalidArgumentException();
}
if ($coefficient == 0) {
return $this->field->getZero();
}
$size = count($this->coefficients);
$product = fill_array(0, $size + $degree, 0);
for ($i = 0; $i < $size; $i++) {
$product[$i] = $this->field->multiply($this->coefficients[$i], $coefficient);
}
return new GenericGFPoly($this->field, $product);
}
public function divide($other)
{
if ($this->field !== $other->field) {
throw new \InvalidArgumentException("GenericGFPolys do not have same GenericGF field");
}
if ($other->isZero()) {
throw new \InvalidArgumentException("Divide by 0");
}
$quotient = $this->field->getZero();
$remainder = $this;
$denominatorLeadingTerm = $other->getCoefficient($other->getDegree());
$inverseDenominatorLeadingTerm = $this->field->inverse($denominatorLeadingTerm);
while ($remainder->getDegree() >= $other->getDegree() && !$remainder->isZero()) {
$degreeDifference = $remainder->getDegree() - $other->getDegree();
$scale = $this->field->multiply($remainder->getCoefficient($remainder->getDegree()), $inverseDenominatorLeadingTerm);
$term = $other->multiplyByMonomial($degreeDifference, $scale);
$iterationQuotient = $this->field->buildMonomial($degreeDifference, $scale);
$quotient = $quotient->addOrSubtract($iterationQuotient);
$remainder = $remainder->addOrSubtract($term);
}
return [$quotient, $remainder];
}
/**
* @return degree of this polynomial
*/
public function getDegree()
{
return count($this->coefficients) - 1;
}
public function addOrSubtract($other)
{
if ($this->field !== $other->field) {
throw new \InvalidArgumentException("GenericGFPolys do not have same GenericGF field");
}
if ($this->isZero()) {
return $other;
}
if ($other->isZero()) {
return $this;
}
$smallerCoefficients = $this->coefficients;
$largerCoefficients = $other->coefficients;
if (count($smallerCoefficients) > count($largerCoefficients)) {
$temp = $smallerCoefficients;
$smallerCoefficients = $largerCoefficients;
$largerCoefficients = $temp;
}
$sumDiff = fill_array(0, count($largerCoefficients), 0);
$lengthDiff = count($largerCoefficients) - count($smallerCoefficients);
// Copy high-order terms only found in higher-degree polynomial's coefficients
$sumDiff = arraycopy($largerCoefficients, 0, $sumDiff, 0, $lengthDiff);
$countLargerCoefficients = count($largerCoefficients);
for ($i = $lengthDiff; $i < $countLargerCoefficients; $i++) {
$sumDiff[$i] = GenericGF::addOrSubtract($smallerCoefficients[$i - $lengthDiff], $largerCoefficients[$i]);
}
return new GenericGFPoly($this->field, $sumDiff);
}
//@Override
public function toString()
{
$result = '';
for ($degree = $this->getDegree(); $degree >= 0; $degree--) {
$coefficient = $this->getCoefficient($degree);
if ($coefficient != 0) {
if ($coefficient < 0) {
$result .= " - ";
$coefficient = -$coefficient;
} else {
if (strlen($result) > 0) {
$result .= " + ";
}
}
if ($degree == 0 || $coefficient != 1) {
$alphaPower = $this->field->log($coefficient);
if ($alphaPower == 0) {
$result .= '1';
} else if ($alphaPower == 1) {
$result .= 'a';
} else {
$result .= "a^";
$result .= ($alphaPower);
}
}
if ($degree != 0) {
if ($degree == 1) {
$result .= 'x';
} else {
$result .= "x^";
$result .= $degree;
}
}
}
}
return $result;
}
}
@@ -0,0 +1,201 @@
<?php
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Zxing\Common\Reedsolomon;
/**
* <p>Implements Reed-Solomon decoding, as the name implies.</p>
*
* <p>The algorithm will not be explained here, but the following references were helpful
* in creating this implementation:</p>
*
* <ul>
* <li>Bruce Maggs.
* <a href="http://www.cs.cmu.edu/afs/cs.cmu.edu/project/pscico-guyb/realworld/www/rs_decode.ps">
* "Decoding Reed-Solomon Codes"</a> (see discussion of Forney's Formula)</li>
* <li>J.I. Hall. <a href="www.mth.msu.edu/~jhall/classes/codenotes/GRS.pdf">
* "Chapter 5. Generalized Reed-Solomon Codes"</a>
* (see discussion of Euclidean algorithm)</li>
* </ul>
*
* <p>Much credit is due to William Rucklidge since portions of this code are an indirect
* port of his C++ Reed-Solomon implementation.</p>
*
* @author Sean Owen
* @author William Rucklidge
* @author sanfordsquires
*/
final class ReedSolomonDecoder
{
private $field;
public function __construct($field)
{
$this->field = $field;
}
/**
* <p>Decodes given set of received codewords, which include both data and error-correction
* codewords. Really, this means it uses Reed-Solomon to detect and correct errors, in-place,
* in the input.</p>
*
* @param received data and error-correction codewords
* @param twoS number of error-correction codewords available
*
* @throws ReedSolomonException if decoding fails for any reason
*/
public function decode(&$received, $twoS)
{
$poly = new GenericGFPoly($this->field, $received);
$syndromeCoefficients = fill_array(0, $twoS, 0);
$noError = true;
for ($i = 0; $i < $twoS; $i++) {
$eval = $poly->evaluateAt($this->field->exp($i + $this->field->getGeneratorBase()));
$syndromeCoefficients[count($syndromeCoefficients) - 1 - $i] = $eval;
if ($eval != 0) {
$noError = false;
}
}
if ($noError) {
return;
}
$syndrome = new GenericGFPoly($this->field, $syndromeCoefficients);
$sigmaOmega =
$this->runEuclideanAlgorithm($this->field->buildMonomial($twoS, 1), $syndrome, $twoS);
$sigma = $sigmaOmega[0];
$omega = $sigmaOmega[1];
$errorLocations = $this->findErrorLocations($sigma);
$errorMagnitudes = $this->findErrorMagnitudes($omega, $errorLocations);
$errorLocationsCount = count($errorLocations);
for ($i = 0; $i < $errorLocationsCount; $i++) {
$position = count($received) - 1 - $this->field->log($errorLocations[$i]);
if ($position < 0) {
throw new ReedSolomonException("Bad error location");
}
$received[$position] = GenericGF::addOrSubtract($received[$position], $errorMagnitudes[$i]);
}
}
private function runEuclideanAlgorithm($a, $b, $R)
{
// Assume a's degree is >= b's
if ($a->getDegree() < $b->getDegree()) {
$temp = $a;
$a = $b;
$b = $temp;
}
$rLast = $a;
$r = $b;
$tLast = $this->field->getZero();
$t = $this->field->getOne();
// Run Euclidean algorithm until r's degree is less than R/2
while ($r->getDegree() >= $R / 2) {
$rLastLast = $rLast;
$tLastLast = $tLast;
$rLast = $r;
$tLast = $t;
// Divide rLastLast by rLast, with quotient in q and remainder in r
if ($rLast->isZero()) {
// Oops, Euclidean algorithm already terminated?
throw new ReedSolomonException("r_{i-1} was zero");
}
$r = $rLastLast;
$q = $this->field->getZero();
$denominatorLeadingTerm = $rLast->getCoefficient($rLast->getDegree());
$dltInverse = $this->field->inverse($denominatorLeadingTerm);
while ($r->getDegree() >= $rLast->getDegree() && !$r->isZero()) {
$degreeDiff = $r->getDegree() - $rLast->getDegree();
$scale = $this->field->multiply($r->getCoefficient($r->getDegree()), $dltInverse);
$q = $q->addOrSubtract($this->field->buildMonomial($degreeDiff, $scale));
$r = $r->addOrSubtract($rLast->multiplyByMonomial($degreeDiff, $scale));
}
$t = $q->multiply($tLast)->addOrSubtract($tLastLast);
if ($r->getDegree() >= $rLast->getDegree()) {
throw new ReedSolomonException("Division algorithm failed to reduce polynomial?");
}
}
$sigmaTildeAtZero = $t->getCoefficient(0);
if ($sigmaTildeAtZero == 0) {
throw new ReedSolomonException("sigmaTilde(0) was zero");
}
$inverse = $this->field->inverse($sigmaTildeAtZero);
$sigma = $t->multiply($inverse);
$omega = $r->multiply($inverse);
return [$sigma, $omega];
}
private function findErrorLocations($errorLocator)
{
// This is a direct application of Chien's search
$numErrors = $errorLocator->getDegree();
if ($numErrors == 1) { // shortcut
return [$errorLocator->getCoefficient(1)];
}
$result = fill_array(0, $numErrors, 0);
$e = 0;
for ($i = 1; $i < $this->field->getSize() && $e < $numErrors; $i++) {
if ($errorLocator->evaluateAt($i) == 0) {
$result[$e] = $this->field->inverse($i);
$e++;
}
}
if ($e != $numErrors) {
throw new ReedSolomonException("Error locator degree does not match number of roots");
}
return $result;
}
private function findErrorMagnitudes($errorEvaluator, $errorLocations)
{
// This is directly applying Forney's Formula
$s = count($errorLocations);
$result = fill_array(0, $s, 0);
for ($i = 0; $i < $s; $i++) {
$xiInverse = $this->field->inverse($errorLocations[$i]);
$denominator = 1;
for ($j = 0; $j < $s; $j++) {
if ($i != $j) {
//denominator = field.multiply(denominator,
// GenericGF.addOrSubtract(1, field.multiply(errorLocations[j], xiInverse)));
// Above should work but fails on some Apple and Linux JDKs due to a Hotspot bug.
// Below is a funny-looking workaround from Steven Parkes
$term = $this->field->multiply($errorLocations[$j], $xiInverse);
$termPlus1 = ($term & 0x1) == 0 ? $term | 1 : $term & ~1;
$denominator = $this->field->multiply($denominator, $termPlus1);
}
}
$result[$i] = $this->field->multiply($errorEvaluator->evaluateAt($xiInverse),
$this->field->inverse($denominator));
if ($this->field->getGeneratorBase() != 0) {
$result[$i] = $this->field->multiply($result[$i], $xiInverse);
}
}
return $result;
}
}
@@ -0,0 +1,28 @@
<?php
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Zxing\Common\Reedsolomon;
/**
* <p>Thrown when an exception occurs during Reed-Solomon decoding, such as when
* there are too many errors to correct.</p>
*
* @author Sean Owen
*/
final class ReedSolomonException extends \Exception
{
}
@@ -0,0 +1,101 @@
<?php
if (!function_exists('arraycopy')) {
function arraycopy($srcArray, $srcPos, $destArray, $destPos, $length)
{
$srcArrayToCopy = array_slice($srcArray, $srcPos, $length);
array_splice($destArray, $destPos, $length, $srcArrayToCopy);
return $destArray;
}
}
if (!function_exists('hashCode')) {
function hashCode($s)
{
$h = 0;
$len = strlen($s);
for ($i = 0; $i < $len; $i++) {
$h = (31 * $h + ord($s[$i]));
}
return $h;
}
}
if (!function_exists('numberOfTrailingZeros')) {
function numberOfTrailingZeros($i)
{
if ($i == 0) return 32;
$num = 0;
while (($i & 1) == 0) {
$i >>= 1;
$num++;
}
return $num;
}
}
if (!function_exists('uRShift')) {
function uRShift($a, $b)
{
static $mask = (8 * PHP_INT_SIZE - 1);
if ($b === 0) {
return $a;
}
return ($a >> $b) & ~(1 << $mask >> ($b - 1));
}
}
/*
function sdvig3($num,$count=1){//>>> 32 bit
$s = decbin($num);
$sarray = str_split($s,1);
$sarray = array_slice($sarray,-32);//32bit
for($i=0;$i<=1;$i++) {
array_pop($sarray);
array_unshift($sarray, '0');
}
return bindec(implode($sarray));
}
*/
if (!function_exists('sdvig3')) {
function sdvig3($a, $b)
{
if ($a >= 0) {
return bindec(decbin($a >> $b)); //simply right shift for positive number
}
$bin = decbin($a >> $b);
$bin = substr($bin, $b); // zero fill on the left side
return bindec($bin);
}
}
if (!function_exists('floatToIntBits')) {
function floatToIntBits($float_val)
{
$int = unpack('i', pack('f', $float_val));
return $int[1];
}
}
if (!function_exists('fill_array')) {
function fill_array($index, $count, $value)
{
if ($count <= 0) {
return [0];
}
return array_fill($index, $count, $value);
}
}
@@ -0,0 +1,49 @@
<?php
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Zxing;
/**
* Thrown when a barcode was successfully detected, but some aspect of
* the content did not conform to the barcode's format rules. This could have
* been due to a mis-detection.
*
* @author Sean Owen
*/
final class FormatException extends ReaderException
{
private static $instance;
public function __construct($cause = null)
{
if ($cause) {
parent::__construct($cause);
}
}
public static function getFormatInstance($cause = null)
{
if (!self::$instance) {
self::$instance = new FormatException();
}
if (self::$isStackTrace) {
return new FormatException($cause);
} else {
return self::$instance;
}
}
}
@@ -0,0 +1,176 @@
<?php
namespace Zxing;
/**
* This class is used to help decode images from files which arrive as GD Resource
* It does not support rotation.
*
*
*
*/
final class GDLuminanceSource extends LuminanceSource
{
public $luminances;
private $dataWidth;
private $dataHeight;
private $left;
private $top;
private $gdImage;
public function __construct(
$gdImage,
$dataWidth,
$dataHeight,
$left = null,
$top = null,
$width = null,
$height = null
) {
if (!$left && !$top && !$width && !$height) {
$this->GDLuminanceSource($gdImage, $dataWidth, $dataHeight);
return;
}
parent::__construct($width, $height);
if ($left + $width > $dataWidth || $top + $height > $dataHeight) {
throw new \InvalidArgumentException("Crop rectangle does not fit within image data.");
}
$this->luminances = $gdImage;
$this->dataWidth = $dataWidth;
$this->dataHeight = $dataHeight;
$this->left = $left;
$this->top = $top;
}
public function GDLuminanceSource($gdImage, $width, $height)
{
parent::__construct($width, $height);
$this->dataWidth = $width;
$this->dataHeight = $height;
$this->left = 0;
$this->top = 0;
$this->gdImage = $gdImage;
// In order to measure pure decoding speed, we convert the entire image to a greyscale array
// up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
$this->luminances = [];
//$this->luminances = $this->grayScaleToBitmap($this->grayscale());
$array = [];
$rgb = [];
for ($j = 0; $j < $height; $j++) {
for ($i = 0; $i < $width; $i++) {
$argb = imagecolorat($this->gdImage, $i, $j);
$pixel = imagecolorsforindex($this->gdImage, $argb);
$r = $pixel['red'];
$g = $pixel['green'];
$b = $pixel['blue'];
if ($r == $g && $g == $b) {
// Image is already greyscale, so pick any channel.
$this->luminances[] = $r;//(($r + 128) % 256) - 128;
} else {
// Calculate luminance cheaply, favoring green.
$this->luminances[] = ($r + 2 * $g + $b) / 4;//(((($r + 2 * $g + $b) / 4) + 128) % 256) - 128;
}
}
}
/*
for ($y = 0; $y < $height; $y++) {
$offset = $y * $width;
for ($x = 0; $x < $width; $x++) {
$pixel = $pixels[$offset + $x];
$r = ($pixel >> 16) & 0xff;
$g = ($pixel >> 8) & 0xff;
$b = $pixel & 0xff;
if ($r == $g && $g == $b) {
// Image is already greyscale, so pick any channel.
$this->luminances[(int)($offset + $x)] = (($r+128) % 256) - 128;
} else {
// Calculate luminance cheaply, favoring green.
$this->luminances[(int)($offset + $x)] = (((($r + 2 * $g + $b) / 4)+128)%256) - 128;
}
}
*/
//}
// $this->luminances = $this->grayScaleToBitmap($this->luminances);
}
//@Override
public function getRow($y, $row = null)
{
if ($y < 0 || $y >= $this->getHeight()) {
throw new \InvalidArgumentException('Requested row is outside the image: ' . $y);
}
$width = $this->getWidth();
if ($row == null || count($row) < $width) {
$row = [];
}
$offset = ($y + $this->top) * $this->dataWidth + $this->left;
$row = arraycopy($this->luminances, $offset, $row, 0, $width);
return $row;
}
//@Override
public function getMatrix()
{
$width = $this->getWidth();
$height = $this->getHeight();
// If the caller asks for the entire underlying image, save the copy and give them the
// original data. The docs specifically warn that result.length must be ignored.
if ($width == $this->dataWidth && $height == $this->dataHeight) {
return $this->luminances;
}
$area = $width * $height;
$matrix = [];
$inputOffset = $this->top * $this->dataWidth + $this->left;
// If the width matches the full width of the underlying data, perform a single copy.
if ($width == $this->dataWidth) {
$matrix = arraycopy($this->luminances, $inputOffset, $matrix, 0, $area);
return $matrix;
}
// Otherwise copy one cropped row at a time.
$rgb = $this->luminances;
for ($y = 0; $y < $height; $y++) {
$outputOffset = $y * $width;
$matrix = arraycopy($rgb, $inputOffset, $matrix, $outputOffset, $width);
$inputOffset += $this->dataWidth;
}
return $matrix;
}
//@Override
public function isCropSupported()
{
return true;
}
//@Override
public function crop($left, $top, $width, $height)
{
return new GDLuminanceSource($this->luminances,
$this->dataWidth,
$this->dataHeight,
$this->left + $left,
$this->top + $top,
$width,
$height);
}
}
@@ -0,0 +1,150 @@
<?php
namespace Zxing;
/**
* This class is used to help decode images from files which arrive as GD Resource
* It does not support rotation.
*/
final class IMagickLuminanceSource extends LuminanceSource
{
public $luminances;
private $dataWidth;
private $dataHeight;
private $left;
private $top;
private $image;
public function __construct(
\Imagick $image,
$dataWidth,
$dataHeight,
$left = null,
$top = null,
$width = null,
$height = null
) {
if (!$left && !$top && !$width && !$height) {
$this->_IMagickLuminanceSource($image, $dataWidth, $dataHeight);
return;
}
parent::__construct($width, $height);
if ($left + $width > $dataWidth || $top + $height > $dataHeight) {
throw new \InvalidArgumentException("Crop rectangle does not fit within image data.");
}
$this->luminances = $image;
$this->dataWidth = $dataWidth;
$this->dataHeight = $dataHeight;
$this->left = $left;
$this->top = $top;
}
public function _IMagickLuminanceSource(\Imagick $image, $width, $height)
{
parent::__construct($width, $height);
$this->dataWidth = $width;
$this->dataHeight = $height;
$this->left = 0;
$this->top = 0;
$this->image = $image;
// In order to measure pure decoding speed, we convert the entire image to a greyscale array
// up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
$this->luminances = [];
$image->setImageColorspace(\Imagick::COLORSPACE_GRAY);
// $image->newPseudoImage(0, 0, "magick:rose");
$pixels = $image->exportImagePixels(1, 1, $width, $height, "RGB", \Imagick::PIXEL_CHAR);
$array = [];
$rgb = [];
$countPixels = count($pixels);
for ($i = 0; $i < $countPixels; $i += 3) {
$r = $pixels[$i] & 0xff;
$g = $pixels[$i + 1] & 0xff;
$b = $pixels[$i + 2] & 0xff;
if ($r == $g && $g == $b) {
// Image is already greyscale, so pick any channel.
$this->luminances[] = $r;//(($r + 128) % 256) - 128;
} else {
// Calculate luminance cheaply, favoring green.
$this->luminances[] = ($r + 2 * $g + $b) / 4;//(((($r + 2 * $g + $b) / 4) + 128) % 256) - 128;
}
}
}
//@Override
public function getRow($y, $row = null)
{
if ($y < 0 || $y >= $this->getHeight()) {
throw new \InvalidArgumentException('Requested row is outside the image: ' . $y);
}
$width = $this->getWidth();
if ($row == null || count($row) < $width) {
$row = [];
}
$offset = ($y + $this->top) * $this->dataWidth + $this->left;
$row = arraycopy($this->luminances, $offset, $row, 0, $width);
return $row;
}
//@Override
public function getMatrix()
{
$width = $this->getWidth();
$height = $this->getHeight();
// If the caller asks for the entire underlying image, save the copy and give them the
// original data. The docs specifically warn that result.length must be ignored.
if ($width == $this->dataWidth && $height == $this->dataHeight) {
return $this->luminances;
}
$area = $width * $height;
$matrix = [];
$inputOffset = $this->top * $this->dataWidth + $this->left;
// If the width matches the full width of the underlying data, perform a single copy.
if ($width == $this->dataWidth) {
$matrix = arraycopy($this->luminances, $inputOffset, $matrix, 0, $area);
return $matrix;
}
// Otherwise copy one cropped row at a time.
$rgb = $this->luminances;
for ($y = 0; $y < $height; $y++) {
$outputOffset = $y * $width;
$matrix = arraycopy($rgb, $inputOffset, $matrix, $outputOffset, $width);
$inputOffset += $this->dataWidth;
}
return $matrix;
}
//@Override
public function isCropSupported()
{
return true;
}
//@Override
public function crop($left, $top, $width, $height)
{
return $this->luminances->cropImage($width, $height, $left, $top);
return new GDLuminanceSource($this->luminances,
$this->dataWidth,
$this->dataHeight,
$this->left + $left,
$this->top + $top,
$width,
$height);
}
}

Some files were not shown because too many files have changed in this diff Show More