作者 WangXuebo

ini

正在显示 53 个修改的文件 包含 4825 行增加0 行删除

要显示太多修改。

为保证性能只显示 53 of 53+ 个文件。

  1 +{
  2 + "directory": "public/assets/libs",
  3 + "ignoredDependencies": [
  4 + "es6-promise",
  5 + "file-saver",
  6 + "html2canvas",
  7 + "jspdf",
  8 + "jspdf-autotable",
  9 + "pdfmake"
  10 + ]
  11 +}
  1 +[app]
  2 +debug = false
  3 +trace = false
  4 +
  5 +[database]
  6 +hostname = 127.0.0.1
  7 +database = fastadmin
  8 +username = root
  9 +password = root
  10 +hostport = 3306
  11 +prefix = fa_
  1 +/nbproject/
  2 +/runtime/*
  3 +/public/uploads/*
  4 +.idea
  5 +*.log
  6 +*.css.map
  7 +!.gitkeep
  8 +.env
  9 +.svn
  10 +.vscode
  11 +node_modules
  1 +Apache License
  2 +Version 2.0, January 2004
  3 +http://www.apache.org/licenses/
  4 +
  5 +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
  6 +
  7 +1. Definitions.
  8 +
  9 +"License" shall mean the terms and conditions for use, reproduction, and
  10 +distribution as defined by Sections 1 through 9 of this document.
  11 +
  12 +"Licensor" shall mean the copyright owner or entity authorized by the copyright
  13 +owner that is granting the License.
  14 +
  15 +"Legal Entity" shall mean the union of the acting entity and all other entities
  16 +that control, are controlled by, or are under common control with that entity.
  17 +For the purposes of this definition, "control" means (i) the power, direct or
  18 +indirect, to cause the direction or management of such entity, whether by
  19 +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
  20 +outstanding shares, or (iii) beneficial ownership of such entity.
  21 +
  22 +"You" (or "Your") shall mean an individual or Legal Entity exercising
  23 +permissions granted by this License.
  24 +
  25 +"Source" form shall mean the preferred form for making modifications, including
  26 +but not limited to software source code, documentation source, and configuration
  27 +files.
  28 +
  29 +"Object" form shall mean any form resulting from mechanical transformation or
  30 +translation of a Source form, including but not limited to compiled object code,
  31 +generated documentation, and conversions to other media types.
  32 +
  33 +"Work" shall mean the work of authorship, whether in Source or Object form, made
  34 +available under the License, as indicated by a copyright notice that is included
  35 +in or attached to the work (an example is provided in the Appendix below).
  36 +
  37 +"Derivative Works" shall mean any work, whether in Source or Object form, that
  38 +is based on (or derived from) the Work and for which the editorial revisions,
  39 +annotations, elaborations, or other modifications represent, as a whole, an
  40 +original work of authorship. For the purposes of this License, Derivative Works
  41 +shall not include works that remain separable from, or merely link (or bind by
  42 +name) to the interfaces of, the Work and Derivative Works thereof.
  43 +
  44 +"Contribution" shall mean any work of authorship, including the original version
  45 +of the Work and any modifications or additions to that Work or Derivative Works
  46 +thereof, that is intentionally submitted to Licensor for inclusion in the Work
  47 +by the copyright owner or by an individual or Legal Entity authorized to submit
  48 +on behalf of the copyright owner. For the purposes of this definition,
  49 +"submitted" means any form of electronic, verbal, or written communication sent
  50 +to the Licensor or its representatives, including but not limited to
  51 +communication on electronic mailing lists, source code control systems, and
  52 +issue tracking systems that are managed by, or on behalf of, the Licensor for
  53 +the purpose of discussing and improving the Work, but excluding communication
  54 +that is conspicuously marked or otherwise designated in writing by the copyright
  55 +owner as "Not a Contribution."
  56 +
  57 +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
  58 +of whom a Contribution has been received by Licensor and subsequently
  59 +incorporated within the Work.
  60 +
  61 +2. Grant of Copyright License.
  62 +
  63 +Subject to the terms and conditions of this License, each Contributor hereby
  64 +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
  65 +irrevocable copyright license to reproduce, prepare Derivative Works of,
  66 +publicly display, publicly perform, sublicense, and distribute the Work and such
  67 +Derivative Works in Source or Object form.
  68 +
  69 +3. Grant of Patent License.
  70 +
  71 +Subject to the terms and conditions of this License, each Contributor hereby
  72 +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
  73 +irrevocable (except as stated in this section) patent license to make, have
  74 +made, use, offer to sell, sell, import, and otherwise transfer the Work, where
  75 +such license applies only to those patent claims licensable by such Contributor
  76 +that are necessarily infringed by their Contribution(s) alone or by combination
  77 +of their Contribution(s) with the Work to which such Contribution(s) was
  78 +submitted. If You institute patent litigation against any entity (including a
  79 +cross-claim or counterclaim in a lawsuit) alleging that the Work or a
  80 +Contribution incorporated within the Work constitutes direct or contributory
  81 +patent infringement, then any patent licenses granted to You under this License
  82 +for that Work shall terminate as of the date such litigation is filed.
  83 +
  84 +4. Redistribution.
  85 +
  86 +You may reproduce and distribute copies of the Work or Derivative Works thereof
  87 +in any medium, with or without modifications, and in Source or Object form,
  88 +provided that You meet the following conditions:
  89 +
  90 +You must give any other recipients of the Work or Derivative Works a copy of
  91 +this License; and
  92 +You must cause any modified files to carry prominent notices stating that You
  93 +changed the files; and
  94 +You must retain, in the Source form of any Derivative Works that You distribute,
  95 +all copyright, patent, trademark, and attribution notices from the Source form
  96 +of the Work, excluding those notices that do not pertain to any part of the
  97 +Derivative Works; and
  98 +If the Work includes a "NOTICE" text file as part of its distribution, then any
  99 +Derivative Works that You distribute must include a readable copy of the
  100 +attribution notices contained within such NOTICE file, excluding those notices
  101 +that do not pertain to any part of the Derivative Works, in at least one of the
  102 +following places: within a NOTICE text file distributed as part of the
  103 +Derivative Works; within the Source form or documentation, if provided along
  104 +with the Derivative Works; or, within a display generated by the Derivative
  105 +Works, if and wherever such third-party notices normally appear. The contents of
  106 +the NOTICE file are for informational purposes only and do not modify the
  107 +License. You may add Your own attribution notices within Derivative Works that
  108 +You distribute, alongside or as an addendum to the NOTICE text from the Work,
  109 +provided that such additional attribution notices cannot be construed as
  110 +modifying the License.
  111 +You may add Your own copyright statement to Your modifications and may provide
  112 +additional or different license terms and conditions for use, reproduction, or
  113 +distribution of Your modifications, or for any such Derivative Works as a whole,
  114 +provided Your use, reproduction, and distribution of the Work otherwise complies
  115 +with the conditions stated in this License.
  116 +
  117 +5. Submission of Contributions.
  118 +
  119 +Unless You explicitly state otherwise, any Contribution intentionally submitted
  120 +for inclusion in the Work by You to the Licensor shall be under the terms and
  121 +conditions of this License, without any additional terms or conditions.
  122 +Notwithstanding the above, nothing herein shall supersede or modify the terms of
  123 +any separate license agreement you may have executed with Licensor regarding
  124 +such Contributions.
  125 +
  126 +6. Trademarks.
  127 +
  128 +This License does not grant permission to use the trade names, trademarks,
  129 +service marks, or product names of the Licensor, except as required for
  130 +reasonable and customary use in describing the origin of the Work and
  131 +reproducing the content of the NOTICE file.
  132 +
  133 +7. Disclaimer of Warranty.
  134 +
  135 +Unless required by applicable law or agreed to in writing, Licensor provides the
  136 +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
  137 +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
  138 +including, without limitation, any warranties or conditions of TITLE,
  139 +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
  140 +solely responsible for determining the appropriateness of using or
  141 +redistributing the Work and assume any risks associated with Your exercise of
  142 +permissions under this License.
  143 +
  144 +8. Limitation of Liability.
  145 +
  146 +In no event and under no legal theory, whether in tort (including negligence),
  147 +contract, or otherwise, unless required by applicable law (such as deliberate
  148 +and grossly negligent acts) or agreed to in writing, shall any Contributor be
  149 +liable to You for damages, including any direct, indirect, special, incidental,
  150 +or consequential damages of any character arising as a result of this License or
  151 +out of the use or inability to use the Work (including but not limited to
  152 +damages for loss of goodwill, work stoppage, computer failure or malfunction, or
  153 +any and all other commercial damages or losses), even if such Contributor has
  154 +been advised of the possibility of such damages.
  155 +
  156 +9. Accepting Warranty or Additional Liability.
  157 +
  158 +While redistributing the Work or Derivative Works thereof, You may choose to
  159 +offer, and charge a fee for, acceptance of support, warranty, indemnity, or
  160 +other liability obligations and/or rights consistent with this License. However,
  161 +in accepting such obligations, You may act only on Your own behalf and on Your
  162 +sole responsibility, not on behalf of any other Contributor, and only if You
  163 +agree to indemnify, defend, and hold each Contributor harmless for any liability
  164 +incurred by, or claims asserted against, such Contributor by reason of your
  165 +accepting any such warranty or additional liability.
  166 +
  167 +END OF TERMS AND CONDITIONS
  168 +
  169 +APPENDIX: How to apply the Apache License to your work
  170 +
  171 +To apply the Apache License to your work, attach the following boilerplate
  172 +notice, with the fields enclosed by brackets "{}" replaced with your own
  173 +identifying information. (Don't include the brackets!) The text should be
  174 +enclosed in the appropriate comment syntax for the file format. We also
  175 +recommend that a file or class name and description of purpose be included on
  176 +the same "printed page" as the copyright notice for easier identification within
  177 +third-party archives.
  178 +
  179 + Copyright 2017 Karson
  180 +
  181 + Licensed under the Apache License, Version 2.0 (the "License");
  182 + you may not use this file except in compliance with the License.
  183 + You may obtain a copy of the License at
  184 +
  185 + http://www.apache.org/licenses/LICENSE-2.0
  186 +
  187 + Unless required by applicable law or agreed to in writing, software
  188 + distributed under the License is distributed on an "AS IS" BASIS,
  189 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  190 + See the License for the specific language governing permissions and
  191 + limitations under the License.
  1 +FastAdmin是一款基于ThinkPHP+Bootstrap的极速后台开发框架。
  2 +
  3 +
  4 +## 主要特性
  5 +
  6 +* 基于`Auth`验证的权限管理系统
  7 + * 支持无限级父子级权限继承,父级的管理员可任意增删改子级管理员及权限设置
  8 + * 支持单管理员多角色
  9 + * 支持管理子级数据或个人数据
  10 +* 强大的一键生成功能
  11 + * 一键生成CRUD,包括控制器、模型、视图、JS、语言包、菜单、回收站等
  12 + * 一键压缩打包JS和CSS文件,一键CDN静态资源部署
  13 + * 一键生成控制器菜单和规则
  14 + * 一键生成API接口文档
  15 +* 完善的前端功能组件开发
  16 + * 基于`AdminLTE`二次开发
  17 + * 基于`Bootstrap`开发,自适应手机、平板、PC
  18 + * 基于`RequireJS`进行JS模块管理,按需加载
  19 + * 基于`Less`进行样式开发
  20 +* 强大的插件扩展功能,在线安装卸载升级插件
  21 +* 通用的会员模块和API模块
  22 +* 共用同一账号体系的Web端会员中心权限验证和API接口会员权限验证
  23 +* 二级域名部署支持,同时域名支持绑定到应用插件
  24 +* 多语言支持,服务端及客户端支持
  25 +* 支持大文件分片上传、剪切板粘贴上传、拖拽上传,进度条显示,图片上传前压缩
  26 +* 支持表格固定列、固定表头、跨页选择、Excel导出、模板渲染等功能
  27 +* 强大的第三方应用模块支持([CMS](https://www.fastadmin.net/store/cms.html)[博客](https://www.fastadmin.net/store/blog.html)[知识付费问答](https://www.fastadmin.net/store/ask.html)[在线投票系统](https://www.fastadmin.net/store/vote.html)[B2C商城](https://www.fastadmin.net/store/shopro.html)[B2B2C商城](https://www.fastadmin.net/store/wanlshop.html))
  28 +* 支持CMS、博客、知识付费问答无缝整合[Xunsearch全文搜索](https://www.fastadmin.net/store/xunsearch.html)
  29 +* 第三方小程序支持([CMS小程序](https://www.fastadmin.net/store/cms.html)[预订小程序](https://www.fastadmin.net/store/ball.html)[问答小程序](https://www.fastadmin.net/store/ask.html)[点餐小程序](https://www.fastadmin.net/store/unidrink.html)[B2C小程序](https://www.fastadmin.net/store/shopro.html)[B2B2C小程序](https://www.fastadmin.net/store/wanlshop.html)[博客小程序](https://www.fastadmin.net/store/blog.html))
  30 +* 整合第三方短信接口(阿里云、腾讯云短信)
  31 +* 无缝整合第三方云存储(七牛云、阿里云OSS、又拍云)功能,支持云储存分片上传
  32 +* 第三方富文本编辑器支持(Summernote、Kindeditor、百度编辑器)
  33 +* 第三方登录(QQ、微信、微博)整合
  34 +* 第三方支付(微信、支付宝)无缝整合,微信支持PC端扫码支付
  35 +* 丰富的插件应用市场
  36 +
  37 +## 安装使用
  38 +
  39 +https://doc.fastadmin.net
  40 +
  41 +## 在线演示
  42 +
  43 +https://demo.fastadmin.net
  44 +
  45 +用户名:admin
  46 +
  47 +密 码:123456
  48 +
  49 +提 示:演示站数据无法进行修改,请下载源码安装体验全部功能
  50 +
  51 +## 界面截图
  52 +![控制台](https://images.gitee.com/uploads/images/2020/0929/202947_8db2d281_10933.gif "控制台")
  53 +
  54 +## 问题反馈
  55 +
  56 +在使用中有任何问题,请使用以下联系方式联系我们
  57 +
  58 +交流社区: https://ask.fastadmin.net
  59 +
  60 +QQ群: [636393962](https://jq.qq.com/?_wv=1027&k=487PNBb)() [708784003](https://jq.qq.com/?_wv=1027&k=5ObjtwM)(满) [964776039](https://jq.qq.com/?_wv=1027&k=59qjU2P)(3群) [749803490](https://jq.qq.com/?_wv=1027&k=5tczi88)(满) [767103006](https://jq.qq.com/?_wv=1027&k=5Z1U751)() [675115483](https://jq.qq.com/?_wv=1027&k=54I6mts)(6群)
  61 +
  62 +Github: https://github.com/karsonzhang/fastadmin
  63 +
  64 +Gitee: https://gitee.com/karson/fastadmin
  65 +
  66 +## 特别鸣谢
  67 +
  68 +感谢以下的项目,排名不分先后
  69 +
  70 +ThinkPHP:http://www.thinkphp.cn
  71 +
  72 +AdminLTE:https://adminlte.io
  73 +
  74 +Bootstrap:http://getbootstrap.com
  75 +
  76 +jQuery:http://jquery.com
  77 +
  78 +Bootstrap-table:https://github.com/wenzhixin/bootstrap-table
  79 +
  80 +Nice-validator: https://validator.niceue.com
  81 +
  82 +SelectPage: https://github.com/TerryZ/SelectPage
  83 +
  84 +Layer: https://layer.layui.com
  85 +
  86 +DropzoneJS: https://www.dropzonejs.com
  87 +
  88 +
  89 +## 版权信息
  90 +
  91 +FastAdmin遵循Apache2开源协议发布,并提供免费使用。
  92 +
  93 +本项目包含的第三方源码和二进制文件之版权信息另行标注。
  94 +
  95 +版权所有Copyright © 2017-2022 by FastAdmin (https://www.fastadmin.net)
  96 +
  97 +All rights reserved。
  1 +deny from all
  1 +{"files":["application\\admin\\controller\\Command.php","application\\admin\\lang\\zh-cn\\command.php","application\\admin\\model\\Command.php","application\\admin\\validate\\Command.php","application\\admin\\view\\command\\add.html","application\\admin\\view\\command\\detail.html","application\\admin\\view\\command\\index.html","public\\assets\\js\\backend\\command.js"],"license":"regular","licenseto":"10789","licensekey":"cF9I6tYjn1ORXeDu grOxaQyT3NGbyAOZYnc3Og==","domains":["chexiubao.cc"],"licensecodes":[],"validations":["aaf3e3478c1f9ee1bcf7bc25025eb678"],"menus":["command","command\/index","command\/add","command\/detail","command\/execute","command\/del","command\/multi"]}
  1 +<?php
  2 +
  3 +namespace addons\command;
  4 +
  5 +use app\common\library\Menu;
  6 +use think\Addons;
  7 +
  8 +/**
  9 + * 在线命令插件
  10 + */
  11 +class Command extends Addons
  12 +{
  13 +
  14 + /**
  15 + * 插件安装方法
  16 + * @return bool
  17 + */
  18 + public function install()
  19 + {
  20 + $menu = [
  21 + [
  22 + 'name' => 'command',
  23 + 'title' => '在线命令管理',
  24 + 'icon' => 'fa fa-terminal',
  25 + 'sublist' => [
  26 + ['name' => 'command/index', 'title' => '查看'],
  27 + ['name' => 'command/add', 'title' => '添加'],
  28 + ['name' => 'command/detail', 'title' => '详情'],
  29 + ['name' => 'command/execute', 'title' => '运行'],
  30 + ['name' => 'command/del', 'title' => '删除'],
  31 + ['name' => 'command/multi', 'title' => '批量更新'],
  32 + ]
  33 + ]
  34 + ];
  35 + Menu::create($menu);
  36 + return true;
  37 + }
  38 +
  39 + /**
  40 + * 插件卸载方法
  41 + * @return bool
  42 + */
  43 + public function uninstall()
  44 + {
  45 + Menu::delete('command');
  46 + return true;
  47 + }
  48 +
  49 + /**
  50 + * 插件启用方法
  51 + * @return bool
  52 + */
  53 + public function enable()
  54 + {
  55 + Menu::enable('command');
  56 + return true;
  57 + }
  58 +
  59 + /**
  60 + * 插件禁用方法
  61 + * @return bool
  62 + */
  63 + public function disable()
  64 + {
  65 + Menu::disable('command');
  66 + return true;
  67 + }
  68 +
  69 +}
  1 +<?php
  2 +
  3 +return [
  4 +];
  1 +<?php
  2 +
  3 +namespace addons\command\controller;
  4 +
  5 +use think\addons\Controller;
  6 +
  7 +class Index extends Controller
  8 +{
  9 +
  10 + public function index()
  11 + {
  12 + $this->error("当前插件暂无前台页面");
  13 + }
  14 +
  15 +}
  1 +name = command
  2 +title = 在线命令
  3 +intro = 可在线执行FastAdmin的命令行相关命令
  4 +author = Karson
  5 +website = https://www.fastadmin.net
  6 +version = 1.0.6
  7 +state = 1
  8 +url = /addons/command
  9 +license = regular
  10 +licenseto = 10789
  1 +CREATE TABLE IF NOT EXISTS `__PREFIX__command` (
  2 + `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID',
  3 + `type` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '类型',
  4 + `params` varchar(1500) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '参数',
  5 + `command` varchar(1500) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '命令',
  6 + `content` text COMMENT '返回结果',
  7 + `executetime` int(10) UNSIGNED DEFAULT NULL COMMENT '执行时间',
  8 + `createtime` int(10) UNSIGNED DEFAULT NULL COMMENT '创建时间',
  9 + `updatetime` int(10) UNSIGNED DEFAULT NULL COMMENT '更新时间',
  10 + `status` enum('successed','failured') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'failured' COMMENT '状态',
  11 + PRIMARY KEY (`id`) USING BTREE
  12 +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '在线命令表';
  1 +<?php
  2 +
  3 +namespace addons\command\library;
  4 +
  5 +/**
  6 + * Class Output
  7 + */
  8 +class Output extends \think\console\Output
  9 +{
  10 +
  11 + protected $message = [];
  12 +
  13 + public function __construct($driver = 'console')
  14 + {
  15 + parent::__construct($driver);
  16 + }
  17 +
  18 + protected function block($style, $message)
  19 + {
  20 + $this->message[] = $message;
  21 + }
  22 +
  23 + public function getMessage()
  24 + {
  25 + return $this->message;
  26 + }
  27 +
  28 +}
  1 +{"files":["application\\admin\\controller\\Epay.php","public\\assets\\addons\\epay\\css\\common.css","public\\assets\\addons\\epay\\css\\epay.css","public\\assets\\addons\\epay\\images\\alipay.png","public\\assets\\addons\\epay\\images\\expired.png","public\\assets\\addons\\epay\\images\\logo-alipay.png","public\\assets\\addons\\epay\\images\\logo-wechat.png","public\\assets\\addons\\epay\\images\\paid.png","public\\assets\\addons\\epay\\images\\scan.png","public\\assets\\addons\\epay\\images\\screenshot-alipay.png","public\\assets\\addons\\epay\\images\\screenshot-wechat.png","public\\assets\\addons\\epay\\images\\wechat.png","public\\assets\\addons\\epay\\js\\common.js","public\\assets\\addons\\epay\\less\\common.less","public\\assets\\addons\\epay\\less\\epay.less"],"license":"regular","licenseto":"10789","licensekey":"ORU0McGKN6smzaS8 OxO4Mbfr73WG\/vizWncVQg==","domains":["chexiubao.cc"],"licensecodes":[],"validations":["aaf3e3478c1f9ee1bcf7bc25025eb678"]}
  1 +<?php
  2 +
  3 +namespace addons\epay;
  4 +
  5 +use think\Addons;
  6 +use think\Config;
  7 +use think\Loader;
  8 +
  9 +/**
  10 + * 微信支付宝整合插件
  11 + */
  12 +class Epay extends Addons
  13 +{
  14 +
  15 + /**
  16 + * 插件安装方法
  17 + * @return bool
  18 + */
  19 + public function install()
  20 + {
  21 +
  22 + return true;
  23 + }
  24 +
  25 + /**
  26 + * 插件卸载方法
  27 + * @return bool
  28 + */
  29 + public function uninstall()
  30 + {
  31 +
  32 + return true;
  33 + }
  34 +
  35 + /**
  36 + * 插件启用方法
  37 + * @return bool
  38 + */
  39 + public function enable()
  40 + {
  41 +
  42 + return true;
  43 + }
  44 +
  45 + /**
  46 + * 插件禁用方法
  47 + * @return bool
  48 + */
  49 + public function disable()
  50 + {
  51 +
  52 + return true;
  53 + }
  54 +
  55 + /**
  56 + * 添加命名空间
  57 + */
  58 + public function appInit()
  59 + {
  60 + //添加命名空间
  61 + if (!class_exists('\Yansongda\Pay\Pay')) {
  62 + Loader::addNamespace('Yansongda\Pay', ADDON_PATH . 'epay' . DS . 'library' . DS . 'Yansongda' . DS . 'Pay' . DS);
  63 + }
  64 + if (!class_exists('\Yansongda\Supports\Logger')) {
  65 + Loader::addNamespace('Yansongda\Supports', ADDON_PATH . 'epay' . DS . 'library' . DS . 'Yansongda' . DS . 'Supports' . DS);
  66 + }
  67 + }
  68 +
  69 +}
  1 +-----BEGIN CERTIFICATE-----
  2 +MIID9jCCAt6gAwIBAgIUEkn92qxrC2XTPxnol9yW7/eOM84wDQYJKoZIhvcNAQEL
  3 +BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
  4 +FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
  5 +Q0EwHhcNMjIwNTE5MDcwNzA5WhcNMjcwNTE4MDcwNzA5WjCBhzETMBEGA1UEAwwK
  6 +MTYyNDc5MDc3MDEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMTMwMQYDVQQL
  7 +DCrljJfkuqzkuK3kv53kvJflsqnnvZHnu5znp5HmioDmnInpmZDlhazlj7gxCzAJ
  8 +BgNVBAYMAkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQAD
  9 +ggEPADCCAQoCggEBAMi4qudLfIL5WJeYE0tX+H/GsvBtmnzU5BKVA658msgaM3F2
  10 +oAqQDSzI1L0EpneW9LI+N2rz3OyiNGks2nAOAzkHF69BYVmWTf7Ylo3okiW+r2RR
  11 +v/OVTPHvOFYdKkHwNaTntFbCIxUtFEV0q76z96ZHDs605WYhWYRMtqxi+/ijgq1f
  12 +njWVktD8Lbezi9bHXK1LXyx/FdYwEdkD3f0Y5IIhLT47Aad0af2RqjXOR/eqA7xe
  13 +7AK2lAUe8ujEMSt5hR80t1R1/+5OAyovVsn/GkGdgcW6scKZ0BO9UKhkfq6A+a8m
  14 +gSmmXGcfCQuWTZrBKFWBtQUNm4Ks3k8C0B9UC0UCAwEAAaOBgTB/MAkGA1UdEwQC
  15 +MAAwCwYDVR0PBAQDAgTwMGUGA1UdHwReMFwwWqBYoFaGVGh0dHA6Ly9ldmNhLml0
  16 +cnVzLmNvbS5jbi9wdWJsaWMvaXRydXNjcmw/Q0E9MUJENDIyMEU1MERCQzA0QjA2
  17 +QUQzOTc1NDk4NDZDMDFDM0U4RUJEMjANBgkqhkiG9w0BAQsFAAOCAQEAI37mG5ip
  18 +wgueNqPb/eVNVeaBbHeXv2Ac6LRHw9iKMhF8HRFDh/bj45MEnbCKJlSvPt834Axs
  19 +uMNkEK4/+tr6JeNkPSMg4krMlyA4fiKm04P3fdIf4XYuHTooKuUNoU5zLfgZZ+2l
  20 +po3ElZBaqE95C/OOwNdgKm8X2uTig0Uzn+aFExXxz4UVU1Sb3UADFYhrBKBuD5aK
  21 +tc/cF1OE5eHvHOi/7C5pkDXCuni4BCWMzrZTSLV6tAmSPiQxwIl6ctC3Jk/voa+c
  22 +JL/hleqrcf3Y4DOBLLcH14fpijAlT2Hg3GLVsP5cUP1gTl2Q/omv2ySjAGTLJ7x0
  23 +EpV/v/n06y+Rsw==
  24 +-----END CERTIFICATE-----
  1 +-----BEGIN PRIVATE KEY-----
  2 +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDIuKrnS3yC+ViX
  3 +mBNLV/h/xrLwbZp81OQSlQOufJrIGjNxdqAKkA0syNS9BKZ3lvSyPjdq89zsojRp
  4 +LNpwDgM5BxevQWFZlk3+2JaN6JIlvq9kUb/zlUzx7zhWHSpB8DWk57RWwiMVLRRF
  5 +dKu+s/emRw7OtOVmIVmETLasYvv4o4KtX541lZLQ/C23s4vWx1ytS18sfxXWMBHZ
  6 +A939GOSCIS0+OwGndGn9kao1zkf3qgO8XuwCtpQFHvLoxDEreYUfNLdUdf/uTgMq
  7 +L1bJ/xpBnYHFurHCmdATvVCoZH6ugPmvJoEpplxnHwkLlk2awShVgbUFDZuCrN5P
  8 +AtAfVAtFAgMBAAECggEAdoPTJU6E7jCcBqZcfhgrOgbwkNWmah6pq+8ghV49ZflS
  9 +dqQ8SwBTRyYtto2QXPxc7wGdZyEmSPJdT9yT/aXIcVumuGw6NtNwqhDolFHGOVMl
  10 +7G34kLKPBCrjcLU3xkc25W+KI8/tNwuu9HO3FJreludN7u8QpAT8/gjts0uey2dz
  11 +5jUbSYAtrtaZn7r+fcGVT+8bEySW569CccODfE85YCDyUoDPf5dQUsT16zzO5ru9
  12 +OsiS9eXyb+ojybaOcvIkXciiS3nt38zeZWG09Dg4gQI4LQRVKe8TYz/mIo5JnXNU
  13 +r5zK/5xKFLnvhMiZ3GW5rZvoHJpGTGgnIpmmc3BTAQKBgQDy07YHyJL1kBJO3U3B
  14 +Q9nhegMxp49/BkCqYkhmy9z7UhYAueo5Ht+zOgkU9vPl6c7DaIDwQz9KSb6iHVci
  15 +lhWt0weBmJi/Xd+uUIT/3HnivQpvf6piqbQWYsTUNBOwkbXMQ1HckczBJiWlRVcC
  16 +FoZWqNJLYy8pcx+vnhmdvDqmCQKBgQDTnDWeRJrwWleiee/ipmdZGzl/YUQ0BnXf
  17 +mP0O7jZsBgFrXo8dvZ63plYxiGa6GTfKoaEHPinNlUroxVL2tEtk35f1uEpSjCA5
  18 +vNIEpICHNyBtXQDkrQHmPTN9IEs4ajDVOmN4os9vAGDvxT2py+S44ifixmywkwdB
  19 +/Zg2zJZqXQKBgD+8lkEAwqI2CLgummQqHRxz3DbCVoXu7DZMydhBlmVZLHFQW0xW
  20 +8Dyo9Q1NrMMiVaUw3vcxj4E+Tg1bSi3H2zTe+U9o8F76kuW8OMy34D5FhGGzztNK
  21 +2/wsPKG+cZSdCmztFqWgl2/zRVpWEvp3UxeYBGrJQ+3LS/vasbqXV4WRAoGAfTjN
  22 +SfETNvRfM5iw6hDKy6IerlBiyd6g1kZ0SeeRcO9E28LDMjhJuifGjd1H3QybRV/M
  23 +KLsShcO45nIOnEso6nKePBxobtzZHRGtvm2SWXG30L/ejXQL0bGZzIvVt3avmx7I
  24 +SlUeJ3Oz6W9xBA/SM84xCDOfkgYPn29wKc17nUkCgYAmRguhI8XfQgv8E+hw2nXj
  25 +HWKYRzjNyACOkMPwrMM2r8S+LyCP0vBteLEEHOOXLPEKgRSYRp/AVEaW0e/1gtmb
  26 +PMYSudQhzDdSa0cKjewkOxsZqifhu7niaxFECWUIF/QVoo6Bt3rT4sNqwEwImuaB
  27 +VusoS8dP08UjpGojgl+9lQ==
  28 +-----END PRIVATE KEY-----
  1 +<form id="config-form" class="edit-form form-horizontal" role="form" data-toggle="validator" method="POST" action="">
  2 +
  3 + <div class="panel panel-default panel-intro">
  4 + <div class="panel-heading">
  5 + <ul class="nav nav-tabs nav-group">
  6 + <li class="active"><a href="#wechat" data-toggle="tab">微信支付</a></li>
  7 + <li><a href="#alipay" data-toggle="tab">支付宝</a></li>
  8 + </ul>
  9 + </div>
  10 +
  11 + <div class="panel-body">
  12 + <div id="myTabContent" class="tab-content">
  13 + {foreach $addon.config as $item}
  14 + {if $item.name=='wechat'}
  15 + <div class="tab-pane fade active in" id="wechat">
  16 + <table class="table table-striped table-config">
  17 + <tbody>
  18 + <tr>
  19 + <td width="15%">APP appid</td>
  20 + <td>
  21 + <div class="row">
  22 + <div class="col-sm-8 col-xs-12">
  23 + <input type="text" name="row[wechat][appid]" value="{$item.value.appid|default=''}" class="form-control" data-rule="" data-tip="APP应用中支付时使用"/>
  24 + </div>
  25 + <div class="col-sm-4"></div>
  26 + </div>
  27 + </td>
  28 + </tr>
  29 + <tr>
  30 + <td>公众号的app_id</td>
  31 + <td>
  32 + <div class="row">
  33 + <div class="col-sm-8 col-xs-12">
  34 + <input type="text" name="row[wechat][app_id]" value="{$item.value.app_id|default=''}" class="form-control" data-rule="" data-tip="公众号支付必须"/>
  35 + </div>
  36 + <div class="col-sm-4"></div>
  37 + </div>
  38 + </td>
  39 + </tr>
  40 + <tr>
  41 + <td>公众号的app_secret</td>
  42 + <td>
  43 + <div class="row">
  44 + <div class="col-sm-8 col-xs-12">
  45 + <input type="text" name="row[wechat][app_secret]" value="{$item.value.app_secret|default=''}" class="form-control" data-rule="" data-tip="仅在需要获取Openid时使用,一般情况下为空"/>
  46 + </div>
  47 + <div class="col-sm-4"></div>
  48 + </div>
  49 + </td>
  50 + </tr>
  51 + <tr>
  52 + <td>小程序的app_id</td>
  53 + <td>
  54 + <div class="row">
  55 + <div class="col-sm-8 col-xs-12">
  56 + <input type="text" name="row[wechat][miniapp_id]" value="{$item.value.miniapp_id|default=''}" class="form-control" data-rule="" data-tip="仅在小程序支付时使用"/>
  57 + </div>
  58 + <div class="col-sm-4"></div>
  59 + </div>
  60 + </td>
  61 + </tr>
  62 + <tr>
  63 + <td>微信支付商户号ID</td>
  64 + <td>
  65 + <div class="row">
  66 + <div class="col-sm-8 col-xs-12">
  67 + <input type="text" name="row[wechat][mch_id]" value="{$item.value.mch_id|default=''}" class="form-control" data-rule="" data-tip=""/>
  68 + </div>
  69 + <div class="col-sm-4"></div>
  70 + </div>
  71 + </td>
  72 + </tr>
  73 + <tr>
  74 + <td>微信支付商户的密钥</td>
  75 + <td>
  76 + <div class="row">
  77 + <div class="col-sm-8 col-xs-12">
  78 + <input type="text" name="row[wechat][key]" value="{$item.value.key|default=''}" class="form-control" data-rule="" data-tip=""/>
  79 + </div>
  80 + <div class="col-sm-4"></div>
  81 + </div>
  82 + </td>
  83 + </tr>
  84 + <tr>
  85 + <td>支付模式</td>
  86 + <td>
  87 + <div class="row">
  88 + <div class="col-sm-8 col-xs-12">
  89 + {:Form::radios('row[wechat][mode]',['normal'=>'正式环境','dev'=>'沙箱环境','service'=>'服务商模式'],$item.value.mode??'normal')}
  90 + </div>
  91 + <div class="col-sm-4"></div>
  92 + </div>
  93 + </td>
  94 + </tr>
  95 + <tr data-type="service" class="{:$item.value.mode!='service'?'hidden':''}">
  96 + <td>子商户商户号ID</td>
  97 + <td>
  98 + <div class="row">
  99 + <div class="col-sm-8 col-xs-12">
  100 + <input type="text" name="row[wechat][sub_mch_id]" value="{$item.value.sub_mch_id|default=''}" class="form-control" data-rule="" data-tip="如果未用到子商户,请勿填写"/>
  101 + </div>
  102 + <div class="col-sm-4"></div>
  103 + </div>
  104 + </td>
  105 + </tr>
  106 + <tr data-type="service" class="{:$item.value.mode!='service'?'hidden':''}">
  107 + <td>子商户 APP appid</td>
  108 + <td>
  109 + <div class="row">
  110 + <div class="col-sm-8 col-xs-12">
  111 + <input type="text" name="row[wechat][sub_appid]" value="{$item.value.sub_appid|default=''}" class="form-control" data-rule="" data-tip="如果未用到子商户,请勿填写"/>
  112 + </div>
  113 + <div class="col-sm-4"></div>
  114 + </div>
  115 + </td>
  116 + </tr>
  117 + <tr data-type="service" class="{:$item.value.mode!='service'?'hidden':''}">
  118 + <td>子商户公众号的appid</td>
  119 + <td>
  120 + <div class="row">
  121 + <div class="col-sm-8 col-xs-12">
  122 + <input type="text" name="row[wechat][sub_app_id]" value="{$item.value.sub_app_id|default=''}" class="form-control" data-rule="" data-tip="如果未用到子商户,请勿填写"/>
  123 + </div>
  124 + <div class="col-sm-4"></div>
  125 + </div>
  126 + </td>
  127 + </tr>
  128 + <tr data-type="service" class="{:$item.value.mode!='service'?'hidden':''}">
  129 + <td>子商户小程序的appid</td>
  130 + <td>
  131 + <div class="row">
  132 + <div class="col-sm-8 col-xs-12">
  133 + <input type="text" name="row[wechat][sub_miniapp_id]" value="{$item.value.sub_miniapp_id|default=''}" class="form-control" data-rule="" data-tip="如果未用到子商户,请勿填写"/>
  134 + </div>
  135 + <div class="col-sm-4"></div>
  136 + </div>
  137 + </td>
  138 + </tr>
  139 + <tr>
  140 + <td>回调通知地址</td>
  141 + <td>
  142 + <div class="row">
  143 + <div class="col-sm-8 col-xs-12">
  144 + <input type="text" name="row[wechat][notify_url]" value="{$item.value.notify_url|default=''}" class="form-control" data-rule="" data-tip="请勿随意修改,实际以逻辑代码中请求的为准"/>
  145 + </div>
  146 + <div class="col-sm-4"></div>
  147 + </div>
  148 + </td>
  149 + </tr>
  150 + <tr>
  151 + <td>微信支付API证书cert</td>
  152 + <td>
  153 + <div class="row">
  154 + <div class="col-sm-8 col-xs-12">
  155 + <div class="input-group">
  156 + <input id="c-cert_client" class="form-control" size="50" name="row[wechat][cert_client]" type="text" value="{$item.value.cert_client|htmlentities}" data-tip="可选, 仅在退款、红包等情况时需要用到">
  157 + <div class="input-group-addon no-border no-padding">
  158 + <span><button type="button" id="faupload-cert_client" class="btn btn-danger faupload" data-url="epay/upload" data-multipart='{"certname":"cert_client"}' data-mimetype="pem" data-input-id="c-cert_client" data-multiple="false"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
  159 + </div>
  160 + <span class="msg-box n-right" for="c-cert_client"></span>
  161 + </div>
  162 + <div style="margin-top:5px;"><a href="https://pay.weixin.qq.com" target="_blank"><i class="fa fa-question-circle"></i> 如何获取微信支付API证书?</a></div>
  163 + </div>
  164 + <div class="col-sm-4"></div>
  165 + </div>
  166 + </td>
  167 + </tr>
  168 + <tr>
  169 + <td>微信支付API证书key</td>
  170 + <td>
  171 + <div class="row">
  172 + <div class="col-sm-8 col-xs-12">
  173 + <div class="input-group">
  174 + <input id="c-cert_key" class="form-control" size="50" name="row[wechat][cert_key]" type="text" value="{$item.value.cert_key|htmlentities}" data-tip="可选, 仅在退款、红包等情况时需要用到">
  175 + <div class="input-group-addon no-border no-padding">
  176 + <span><button type="button" id="faupload-cert_key" class="btn btn-danger faupload" data-url="epay/upload" data-multipart='{"certname":"cert_key"}' data-mimetype="pem" data-input-id="c-cert_key" data-multiple="false"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
  177 + </div>
  178 + <span class="msg-box n-right" for="c-cert_key"></span>
  179 + </div>
  180 + <div style="margin-top:5px;"><a href="https://pay.weixin.qq.com" target="_blank"><i class="fa fa-question-circle"></i> 如何获取微信支付API证书?</a></div>
  181 + </div>
  182 + <div class="col-sm-4"></div>
  183 + </div>
  184 + </td>
  185 + </tr>
  186 +
  187 + <tr>
  188 + <td>记录日志</td>
  189 + <td>
  190 + <div class="row">
  191 + <div class="col-sm-8 col-xs-12">
  192 + {:Form::radios('row[wechat][log]',['1'=>'开启','0'=>'关闭'],$item.value.log)}
  193 + </div>
  194 + <div class="col-sm-4"></div>
  195 + </div>
  196 + </td>
  197 + </tr>
  198 + </tbody>
  199 + </table>
  200 + </div>
  201 + {elseif $item.name=='alipay'}
  202 + <div class="tab-pane fade" id="alipay">
  203 + <table class="table table-striped table-config">
  204 + <tbody>
  205 + <tr>
  206 + <td width="15%">应用ID(app_id)</td>
  207 + <td>
  208 + <div class="row">
  209 + <div class="col-sm-8 col-xs-12">
  210 + <input type="text" name="row[alipay][app_id]" value="{$item.value.app_id|default=''}" class="form-control" data-rule="" data-tip=""/>
  211 + </div>
  212 + <div class="col-sm-4"></div>
  213 + </div>
  214 + </td>
  215 + </tr>
  216 + <tr>
  217 + <td>支付模式</td>
  218 + <td>
  219 + <div class="row">
  220 + <div class="col-sm-8 col-xs-12">
  221 + {:Form::radios('row[alipay][mode]',['normal'=>'正式环境','dev'=>'沙箱环境'],$item.value.mode??'normal')}
  222 + </div>
  223 + <div class="col-sm-4"></div>
  224 + </div>
  225 + </td>
  226 + </tr>
  227 + <tr>
  228 + <td>回调通知地址</td>
  229 + <td>
  230 + <div class="row">
  231 + <div class="col-sm-8 col-xs-12">
  232 + <input type="text" name="row[alipay][notify_url]" value="{$item.value.notify_url|default=''}" class="form-control" data-rule="" data-tip="请勿随意修改,实际以逻辑代码中请求的为准"/>
  233 + </div>
  234 + <div class="col-sm-4"></div>
  235 + </div>
  236 + </td>
  237 + </tr>
  238 + <tr>
  239 + <td>支付跳转地址</td>
  240 + <td>
  241 + <div class="row">
  242 + <div class="col-sm-8 col-xs-12">
  243 + <input type="text" name="row[alipay][return_url]" value="{$item.value.return_url|default=''}" class="form-control" data-rule="" data-tip="请勿随意修改,实际以逻辑代码中请求的为准"/>
  244 + </div>
  245 + <div class="col-sm-4"></div>
  246 + </div>
  247 + </td>
  248 + </tr>
  249 + <tr>
  250 + <td>应用私钥(private_key)</td>
  251 + <td>
  252 + <div class="row">
  253 + <div class="col-sm-8 col-xs-12">
  254 + <input type="text" name="row[alipay][private_key]" value="{$item.value.private_key|default=''}" class="form-control" data-rule="" />
  255 + <div style="margin-top:5px;"><a href="https://opensupport.alipay.com/support/helpcenter/207/201602469554" target="_blank"><i class="fa fa-question-circle"></i> 如何获取应用私钥?</a></div>
  256 + </div>
  257 + <div class="col-sm-4"></div>
  258 + </div>
  259 + </td>
  260 + </tr>
  261 + <tr>
  262 + <td>支付宝公钥路径(ali_public_key)</td>
  263 + <td>
  264 + <div class="row">
  265 + <div class="col-sm-8 col-xs-12">
  266 + <div class="input-group">
  267 + <input id="c-ali_public_key" class="form-control" size="50" name="row[alipay][ali_public_key]" type="text" value="{$item.value.ali_public_key|default=''|htmlentities}" placeholder="公钥请直接粘贴,公钥证书请点击右侧的上传">
  268 + <div class="input-group-addon no-border no-padding">
  269 + <span><button type="button" id="faupload-ali_public_key" class="btn btn-danger faupload" data-url="epay/upload" data-multipart='{"certname":"ali_public_key"}' data-mimetype="crt" data-input-id="c-ali_public_key" data-multiple="false"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
  270 + </div>
  271 + <span class="msg-box n-right" for="c-ali_public_key"></span>
  272 + </div>
  273 + <div style="margin-top:5px;"><a href="javascript:" data-toggle="tooltip" data-title="如果要使用转账、提现功能,则必须使用公钥证书"> <i class="fa fa-info-circle"></i> 公钥和公钥证书说明</a> <a href="https://opensupport.alipay.com/support/helpcenter/207/201602471154" target="_blank"><i class="fa fa-question-circle"></i> 如何获取支付宝公钥证书?</a></div>
  274 + </div>
  275 + <div class="col-sm-4"></div>
  276 + </div>
  277 + </td>
  278 + </tr>
  279 + <tr>
  280 + <td>应用公钥证书路径(app_cert_public_key)</td>
  281 + <td>
  282 + <div class="row">
  283 + <div class="col-sm-8 col-xs-12">
  284 + <div class="input-group">
  285 + <input id="c-app_cert_public_key" class="form-control" size="50" name="row[alipay][app_cert_public_key]" type="text" value="{$item.value.app_cert_public_key|default=''|htmlentities}">
  286 + <div class="input-group-addon no-border no-padding">
  287 + <span><button type="button" id="faupload-app_cert_public_key" class="btn btn-danger faupload" data-url="epay/upload" data-multipart='{"certname":"app_cert_public_key"}' data-mimetype="crt" data-input-id="c-app_cert_public_key" data-multiple="false"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
  288 + </div>
  289 + <span class="msg-box n-right" for="c-app_cert_public_key"></span>
  290 + </div>
  291 + <div style="margin-top:5px;"><a href="https://opensupport.alipay.com/support/helpcenter/207/201602469554" target="_blank"><i class="fa fa-question-circle"></i> 如何获取应用公钥证书?</a></div>
  292 + </div>
  293 + <div class="col-sm-4"></div>
  294 + </div>
  295 + </td>
  296 + </tr>
  297 + <tr>
  298 + <td>支付宝根证书路径(alipay_root_cert)</td>
  299 + <td>
  300 + <div class="row">
  301 + <div class="col-sm-8 col-xs-12">
  302 + <div class="input-group">
  303 + <input id="c-alipay_root_cert" class="form-control" size="50" name="row[alipay][alipay_root_cert]" type="text" value="{$item.value.alipay_root_cert|default=''|htmlentities}">
  304 + <div class="input-group-addon no-border no-padding">
  305 + <span><button type="button" id="faupload-alipay_root_cert" class="btn btn-danger faupload" data-url="epay/upload" data-multipart='{"certname":"alipay_root_cert"}' data-mimetype="crt" data-input-id="c-alipay_root_cert" data-multiple="false"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
  306 + </div>
  307 + <span class="msg-box n-right" for="c-alipay_root_cert"></span>
  308 + </div>
  309 + <div style="margin-top:5px;"><a href="https://opensupport.alipay.com/support/helpcenter/207/201602469554" target="_blank"><i class="fa fa-question-circle"></i> 如何获取支付宝证书?</a></div>
  310 + </div>
  311 + <div class="col-sm-4"></div>
  312 + </div>
  313 + </td>
  314 + </tr>
  315 +
  316 + <tr>
  317 + <td>记录日志</td>
  318 + <td>
  319 + <div class="row">
  320 + <div class="col-sm-8 col-xs-12">
  321 + {:Form::radios('row[alipay][log]',['1'=>'开启','0'=>'关闭'],$item.value.log)}
  322 + </div>
  323 + <div class="col-sm-4"></div>
  324 + </div>
  325 + </td>
  326 + </tr>
  327 +
  328 + <tr>
  329 + <td>PC端使用扫码支付</td>
  330 + <td>
  331 + <div class="row">
  332 + <div class="col-sm-8 col-xs-12">
  333 + {:Form::radios('row[alipay][scanpay]',['1'=>'开启','0'=>'关闭'],$item.value.scanpay??0)}
  334 + </div>
  335 + <div class="col-sm-4"></div>
  336 + </div>
  337 + </td>
  338 + </tr>
  339 + </tbody>
  340 + </table>
  341 + </div>
  342 + {/if}
  343 + {/foreach}
  344 + <div class="form-group layer-footer">
  345 + <label class="control-label col-xs-12 col-sm-2"></label>
  346 + <div class="col-xs-12 col-sm-8">
  347 + <button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
  348 + <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
  349 + </div>
  350 + </div>
  351 + </div>
  352 + </div>
  353 + </div>
  354 +</form>
  355 +<script>
  356 + document.querySelectorAll("input[name='row[wechat][mode]']").forEach(function (i, j) {
  357 + i.addEventListener("click", function () {
  358 + document.querySelectorAll("#wechat table tr[data-type]").forEach(function (m, n) {
  359 + m.classList.add("hidden");
  360 + });
  361 + document.querySelectorAll("#wechat table tr[data-type='" + this.value + "']").forEach(function (m, n) {
  362 + m.classList.remove("hidden");
  363 + });
  364 + });
  365 + });
  366 +</script>
  1 +<?php
  2 +
  3 +return [
  4 + [
  5 + 'name' => 'wechat',
  6 + 'title' => '微信',
  7 + 'type' => 'array',
  8 + 'content' => [],
  9 + 'value' => [
  10 + 'appid' => '',
  11 + 'app_id' => 'wx239e35047fe80913',
  12 + 'app_secret' => '9b478ad7e79e91084d94d2a6de9b3221',
  13 + 'miniapp_id' => '',
  14 + 'mch_id' => '1624790770',
  15 + 'key' => 'chexiubaogonghzonghao20220519000',
  16 + 'mode' => 'normal',
  17 + 'sub_mch_id' => '',
  18 + 'sub_appid' => '',
  19 + 'sub_app_id' => '',
  20 + 'sub_miniapp_id' => '',
  21 + 'notify_url' => '/addons/epay/api/notifyx/type/wechat',
  22 + 'cert_client' => '/addons/epay/certs/apiclient_cert.pem',
  23 + 'cert_key' => '/addons/epay/certs/apiclient_key.pem',
  24 + 'log' => '1',
  25 + ],
  26 + 'rule' => '',
  27 + 'msg' => '',
  28 + 'tip' => '微信参数配置',
  29 + 'ok' => '',
  30 + 'extend' => '',
  31 + ],
  32 + [
  33 + 'name' => 'alipay',
  34 + 'title' => '支付宝',
  35 + 'type' => 'array',
  36 + 'content' => [],
  37 + 'value' => [
  38 + 'app_id' => '',
  39 + 'mode' => 'normal',
  40 + 'notify_url' => '/addons/epay/api/notifyx/type/alipay',
  41 + 'return_url' => '/addons/epay/api/returnx/type/alipay',
  42 + 'private_key' => '',
  43 + 'ali_public_key' => '',
  44 + 'app_cert_public_key' => '',
  45 + 'alipay_root_cert' => '',
  46 + 'log' => '1',
  47 + 'scanpay' => '0',
  48 + ],
  49 + 'rule' => 'required',
  50 + 'msg' => '',
  51 + 'tip' => '支付宝参数配置',
  52 + 'ok' => '',
  53 + 'extend' => '',
  54 + ],
  55 + [
  56 + 'name' => '__tips__',
  57 + 'title' => '温馨提示',
  58 + 'type' => 'array',
  59 + 'content' => [],
  60 + 'value' => '请注意微信支付证书路径位于/addons/epay/certs目录下,请替换成你自己的证书<br>appid:APP的appid<br>app_id:公众号的appid<br>app_secret:公众号的secret<br>miniapp_id:小程序ID<br>mch_id:微信商户ID<br>key:微信商户支付的密钥',
  61 + 'rule' => '',
  62 + 'msg' => '',
  63 + 'tip' => '微信参数配置',
  64 + 'ok' => '',
  65 + 'extend' => '',
  66 + ],
  67 +];
  1 +<?php
  2 +
  3 +namespace addons\epay\controller;
  4 +
  5 +use addons\epay\library\QRCode;
  6 +use addons\epay\library\Service;
  7 +use addons\epay\library\Wechat;
  8 +use addons\third\model\Third;
  9 +use app\common\library\Auth;
  10 +use think\addons\Controller;
  11 +use think\Response;
  12 +use think\Session;
  13 +use Yansongda\Pay\Exceptions\GatewayException;
  14 +use Yansongda\Pay\Pay;
  15 +
  16 +/**
  17 + * API接口控制器
  18 + *
  19 + * @package addons\epay\controller
  20 + */
  21 +class Api extends Controller
  22 +{
  23 +
  24 + protected $layout = 'default';
  25 + protected $config = [];
  26 +
  27 + /**
  28 + * 默认方法
  29 + */
  30 + public function index()
  31 + {
  32 + return;
  33 + }
  34 +
  35 + /**
  36 + * 外部提交
  37 + */
  38 + public function submit()
  39 + {
  40 + $this->request->filter('trim');
  41 + $out_trade_no = $this->request->request("out_trade_no");
  42 + $title = $this->request->request("title");
  43 + $amount = $this->request->request('amount');
  44 + $type = $this->request->request('type');
  45 + $method = $this->request->request('method', 'web');
  46 + $openid = $this->request->request('openid', '');
  47 + $auth_code = $this->request->request('auth_code', '');
  48 + $notifyurl = $this->request->request('notifyurl', '');
  49 + $returnurl = $this->request->request('returnurl', '');
  50 +
  51 + if (!$amount || $amount < 0) {
  52 + $this->error("支付金额必须大于0");
  53 + }
  54 +
  55 + if (!$type || !in_array($type, ['alipay', 'wechat'])) {
  56 + $this->error("支付类型错误");
  57 + }
  58 +
  59 + $params = [
  60 + 'type' => $type,
  61 + 'out_trade_no' => $out_trade_no,
  62 + 'title' => $title,
  63 + 'amount' => $amount,
  64 + 'method' => $method,
  65 + 'openid' => $openid,
  66 + 'auth_code' => $auth_code,
  67 + 'notifyurl' => $notifyurl,
  68 + 'returnurl' => $returnurl,
  69 + ];
  70 + return Service::submitOrder($params);
  71 + }
  72 +
  73 + /**
  74 + * 微信支付(公众号支付&PC扫码支付)
  75 + * @return string
  76 + */
  77 + public function wechat()
  78 + {
  79 + $config = Service::getConfig('wechat');
  80 +
  81 + $isWechat = stripos($this->request->server('HTTP_USER_AGENT'), 'MicroMessenger') !== false;
  82 + $isMobile = $this->request->isMobile();
  83 + $this->view->assign("isWechat", $isWechat);
  84 + $this->view->assign("isMobile", $isMobile);
  85 +
  86 + //发起PC支付(Scan支付)(PC扫码模式)
  87 + if ($this->request->isAjax()) {
  88 + $pay = Pay::wechat($config);
  89 + $orderid = $this->request->post("orderid");
  90 + try {
  91 + $result = $pay->find($orderid);
  92 + if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
  93 + $this->success("", "", ['status' => $result['trade_state']]);
  94 + } else {
  95 + $this->error("查询失败");
  96 + }
  97 + } catch (GatewayException $e) {
  98 + $this->error("查询失败");
  99 + }
  100 + }
  101 +
  102 + $orderData = Session::get("wechatorderdata");
  103 + if (!$orderData) {
  104 + $this->error("请求参数错误");
  105 + }
  106 + if ($isWechat) {
  107 + //发起公众号(jsapi支付),openid必须
  108 +
  109 + //如果没有openid,则自动去获取openid
  110 + if (!isset($orderData['openid']) || !$orderData['openid']) {
  111 + $orderData['openid'] = Service::getOpenid();
  112 + }
  113 +
  114 + $orderData['method'] = 'mp';
  115 + $type = 'jsapi';
  116 + $payData = Service::submitOrder($orderData);
  117 + if (!isset($payData['paySign'])) {
  118 + $this->error("创建订单失败,请返回重试", "");
  119 + }
  120 + } else {
  121 + $orderData['method'] = 'scan';
  122 + $type = 'pc';
  123 + $payData = Service::submitOrder($orderData);
  124 + if (!isset($payData['code_url'])) {
  125 + $this->error("创建订单失败,请返回重试", "");
  126 + }
  127 + }
  128 + $this->view->assign("orderData", $orderData);
  129 + $this->view->assign("payData", $payData);
  130 + $this->view->assign("type", $type);
  131 +
  132 + $this->view->assign("title", "微信支付");
  133 + return $this->view->fetch();
  134 + }
  135 +
  136 + /**
  137 + * 支付宝支付(PC扫码支付)
  138 + * @return string
  139 + */
  140 + public function alipay()
  141 + {
  142 + $config = Service::getConfig('alipay');
  143 +
  144 + $isWechat = stripos($this->request->server('HTTP_USER_AGENT'), 'MicroMessenger') !== false;
  145 + $isMobile = $this->request->isMobile();
  146 + $this->view->assign("isWechat", $isWechat);
  147 + $this->view->assign("isMobile", $isMobile);
  148 +
  149 + if ($this->request->isAjax()) {
  150 + $orderid = $this->request->post("orderid");
  151 + $pay = Pay::alipay($config);
  152 + try {
  153 + $result = $pay->find($orderid);
  154 + if ($result['code'] == '10000' && $result['trade_status'] == 'TRADE_SUCCESS') {
  155 + $this->success("", "", ['status' => $result['trade_status']]);
  156 + } else {
  157 + $this->error("查询失败");
  158 + }
  159 + } catch (GatewayException $e) {
  160 + $this->error("查询失败");
  161 + }
  162 + }
  163 +
  164 + //发起PC支付(Scan支付)(PC扫码模式)
  165 + $orderData = Session::get("alipayorderdata");
  166 + if (!$orderData) {
  167 + $this->error("请求参数错误");
  168 + }
  169 +
  170 + $orderData['method'] = 'scan';
  171 + $payData = Service::submitOrder($orderData);
  172 + if (!isset($payData['qr_code'])) {
  173 + $this->error("创建订单失败,请返回重试");
  174 + }
  175 +
  176 + $type = 'pc';
  177 + $this->view->assign("orderData", $orderData);
  178 + $this->view->assign("payData", $payData);
  179 + $this->view->assign("type", $type);
  180 + $this->view->assign("title", "支付宝支付");
  181 + return $this->view->fetch();
  182 + }
  183 +
  184 + /**
  185 + * 支付成功回调
  186 + */
  187 + public function notifyx()
  188 + {
  189 + $type = $this->request->param('type');
  190 + if (!Service::checkNotify($type)) {
  191 + echo '签名错误';
  192 + return;
  193 + }
  194 +
  195 + //你可以在这里你的业务处理逻辑,比如处理你的订单状态、给会员加余额等等功能
  196 + //下面这句必须要执行,且在此之前不能有任何输出
  197 + echo "success";
  198 + return;
  199 + }
  200 +
  201 + /**
  202 + * 支付成功返回
  203 + */
  204 + public function returnx()
  205 + {
  206 + $type = $this->request->param('type');
  207 + if (Service::checkReturn($type)) {
  208 + echo '签名错误';
  209 + return;
  210 + }
  211 +
  212 + //你可以在这里定义你的提示信息,但切记不可在此编写逻辑
  213 + $this->success("恭喜你!支付成功!", addon_url("epay/index/index"));
  214 +
  215 + return;
  216 + }
  217 +
  218 + /**
  219 + * 生成二维码
  220 + */
  221 + public function qrcode()
  222 + {
  223 + $text = $this->request->get('text', 'hello world');
  224 +
  225 + //如果有安装二维码插件,则调用插件的生成方法
  226 + if (class_exists("\addons\qrcode\library\Service") && get_addon_info('qrcode')['state']) {
  227 + $qrCode = \addons\qrcode\library\Service::qrcode(['text' => $text]);
  228 + $response = Response::create()->header("Content-Type", "image/png");
  229 +
  230 + header('Content-Type: ' . $qrCode->getContentType());
  231 + $response->content($qrCode->writeString());
  232 + return $response;
  233 + } else {
  234 + $qr = QRCode::getMinimumQRCode($text);
  235 + $im = $qr->createImage(8, 5);
  236 + header("Content-type: image/png");
  237 + imagepng($im);
  238 + imagedestroy($im);
  239 + return;
  240 + }
  241 + }
  242 +
  243 +}
  1 +<?php
  2 +
  3 +namespace addons\epay\controller;
  4 +
  5 +use addons\epay\library\Service;
  6 +use fast\Random;
  7 +use think\addons\Controller;
  8 +use Exception;
  9 +
  10 +/**
  11 + * 微信支付宝插件首页
  12 + *
  13 + * 此控制器仅用于开发展示说明和体验,建议自行添加一个新的控制器进行处理返回和回调事件,同时删除此控制器文件
  14 + *
  15 + * Class Index
  16 + * @package addons\epay\controller
  17 + */
  18 +class Index extends Controller
  19 +{
  20 +
  21 + protected $layout = 'default';
  22 +
  23 + protected $config = [];
  24 +
  25 + public function _initialize()
  26 + {
  27 + parent::_initialize();
  28 + if (!config("app_debug")) {
  29 + $this->error("仅在开发环境下查看");
  30 + }
  31 + }
  32 +
  33 + public function index()
  34 + {
  35 + $this->view->assign("title", "微信支付宝整合插件");
  36 + return $this->view->fetch();
  37 + }
  38 +
  39 + /**
  40 + * 体验,仅供开发测试
  41 + */
  42 + public function experience()
  43 + {
  44 + $amount = $this->request->request('amount');
  45 + $type = $this->request->request('type');
  46 + $method = $this->request->request('method');
  47 +
  48 + if (!$amount || $amount < 0) {
  49 + $this->error("支付金额必须大于0");
  50 + }
  51 +
  52 + if (!$type || !in_array($type, ['alipay', 'wechat'])) {
  53 + $this->error("支付类型不能为空");
  54 + }
  55 +
  56 + //订单号
  57 + $out_trade_no = date("YmdHis") . mt_rand(100000, 999999);
  58 +
  59 + //订单标题
  60 + $title = '测试订单';
  61 +
  62 + //回调链接
  63 + $notifyurl = $this->request->root(true) . '/addons/epay/index/notifyx/paytype/' . $type;
  64 + $returnurl = $this->request->root(true) . '/addons/epay/index/returnx/paytype/' . $type . '/out_trade_no/' . $out_trade_no;
  65 +
  66 + $response = Service::submitOrder($amount, $out_trade_no, $type, $title, $notifyurl, $returnurl, $method);
  67 +
  68 + return $response;
  69 + }
  70 +
  71 + /**
  72 + * 支付成功,仅供开发测试
  73 + */
  74 + public function notifyx()
  75 + {
  76 + $paytype = $this->request->param('paytype');
  77 + $pay = Service::checkNotify($paytype);
  78 + if (!$pay) {
  79 + echo '签名错误';
  80 + return;
  81 + }
  82 + $data = $pay->verify();
  83 + try {
  84 + $payamount = $paytype == 'alipay' ? $data['total_amount'] : $data['total_fee'] / 100;
  85 + $out_trade_no = $data['out_trade_no'];
  86 +
  87 + //你可以在此编写订单逻辑
  88 + } catch (Exception $e) {
  89 + }
  90 + echo $pay->success();
  91 + }
  92 +
  93 + /**
  94 + * 支付返回,仅供开发测试
  95 + */
  96 + public function returnx()
  97 + {
  98 + $paytype = $this->request->param('paytype');
  99 + $out_trade_no = $this->request->param('out_trade_no');
  100 + $pay = Service::checkReturn($paytype);
  101 + if (!$pay) {
  102 + $this->error('签名错误', '');
  103 + }
  104 +
  105 + //你可以在这里通过out_trade_no去验证订单状态
  106 + //但是不可以在此编写订单逻辑!!!
  107 +
  108 + $this->success("请返回网站查看支付结果", addon_url("epay/index/index"));
  109 + }
  110 +
  111 +}
  1 +name = epay
  2 +title = 微信支付宝整合
  3 +intro = 可用于快速整合微信、支付宝支付功能
  4 +author = FastAdmin
  5 +website = https://www.fastadmin.net
  6 +version = 1.2.5
  7 +state = 1
  8 +url = /addons/epay
  9 +license = regular
  10 +licenseto = 10789
  1 +<?php
  2 +
  3 +namespace addons\epay\library;
  4 +
  5 +class Collection extends \Yansongda\Supports\Collection
  6 +{
  7 +
  8 + /**
  9 + * 创建 Collection 实例
  10 + * @access public
  11 + * @param array $items 数据
  12 + * @return static
  13 + */
  14 + public static function make($items = [])
  15 + {
  16 + return new static($items);
  17 + }
  18 +}
  1 +<?php
  2 +
  3 +namespace addons\epay\library;
  4 +
  5 +use think\Exception;
  6 +
  7 +class OrderException extends Exception
  8 +{
  9 + public function __construct($message = "", $code = 0, $data = [])
  10 + {
  11 + $this->message = $message;
  12 + $this->code = $code;
  13 + $this->data = $data;
  14 + }
  15 +
  16 +}
  1 +<?php
  2 +
  3 +namespace addons\epay\library;
  4 +
  5 +//---------------------------------------------------------------
  6 +// QRCode for PHP5
  7 +//
  8 +// Copyright (c) 2009 Kazuhiko Arase
  9 +//
  10 +// URL: http://www.d-project.com/
  11 +//
  12 +// Licensed under the MIT license:
  13 +// http://www.opensource.org/licenses/mit-license.php
  14 +//
  15 +// The word "QR Code" is registered trademark of
  16 +// DENSO WAVE INCORPORATED
  17 +// http://www.denso-wave.com/qrcode/faqpatent-e.html
  18 +//
  19 +//---------------------------------------------------------------------
  20 +
  21 +//---------------------------------------------------------------
  22 +// QRCode
  23 +//---------------------------------------------------------------
  24 +
  25 +define("QR_PAD0", 0xEC);
  26 +define("QR_PAD1", 0x11);
  27 +
  28 +class QRCode
  29 +{
  30 +
  31 + var $typeNumber;
  32 +
  33 + var $modules;
  34 +
  35 + var $moduleCount;
  36 +
  37 + var $errorCorrectLevel;
  38 +
  39 + var $qrDataList;
  40 +
  41 + function __construct()
  42 + {
  43 + $this->typeNumber = 1;
  44 + $this->errorCorrectLevel = QR_ERROR_CORRECT_LEVEL_H;
  45 + $this->qrDataList = array();
  46 + }
  47 +
  48 + function getTypeNumber()
  49 + {
  50 + return $this->typeNumber;
  51 + }
  52 +
  53 + function setTypeNumber($typeNumber)
  54 + {
  55 + $this->typeNumber = $typeNumber;
  56 + }
  57 +
  58 + function getErrorCorrectLevel()
  59 + {
  60 + return $this->errorCorrectLevel;
  61 + }
  62 +
  63 + function setErrorCorrectLevel($errorCorrectLevel)
  64 + {
  65 + $this->errorCorrectLevel = $errorCorrectLevel;
  66 + }
  67 +
  68 + function addData($data, $mode = 0)
  69 + {
  70 +
  71 + if ($mode == 0) {
  72 + $mode = QRUtil::getMode($data);
  73 + }
  74 +
  75 + switch ($mode) {
  76 +
  77 + case QR_MODE_NUMBER :
  78 + $this->addDataImpl(new QRNumber($data));
  79 + break;
  80 +
  81 + case QR_MODE_ALPHA_NUM :
  82 + $this->addDataImpl(new QRAlphaNum($data));
  83 + break;
  84 +
  85 + case QR_MODE_8BIT_BYTE :
  86 + $this->addDataImpl(new QR8BitByte($data));
  87 + break;
  88 +
  89 + case QR_MODE_KANJI :
  90 + $this->addDataImpl(new QRKanji($data));
  91 + break;
  92 +
  93 + default :
  94 + trigger_error("mode:$mode", E_USER_ERROR);
  95 + }
  96 + }
  97 +
  98 + function clearData()
  99 + {
  100 + $this->qrDataList = array();
  101 + }
  102 +
  103 + function addDataImpl($qrData)
  104 + {
  105 + $this->qrDataList[] = $qrData;
  106 + }
  107 +
  108 + function getDataCount()
  109 + {
  110 + return count($this->qrDataList);
  111 + }
  112 +
  113 + function getData($index)
  114 + {
  115 + return $this->qrDataList[$index];
  116 + }
  117 +
  118 + function isDark($row, $col)
  119 + {
  120 + if ($this->modules[$row][$col] !== null) {
  121 + return $this->modules[$row][$col];
  122 + } else {
  123 + return false;
  124 + }
  125 + }
  126 +
  127 + function getModuleCount()
  128 + {
  129 + return $this->moduleCount;
  130 + }
  131 +
  132 + // used for converting fg/bg colors (e.g. #0000ff = 0x0000FF)
  133 + // added 2015.07.27 ~ DoktorJ
  134 + function hex2rgb($hex = 0x0)
  135 + {
  136 + return array(
  137 + 'r' => floor($hex / 65536),
  138 + 'g' => floor($hex / 256) % 256,
  139 + 'b' => $hex % 256
  140 + );
  141 + }
  142 +
  143 + function make()
  144 + {
  145 + $this->makeImpl(false, $this->getBestMaskPattern());
  146 + }
  147 +
  148 + function getBestMaskPattern()
  149 + {
  150 +
  151 + $minLostPoint = 0;
  152 + $pattern = 0;
  153 +
  154 + for ($i = 0; $i < 8; $i++) {
  155 +
  156 + $this->makeImpl(true, $i);
  157 +
  158 + $lostPoint = QRUtil::getLostPoint($this);
  159 +
  160 + if ($i == 0 || $minLostPoint > $lostPoint) {
  161 + $minLostPoint = $lostPoint;
  162 + $pattern = $i;
  163 + }
  164 + }
  165 +
  166 + return $pattern;
  167 + }
  168 +
  169 + function createNullArray($length)
  170 + {
  171 + $nullArray = array();
  172 + for ($i = 0; $i < $length; $i++) {
  173 + $nullArray[] = null;
  174 + }
  175 + return $nullArray;
  176 + }
  177 +
  178 + function makeImpl($test, $maskPattern)
  179 + {
  180 +
  181 + $this->moduleCount = $this->typeNumber * 4 + 17;
  182 +
  183 + $this->modules = array();
  184 + for ($i = 0; $i < $this->moduleCount; $i++) {
  185 + $this->modules[] = QRCode::createNullArray($this->moduleCount);
  186 + }
  187 +
  188 + $this->setupPositionProbePattern(0, 0);
  189 + $this->setupPositionProbePattern($this->moduleCount - 7, 0);
  190 + $this->setupPositionProbePattern(0, $this->moduleCount - 7);
  191 +
  192 + $this->setupPositionAdjustPattern();
  193 + $this->setupTimingPattern();
  194 +
  195 + $this->setupTypeInfo($test, $maskPattern);
  196 +
  197 + if ($this->typeNumber >= 7) {
  198 + $this->setupTypeNumber($test);
  199 + }
  200 +
  201 + $dataArray = $this->qrDataList;
  202 +
  203 + $data = QRCode::createData($this->typeNumber, $this->errorCorrectLevel, $dataArray);
  204 +
  205 + $this->mapData($data, $maskPattern);
  206 + }
  207 +
  208 + function mapData(&$data, $maskPattern)
  209 + {
  210 +
  211 + $inc = -1;
  212 + $row = $this->moduleCount - 1;
  213 + $bitIndex = 7;
  214 + $byteIndex = 0;
  215 +
  216 + for ($col = $this->moduleCount - 1; $col > 0; $col -= 2) {
  217 +
  218 + if ($col == 6) $col--;
  219 +
  220 + while (true) {
  221 +
  222 + for ($c = 0; $c < 2; $c++) {
  223 +
  224 + if ($this->modules[$row][$col - $c] === null) {
  225 +
  226 + $dark = false;
  227 +
  228 + if ($byteIndex < count($data)) {
  229 + $dark = ((($data[$byteIndex] >> $bitIndex) & 1) == 1);
  230 + }
  231 +
  232 + if (QRUtil::getMask($maskPattern, $row, $col - $c)) {
  233 + $dark = !$dark;
  234 + }
  235 +
  236 + $this->modules[$row][$col - $c] = $dark;
  237 + $bitIndex--;
  238 +
  239 + if ($bitIndex == -1) {
  240 + $byteIndex++;
  241 + $bitIndex = 7;
  242 + }
  243 + }
  244 + }
  245 +
  246 + $row += $inc;
  247 +
  248 + if ($row < 0 || $this->moduleCount <= $row) {
  249 + $row -= $inc;
  250 + $inc = -$inc;
  251 + break;
  252 + }
  253 + }
  254 + }
  255 + }
  256 +
  257 + function setupPositionAdjustPattern()
  258 + {
  259 +
  260 + $pos = QRUtil::getPatternPosition($this->typeNumber);
  261 +
  262 + for ($i = 0; $i < count($pos); $i++) {
  263 +
  264 + for ($j = 0; $j < count($pos); $j++) {
  265 +
  266 + $row = $pos[$i];
  267 + $col = $pos[$j];
  268 +
  269 + if ($this->modules[$row][$col] !== null) {
  270 + continue;
  271 + }
  272 +
  273 + for ($r = -2; $r <= 2; $r++) {
  274 +
  275 + for ($c = -2; $c <= 2; $c++) {
  276 + $this->modules[$row + $r][$col + $c] =
  277 + $r == -2 || $r == 2 || $c == -2 || $c == 2 || ($r == 0 && $c == 0);
  278 + }
  279 + }
  280 + }
  281 + }
  282 + }
  283 +
  284 + function setupPositionProbePattern($row, $col)
  285 + {
  286 +
  287 + for ($r = -1; $r <= 7; $r++) {
  288 +
  289 + for ($c = -1; $c <= 7; $c++) {
  290 +
  291 + if ($row + $r <= -1 || $this->moduleCount <= $row + $r
  292 + || $col + $c <= -1 || $this->moduleCount <= $col + $c) {
  293 + continue;
  294 + }
  295 +
  296 + $this->modules[$row + $r][$col + $c] =
  297 + (0 <= $r && $r <= 6 && ($c == 0 || $c == 6))
  298 + || (0 <= $c && $c <= 6 && ($r == 0 || $r == 6))
  299 + || (2 <= $r && $r <= 4 && 2 <= $c && $c <= 4);
  300 + }
  301 + }
  302 + }
  303 +
  304 + function setupTimingPattern()
  305 + {
  306 +
  307 + for ($i = 8; $i < $this->moduleCount - 8; $i++) {
  308 +
  309 + if ($this->modules[$i][6] !== null || $this->modules[6][$i] !== null) {
  310 + continue;
  311 + }
  312 +
  313 + $this->modules[$i][6] = ($i % 2 == 0);
  314 + $this->modules[6][$i] = ($i % 2 == 0);
  315 + }
  316 + }
  317 +
  318 + function setupTypeNumber($test)
  319 + {
  320 +
  321 + $bits = QRUtil::getBCHTypeNumber($this->typeNumber);
  322 +
  323 + for ($i = 0; $i < 18; $i++) {
  324 + $mod = (!$test && (($bits >> $i) & 1) == 1);
  325 + $this->modules[(int)floor($i / 3)][$i % 3 + $this->moduleCount - 8 - 3] = $mod;
  326 + $this->modules[$i % 3 + $this->moduleCount - 8 - 3][floor($i / 3)] = $mod;
  327 + }
  328 + }
  329 +
  330 + function setupTypeInfo($test, $maskPattern)
  331 + {
  332 +
  333 + $data = ($this->errorCorrectLevel << 3) | $maskPattern;
  334 + $bits = QRUtil::getBCHTypeInfo($data);
  335 +
  336 + for ($i = 0; $i < 15; $i++) {
  337 +
  338 + $mod = (!$test && (($bits >> $i) & 1) == 1);
  339 +
  340 + if ($i < 6) {
  341 + $this->modules[$i][8] = $mod;
  342 + } else if ($i < 8) {
  343 + $this->modules[$i + 1][8] = $mod;
  344 + } else {
  345 + $this->modules[$this->moduleCount - 15 + $i][8] = $mod;
  346 + }
  347 +
  348 + if ($i < 8) {
  349 + $this->modules[8][$this->moduleCount - $i - 1] = $mod;
  350 + } else if ($i < 9) {
  351 + $this->modules[8][15 - $i - 1 + 1] = $mod;
  352 + } else {
  353 + $this->modules[8][15 - $i - 1] = $mod;
  354 + }
  355 + }
  356 +
  357 + $this->modules[$this->moduleCount - 8][8] = !$test;
  358 + }
  359 +
  360 + function createData($typeNumber, $errorCorrectLevel, $dataArray)
  361 + {
  362 +
  363 + $rsBlocks = QRRSBlock::getRSBlocks($typeNumber, $errorCorrectLevel);
  364 +
  365 + $buffer = new QRBitBuffer();
  366 +
  367 + for ($i = 0; $i < count($dataArray); $i++) {
  368 + /** @var \QRData $data */
  369 + $data = $dataArray[$i];
  370 + $buffer->put($data->getMode(), 4);
  371 + $buffer->put($data->getLength(), $data->getLengthInBits($typeNumber));
  372 + $data->write($buffer);
  373 + }
  374 +
  375 + $totalDataCount = 0;
  376 + for ($i = 0; $i < count($rsBlocks); $i++) {
  377 + $totalDataCount += $rsBlocks[$i]->getDataCount();
  378 + }
  379 +
  380 + if ($buffer->getLengthInBits() > $totalDataCount * 8) {
  381 + trigger_error("code length overflow. ("
  382 + . $buffer->getLengthInBits()
  383 + . ">"
  384 + . $totalDataCount * 8
  385 + . ")", E_USER_ERROR);
  386 + }
  387 +
  388 + // end code.
  389 + if ($buffer->getLengthInBits() + 4 <= $totalDataCount * 8) {
  390 + $buffer->put(0, 4);
  391 + }
  392 +
  393 + // padding
  394 + while ($buffer->getLengthInBits() % 8 != 0) {
  395 + $buffer->putBit(false);
  396 + }
  397 +
  398 + // padding
  399 + while (true) {
  400 +
  401 + if ($buffer->getLengthInBits() >= $totalDataCount * 8) {
  402 + break;
  403 + }
  404 + $buffer->put(QR_PAD0, 8);
  405 +
  406 + if ($buffer->getLengthInBits() >= $totalDataCount * 8) {
  407 + break;
  408 + }
  409 + $buffer->put(QR_PAD1, 8);
  410 + }
  411 +
  412 + return QRCode::createBytes($buffer, $rsBlocks);
  413 + }
  414 +
  415 + /**
  416 + * @param \QRBitBuffer $buffer
  417 + * @param \QRRSBlock[] $rsBlocks
  418 + *
  419 + * @return array
  420 + */
  421 + function createBytes(&$buffer, &$rsBlocks)
  422 + {
  423 +
  424 + $offset = 0;
  425 +
  426 + $maxDcCount = 0;
  427 + $maxEcCount = 0;
  428 +
  429 + $dcdata = QRCode::createNullArray(count($rsBlocks));
  430 + $ecdata = QRCode::createNullArray(count($rsBlocks));
  431 +
  432 + $rsBlockCount = count($rsBlocks);
  433 + for ($r = 0; $r < $rsBlockCount; $r++) {
  434 +
  435 + $dcCount = $rsBlocks[$r]->getDataCount();
  436 + $ecCount = $rsBlocks[$r]->getTotalCount() - $dcCount;
  437 +
  438 + $maxDcCount = max($maxDcCount, $dcCount);
  439 + $maxEcCount = max($maxEcCount, $ecCount);
  440 +
  441 + $dcdata[$r] = QRCode::createNullArray($dcCount);
  442 + $dcDataCount = count($dcdata[$r]);
  443 + for ($i = 0; $i < $dcDataCount; $i++) {
  444 + $bdata = $buffer->getBuffer();
  445 + $dcdata[$r][$i] = 0xff & $bdata[$i + $offset];
  446 + }
  447 + $offset += $dcCount;
  448 +
  449 + $rsPoly = QRUtil::getErrorCorrectPolynomial($ecCount);
  450 + $rawPoly = new QRPolynomial($dcdata[$r], $rsPoly->getLength() - 1);
  451 +
  452 + $modPoly = $rawPoly->mod($rsPoly);
  453 + $ecdata[$r] = QRCode::createNullArray($rsPoly->getLength() - 1);
  454 +
  455 + $ecDataCount = count($ecdata[$r]);
  456 + for ($i = 0; $i < $ecDataCount; $i++) {
  457 + $modIndex = $i + $modPoly->getLength() - count($ecdata[$r]);
  458 + $ecdata[$r][$i] = ($modIndex >= 0) ? $modPoly->get($modIndex) : 0;
  459 + }
  460 + }
  461 +
  462 + $totalCodeCount = 0;
  463 + for ($i = 0; $i < $rsBlockCount; $i++) {
  464 + $totalCodeCount += $rsBlocks[$i]->getTotalCount();
  465 + }
  466 +
  467 + $data = QRCode::createNullArray($totalCodeCount);
  468 +
  469 + $index = 0;
  470 +
  471 + for ($i = 0; $i < $maxDcCount; $i++) {
  472 + for ($r = 0; $r < $rsBlockCount; $r++) {
  473 + if ($i < count($dcdata[$r])) {
  474 + $data[$index++] = $dcdata[$r][$i];
  475 + }
  476 + }
  477 + }
  478 +
  479 + for ($i = 0; $i < $maxEcCount; $i++) {
  480 + for ($r = 0; $r < $rsBlockCount; $r++) {
  481 + if ($i < count($ecdata[$r])) {
  482 + $data[$index++] = $ecdata[$r][$i];
  483 + }
  484 + }
  485 + }
  486 +
  487 + return $data;
  488 + }
  489 +
  490 + static function getMinimumQRCode($data, $errorCorrectLevel = QR_ERROR_CORRECT_LEVEL_L)
  491 + {
  492 +
  493 + $mode = QRUtil::getMode($data);
  494 +
  495 + $qr = new QRCode();
  496 + $qr->setErrorCorrectLevel($errorCorrectLevel);
  497 + $qr->addData($data, $mode);
  498 +
  499 + $qrData = $qr->getData(0);
  500 + $length = $qrData->getLength();
  501 +
  502 + for ($typeNumber = 1; $typeNumber <= 40; $typeNumber++) {
  503 + if ($length <= QRUtil::getMaxLength($typeNumber, $mode, $errorCorrectLevel)) {
  504 + $qr->setTypeNumber($typeNumber);
  505 + break;
  506 + }
  507 + }
  508 +
  509 + $qr->make();
  510 +
  511 + return $qr;
  512 + }
  513 +
  514 + // added $fg (foreground), $bg (background), and $bgtrans (use transparent bg) parameters
  515 + // also added some simple error checking on parameters
  516 + // updated 2015.07.27 ~ DoktorJ
  517 + function createImage($size = 2, $margin = 2, $fg = 0x000000, $bg = 0xFFFFFF, $bgtrans = false)
  518 + {
  519 +
  520 + // size/margin EC
  521 + if (!is_numeric($size)) $size = 2;
  522 + if (!is_numeric($margin)) $margin = 2;
  523 + if ($size < 1) $size = 1;
  524 + if ($margin < 0) $margin = 0;
  525 +
  526 + $image_size = $this->getModuleCount() * $size + $margin * 2;
  527 +
  528 + $image = imagecreatetruecolor($image_size, $image_size);
  529 +
  530 + // fg/bg EC
  531 + if ($fg < 0 || $fg > 0xFFFFFF) $fg = 0x0;
  532 + if ($bg < 0 || $bg > 0xFFFFFF) $bg = 0xFFFFFF;
  533 +
  534 + // convert hexadecimal RGB to arrays for imagecolorallocate
  535 + $fgrgb = $this->hex2rgb($fg);
  536 + $bgrgb = $this->hex2rgb($bg);
  537 +
  538 + // replace $black and $white with $fgc and $bgc
  539 + $fgc = imagecolorallocate($image, $fgrgb['r'], $fgrgb['g'], $fgrgb['b']);
  540 + $bgc = imagecolorallocate($image, $bgrgb['r'], $bgrgb['g'], $bgrgb['b']);
  541 + if ($bgtrans) imagecolortransparent($image, $bgc);
  542 +
  543 + // update $white to $bgc
  544 + imagefilledrectangle($image, 0, 0, $image_size, $image_size, $bgc);
  545 +
  546 + for ($r = 0; $r < $this->getModuleCount(); $r++) {
  547 + for ($c = 0; $c < $this->getModuleCount(); $c++) {
  548 + if ($this->isDark($r, $c)) {
  549 +
  550 + // update $black to $fgc
  551 + imagefilledrectangle($image,
  552 + $margin + $c * $size,
  553 + $margin + $r * $size,
  554 + $margin + ($c + 1) * $size - 1,
  555 + $margin + ($r + 1) * $size - 1,
  556 + $fgc);
  557 + }
  558 + }
  559 + }
  560 +
  561 + return $image;
  562 + }
  563 +
  564 + function printHTML($size = "2px")
  565 + {
  566 +
  567 + $style = "border-style:none;border-collapse:collapse;margin:0px;padding:0px;";
  568 +
  569 + print("<table style='$style'>");
  570 +
  571 + for ($r = 0; $r < $this->getModuleCount(); $r++) {
  572 +
  573 + print("<tr style='$style'>");
  574 +
  575 + for ($c = 0; $c < $this->getModuleCount(); $c++) {
  576 + $color = $this->isDark($r, $c) ? "#000000" : "#ffffff";
  577 + print("<td style='$style;width:$size;height:$size;background-color:$color'></td>");
  578 + }
  579 +
  580 + print("</tr>");
  581 + }
  582 +
  583 + print("</table>");
  584 + }
  585 +}
  586 +
  587 +//---------------------------------------------------------------
  588 +// QRUtil
  589 +//---------------------------------------------------------------
  590 +
  591 +define("QR_G15", (1 << 10) | (1 << 8) | (1 << 5)
  592 + | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0));
  593 +
  594 +define("QR_G18", (1 << 12) | (1 << 11) | (1 << 10)
  595 + | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0));
  596 +
  597 +define("QR_G15_MASK", (1 << 14) | (1 << 12) | (1 << 10)
  598 + | (1 << 4) | (1 << 1));
  599 +
  600 +class QRUtil
  601 +{
  602 +
  603 + static $QR_MAX_LENGTH = array(
  604 + array(array(41, 25, 17, 10), array(34, 20, 14, 8), array(27, 16, 11, 7), array(17, 10, 7, 4)),
  605 + array(array(77, 47, 32, 20), array(63, 38, 26, 16), array(48, 29, 20, 12), array(34, 20, 14, 8)),
  606 + array(array(127, 77, 53, 32), array(101, 61, 42, 26), array(77, 47, 32, 20), array(58, 35, 24, 15)),
  607 + array(array(187, 114, 78, 48), array(149, 90, 62, 38), array(111, 67, 46, 28), array(82, 50, 34, 21)),
  608 + array(array(255, 154, 106, 65), array(202, 122, 84, 52), array(144, 87, 60, 37), array(106, 64, 44, 27)),
  609 + array(array(322, 195, 134, 82), array(255, 154, 106, 65), array(178, 108, 74, 45), array(139, 84, 58, 36)),
  610 + array(array(370, 224, 154, 95), array(293, 178, 122, 75), array(207, 125, 86, 53), array(154, 93, 64, 39)),
  611 + array(array(461, 279, 192, 118), array(365, 221, 152, 93), array(259, 157, 108, 66), array(202, 122, 84, 52)),
  612 + array(array(552, 335, 230, 141), array(432, 262, 180, 111), array(312, 189, 130, 80), array(235, 143, 98, 60)),
  613 + array(array(652, 395, 271, 167), array(513, 311, 213, 131), array(364, 221, 151, 93), array(288, 174, 119, 74))
  614 + );
  615 +
  616 + static $QR_PATTERN_POSITION_TABLE = array(
  617 + array(),
  618 + array(6, 18),
  619 + array(6, 22),
  620 + array(6, 26),
  621 + array(6, 30),
  622 + array(6, 34),
  623 + array(6, 22, 38),
  624 + array(6, 24, 42),
  625 + array(6, 26, 46),
  626 + array(6, 28, 50),
  627 + array(6, 30, 54),
  628 + array(6, 32, 58),
  629 + array(6, 34, 62),
  630 + array(6, 26, 46, 66),
  631 + array(6, 26, 48, 70),
  632 + array(6, 26, 50, 74),
  633 + array(6, 30, 54, 78),
  634 + array(6, 30, 56, 82),
  635 + array(6, 30, 58, 86),
  636 + array(6, 34, 62, 90),
  637 + array(6, 28, 50, 72, 94),
  638 + array(6, 26, 50, 74, 98),
  639 + array(6, 30, 54, 78, 102),
  640 + array(6, 28, 54, 80, 106),
  641 + array(6, 32, 58, 84, 110),
  642 + array(6, 30, 58, 86, 114),
  643 + array(6, 34, 62, 90, 118),
  644 + array(6, 26, 50, 74, 98, 122),
  645 + array(6, 30, 54, 78, 102, 126),
  646 + array(6, 26, 52, 78, 104, 130),
  647 + array(6, 30, 56, 82, 108, 134),
  648 + array(6, 34, 60, 86, 112, 138),
  649 + array(6, 30, 58, 86, 114, 142),
  650 + array(6, 34, 62, 90, 118, 146),
  651 + array(6, 30, 54, 78, 102, 126, 150),
  652 + array(6, 24, 50, 76, 102, 128, 154),
  653 + array(6, 28, 54, 80, 106, 132, 158),
  654 + array(6, 32, 58, 84, 110, 136, 162),
  655 + array(6, 26, 54, 82, 110, 138, 166),
  656 + array(6, 30, 58, 86, 114, 142, 170)
  657 + );
  658 +
  659 + static function getPatternPosition($typeNumber)
  660 + {
  661 + return self::$QR_PATTERN_POSITION_TABLE[$typeNumber - 1];
  662 + }
  663 +
  664 + static function getMaxLength($typeNumber, $mode, $errorCorrectLevel)
  665 + {
  666 +
  667 + $t = $typeNumber - 1;
  668 + $e = 0;
  669 + $m = 0;
  670 +
  671 + switch ($errorCorrectLevel) {
  672 + case QR_ERROR_CORRECT_LEVEL_L :
  673 + $e = 0;
  674 + break;
  675 + case QR_ERROR_CORRECT_LEVEL_M :
  676 + $e = 1;
  677 + break;
  678 + case QR_ERROR_CORRECT_LEVEL_Q :
  679 + $e = 2;
  680 + break;
  681 + case QR_ERROR_CORRECT_LEVEL_H :
  682 + $e = 3;
  683 + break;
  684 + default :
  685 + trigger_error("e:$errorCorrectLevel", E_USER_ERROR);
  686 + }
  687 +
  688 + switch ($mode) {
  689 + case QR_MODE_NUMBER :
  690 + $m = 0;
  691 + break;
  692 + case QR_MODE_ALPHA_NUM :
  693 + $m = 1;
  694 + break;
  695 + case QR_MODE_8BIT_BYTE :
  696 + $m = 2;
  697 + break;
  698 + case QR_MODE_KANJI :
  699 + $m = 3;
  700 + break;
  701 + default :
  702 + trigger_error("m:$mode", E_USER_ERROR);
  703 + }
  704 +
  705 + return self::$QR_MAX_LENGTH[$t][$e][$m];
  706 + }
  707 +
  708 + static function getErrorCorrectPolynomial($errorCorrectLength)
  709 + {
  710 +
  711 + $a = new QRPolynomial(array(1));
  712 +
  713 + for ($i = 0; $i < $errorCorrectLength; $i++) {
  714 + $a = $a->multiply(new QRPolynomial(array(1, QRMath::gexp($i))));
  715 + }
  716 +
  717 + return $a;
  718 + }
  719 +
  720 + static function getMask($maskPattern, $i, $j)
  721 + {
  722 +
  723 + switch ($maskPattern) {
  724 +
  725 + case QR_MASK_PATTERN000 :
  726 + return ($i + $j) % 2 == 0;
  727 + case QR_MASK_PATTERN001 :
  728 + return $i % 2 == 0;
  729 + case QR_MASK_PATTERN010 :
  730 + return $j % 3 == 0;
  731 + case QR_MASK_PATTERN011 :
  732 + return ($i + $j) % 3 == 0;
  733 + case QR_MASK_PATTERN100 :
  734 + return (floor($i / 2) + floor($j / 3)) % 2 == 0;
  735 + case QR_MASK_PATTERN101 :
  736 + return ($i * $j) % 2 + ($i * $j) % 3 == 0;
  737 + case QR_MASK_PATTERN110 :
  738 + return (($i * $j) % 2 + ($i * $j) % 3) % 2 == 0;
  739 + case QR_MASK_PATTERN111 :
  740 + return (($i * $j) % 3 + ($i + $j) % 2) % 2 == 0;
  741 +
  742 + default :
  743 + trigger_error("mask:$maskPattern", E_USER_ERROR);
  744 + }
  745 + }
  746 +
  747 + /**
  748 + * @param \QRCode $qrCode
  749 + *
  750 + * @return float|int
  751 + */
  752 + static function getLostPoint($qrCode)
  753 + {
  754 +
  755 + $moduleCount = $qrCode->getModuleCount();
  756 +
  757 + $lostPoint = 0;
  758 +
  759 +
  760 + // LEVEL1
  761 +
  762 + for ($row = 0; $row < $moduleCount; $row++) {
  763 +
  764 + for ($col = 0; $col < $moduleCount; $col++) {
  765 +
  766 + $sameCount = 0;
  767 + $dark = $qrCode->isDark($row, $col);
  768 +
  769 + for ($r = -1; $r <= 1; $r++) {
  770 +
  771 + if ($row + $r < 0 || $moduleCount <= $row + $r) {
  772 + continue;
  773 + }
  774 +
  775 + for ($c = -1; $c <= 1; $c++) {
  776 +
  777 + if (($col + $c < 0 || $moduleCount <= $col + $c) || ($r == 0 && $c == 0)) {
  778 + continue;
  779 + }
  780 +
  781 + if ($dark == $qrCode->isDark($row + $r, $col + $c)) {
  782 + $sameCount++;
  783 + }
  784 + }
  785 + }
  786 +
  787 + if ($sameCount > 5) {
  788 + $lostPoint += (3 + $sameCount - 5);
  789 + }
  790 + }
  791 + }
  792 +
  793 + // LEVEL2
  794 +
  795 + for ($row = 0; $row < $moduleCount - 1; $row++) {
  796 + for ($col = 0; $col < $moduleCount - 1; $col++) {
  797 + $count = 0;
  798 + if ($qrCode->isDark($row, $col)) $count++;
  799 + if ($qrCode->isDark($row + 1, $col)) $count++;
  800 + if ($qrCode->isDark($row, $col + 1)) $count++;
  801 + if ($qrCode->isDark($row + 1, $col + 1)) $count++;
  802 + if ($count == 0 || $count == 4) {
  803 + $lostPoint += 3;
  804 + }
  805 + }
  806 + }
  807 +
  808 + // LEVEL3
  809 +
  810 + for ($row = 0; $row < $moduleCount; $row++) {
  811 + for ($col = 0; $col < $moduleCount - 6; $col++) {
  812 + if ($qrCode->isDark($row, $col)
  813 + && !$qrCode->isDark($row, $col + 1)
  814 + && $qrCode->isDark($row, $col + 2)
  815 + && $qrCode->isDark($row, $col + 3)
  816 + && $qrCode->isDark($row, $col + 4)
  817 + && !$qrCode->isDark($row, $col + 5)
  818 + && $qrCode->isDark($row, $col + 6)) {
  819 + $lostPoint += 40;
  820 + }
  821 + }
  822 + }
  823 +
  824 + for ($col = 0; $col < $moduleCount; $col++) {
  825 + for ($row = 0; $row < $moduleCount - 6; $row++) {
  826 + if ($qrCode->isDark($row, $col)
  827 + && !$qrCode->isDark($row + 1, $col)
  828 + && $qrCode->isDark($row + 2, $col)
  829 + && $qrCode->isDark($row + 3, $col)
  830 + && $qrCode->isDark($row + 4, $col)
  831 + && !$qrCode->isDark($row + 5, $col)
  832 + && $qrCode->isDark($row + 6, $col)) {
  833 + $lostPoint += 40;
  834 + }
  835 + }
  836 + }
  837 +
  838 + // LEVEL4
  839 +
  840 + $darkCount = 0;
  841 +
  842 + for ($col = 0; $col < $moduleCount; $col++) {
  843 + for ($row = 0; $row < $moduleCount; $row++) {
  844 + if ($qrCode->isDark($row, $col)) {
  845 + $darkCount++;
  846 + }
  847 + }
  848 + }
  849 +
  850 + $ratio = abs(100 * $darkCount / $moduleCount / $moduleCount - 50) / 5;
  851 + $lostPoint += $ratio * 10;
  852 +
  853 + return $lostPoint;
  854 + }
  855 +
  856 + static function getMode($s)
  857 + {
  858 + if (QRUtil::isAlphaNum($s)) {
  859 + if (QRUtil::isNumber($s)) {
  860 + return QR_MODE_NUMBER;
  861 + }
  862 + return QR_MODE_ALPHA_NUM;
  863 + } else if (QRUtil::isKanji($s)) {
  864 + return QR_MODE_KANJI;
  865 + } else {
  866 + return QR_MODE_8BIT_BYTE;
  867 + }
  868 + }
  869 +
  870 + static function isNumber($s)
  871 + {
  872 + for ($i = 0; $i < strlen($s); $i++) {
  873 + $c = ord($s[$i]);
  874 + if (!(QRUtil::toCharCode('0') <= $c && $c <= QRUtil::toCharCode('9'))) {
  875 + return false;
  876 + }
  877 + }
  878 + return true;
  879 + }
  880 +
  881 + static function isAlphaNum($s)
  882 + {
  883 + for ($i = 0; $i < strlen($s); $i++) {
  884 + $c = ord($s[$i]);
  885 + if (!(QRUtil::toCharCode('0') <= $c && $c <= QRUtil::toCharCode('9'))
  886 + && !(QRUtil::toCharCode('A') <= $c && $c <= QRUtil::toCharCode('Z'))
  887 + && strpos(" $%*+-./:", $s[$i]) === false) {
  888 + return false;
  889 + }
  890 + }
  891 + return true;
  892 + }
  893 +
  894 + static function isKanji($s)
  895 + {
  896 +
  897 + $data = $s;
  898 +
  899 + $i = 0;
  900 +
  901 + while ($i + 1 < strlen($data)) {
  902 +
  903 + $c = ((0xff & ord($data[$i])) << 8) | (0xff & ord($data[$i + 1]));
  904 +
  905 + if (!(0x8140 <= $c && $c <= 0x9FFC) && !(0xE040 <= $c && $c <= 0xEBBF)) {
  906 + return false;
  907 + }
  908 +
  909 + $i += 2;
  910 + }
  911 +
  912 + if ($i < strlen($data)) {
  913 + return false;
  914 + }
  915 +
  916 + return true;
  917 + }
  918 +
  919 + static function toCharCode($s)
  920 + {
  921 + return ord($s[0]);
  922 + }
  923 +
  924 + static function getBCHTypeInfo($data)
  925 + {
  926 + $d = $data << 10;
  927 + while (QRUtil::getBCHDigit($d) - QRUtil::getBCHDigit(QR_G15) >= 0) {
  928 + $d ^= (QR_G15 << (QRUtil::getBCHDigit($d) - QRUtil::getBCHDigit(QR_G15)));
  929 + }
  930 + return (($data << 10) | $d) ^ QR_G15_MASK;
  931 + }
  932 +
  933 + static function getBCHTypeNumber($data)
  934 + {
  935 + $d = $data << 12;
  936 + while (QRUtil::getBCHDigit($d) - QRUtil::getBCHDigit(QR_G18) >= 0) {
  937 + $d ^= (QR_G18 << (QRUtil::getBCHDigit($d) - QRUtil::getBCHDigit(QR_G18)));
  938 + }
  939 + return ($data << 12) | $d;
  940 + }
  941 +
  942 + static function getBCHDigit($data)
  943 + {
  944 +
  945 + $digit = 0;
  946 +
  947 + while ($data != 0) {
  948 + $digit++;
  949 + $data >>= 1;
  950 + }
  951 +
  952 + return $digit;
  953 + }
  954 +}
  955 +
  956 +//---------------------------------------------------------------
  957 +// QRRSBlock
  958 +//---------------------------------------------------------------
  959 +
  960 +class QRRSBlock
  961 +{
  962 +
  963 + var $totalCount;
  964 + var $dataCount;
  965 +
  966 + static $QR_RS_BLOCK_TABLE = array(
  967 +
  968 + // L
  969 + // M
  970 + // Q
  971 + // H
  972 +
  973 + // 1
  974 + array(1, 26, 19),
  975 + array(1, 26, 16),
  976 + array(1, 26, 13),
  977 + array(1, 26, 9),
  978 +
  979 + // 2
  980 + array(1, 44, 34),
  981 + array(1, 44, 28),
  982 + array(1, 44, 22),
  983 + array(1, 44, 16),
  984 +
  985 + // 3
  986 + array(1, 70, 55),
  987 + array(1, 70, 44),
  988 + array(2, 35, 17),
  989 + array(2, 35, 13),
  990 +
  991 + // 4
  992 + array(1, 100, 80),
  993 + array(2, 50, 32),
  994 + array(2, 50, 24),
  995 + array(4, 25, 9),
  996 +
  997 + // 5
  998 + array(1, 134, 108),
  999 + array(2, 67, 43),
  1000 + array(2, 33, 15, 2, 34, 16),
  1001 + array(2, 33, 11, 2, 34, 12),
  1002 +
  1003 + // 6
  1004 + array(2, 86, 68),
  1005 + array(4, 43, 27),
  1006 + array(4, 43, 19),
  1007 + array(4, 43, 15),
  1008 +
  1009 + // 7
  1010 + array(2, 98, 78),
  1011 + array(4, 49, 31),
  1012 + array(2, 32, 14, 4, 33, 15),
  1013 + array(4, 39, 13, 1, 40, 14),
  1014 +
  1015 + // 8
  1016 + array(2, 121, 97),
  1017 + array(2, 60, 38, 2, 61, 39),
  1018 + array(4, 40, 18, 2, 41, 19),
  1019 + array(4, 40, 14, 2, 41, 15),
  1020 +
  1021 + // 9
  1022 + array(2, 146, 116),
  1023 + array(3, 58, 36, 2, 59, 37),
  1024 + array(4, 36, 16, 4, 37, 17),
  1025 + array(4, 36, 12, 4, 37, 13),
  1026 +
  1027 + // 10
  1028 + array(2, 86, 68, 2, 87, 69),
  1029 + array(4, 69, 43, 1, 70, 44),
  1030 + array(6, 43, 19, 2, 44, 20),
  1031 + array(6, 43, 15, 2, 44, 16),
  1032 +
  1033 + // 11
  1034 + array(4, 101, 81),
  1035 + array(1, 80, 50, 4, 81, 51),
  1036 + array(4, 50, 22, 4, 51, 23),
  1037 + array(3, 36, 12, 8, 37, 13),
  1038 +
  1039 + // 12
  1040 + array(2, 116, 92, 2, 117, 93),
  1041 + array(6, 58, 36, 2, 59, 37),
  1042 + array(4, 46, 20, 6, 47, 21),
  1043 + array(7, 42, 14, 4, 43, 15),
  1044 +
  1045 + // 13
  1046 + array(4, 133, 107),
  1047 + array(8, 59, 37, 1, 60, 38),
  1048 + array(8, 44, 20, 4, 45, 21),
  1049 + array(12, 33, 11, 4, 34, 12),
  1050 +
  1051 + // 14
  1052 + array(3, 145, 115, 1, 146, 116),
  1053 + array(4, 64, 40, 5, 65, 41),
  1054 + array(11, 36, 16, 5, 37, 17),
  1055 + array(11, 36, 12, 5, 37, 13),
  1056 +
  1057 + // 15
  1058 + array(5, 109, 87, 1, 110, 88),
  1059 + array(5, 65, 41, 5, 66, 42),
  1060 + array(5, 54, 24, 7, 55, 25),
  1061 + array(11, 36, 12, 7, 37, 13),
  1062 +
  1063 + // 16
  1064 + array(5, 122, 98, 1, 123, 99),
  1065 + array(7, 73, 45, 3, 74, 46),
  1066 + array(15, 43, 19, 2, 44, 20),
  1067 + array(3, 45, 15, 13, 46, 16),
  1068 +
  1069 + // 17
  1070 + array(1, 135, 107, 5, 136, 108),
  1071 + array(10, 74, 46, 1, 75, 47),
  1072 + array(1, 50, 22, 15, 51, 23),
  1073 + array(2, 42, 14, 17, 43, 15),
  1074 +
  1075 + // 18
  1076 + array(5, 150, 120, 1, 151, 121),
  1077 + array(9, 69, 43, 4, 70, 44),
  1078 + array(17, 50, 22, 1, 51, 23),
  1079 + array(2, 42, 14, 19, 43, 15),
  1080 +
  1081 + // 19
  1082 + array(3, 141, 113, 4, 142, 114),
  1083 + array(3, 70, 44, 11, 71, 45),
  1084 + array(17, 47, 21, 4, 48, 22),
  1085 + array(9, 39, 13, 16, 40, 14),
  1086 +
  1087 + // 20
  1088 + array(3, 135, 107, 5, 136, 108),
  1089 + array(3, 67, 41, 13, 68, 42),
  1090 + array(15, 54, 24, 5, 55, 25),
  1091 + array(15, 43, 15, 10, 44, 16),
  1092 +
  1093 + // 21
  1094 + array(4, 144, 116, 4, 145, 117),
  1095 + array(17, 68, 42),
  1096 + array(17, 50, 22, 6, 51, 23),
  1097 + array(19, 46, 16, 6, 47, 17),
  1098 +
  1099 + // 22
  1100 + array(2, 139, 111, 7, 140, 112),
  1101 + array(17, 74, 46),
  1102 + array(7, 54, 24, 16, 55, 25),
  1103 + array(34, 37, 13),
  1104 +
  1105 + // 23
  1106 + array(4, 151, 121, 5, 152, 122),
  1107 + array(4, 75, 47, 14, 76, 48),
  1108 + array(11, 54, 24, 14, 55, 25),
  1109 + array(16, 45, 15, 14, 46, 16),
  1110 +
  1111 + // 24
  1112 + array(6, 147, 117, 4, 148, 118),
  1113 + array(6, 73, 45, 14, 74, 46),
  1114 + array(11, 54, 24, 16, 55, 25),
  1115 + array(30, 46, 16, 2, 47, 17),
  1116 +
  1117 + // 25
  1118 + array(8, 132, 106, 4, 133, 107),
  1119 + array(8, 75, 47, 13, 76, 48),
  1120 + array(7, 54, 24, 22, 55, 25),
  1121 + array(22, 45, 15, 13, 46, 16),
  1122 +
  1123 + // 26
  1124 + array(10, 142, 114, 2, 143, 115),
  1125 + array(19, 74, 46, 4, 75, 47),
  1126 + array(28, 50, 22, 6, 51, 23),
  1127 + array(33, 46, 16, 4, 47, 17),
  1128 +
  1129 + // 27
  1130 + array(8, 152, 122, 4, 153, 123),
  1131 + array(22, 73, 45, 3, 74, 46),
  1132 + array(8, 53, 23, 26, 54, 24),
  1133 + array(12, 45, 15, 28, 46, 16),
  1134 +
  1135 + // 28
  1136 + array(3, 147, 117, 10, 148, 118),
  1137 + array(3, 73, 45, 23, 74, 46),
  1138 + array(4, 54, 24, 31, 55, 25),
  1139 + array(11, 45, 15, 31, 46, 16),
  1140 +
  1141 + // 29
  1142 + array(7, 146, 116, 7, 147, 117),
  1143 + array(21, 73, 45, 7, 74, 46),
  1144 + array(1, 53, 23, 37, 54, 24),
  1145 + array(19, 45, 15, 26, 46, 16),
  1146 +
  1147 + // 30
  1148 + array(5, 145, 115, 10, 146, 116),
  1149 + array(19, 75, 47, 10, 76, 48),
  1150 + array(15, 54, 24, 25, 55, 25),
  1151 + array(23, 45, 15, 25, 46, 16),
  1152 +
  1153 + // 31
  1154 + array(13, 145, 115, 3, 146, 116),
  1155 + array(2, 74, 46, 29, 75, 47),
  1156 + array(42, 54, 24, 1, 55, 25),
  1157 + array(23, 45, 15, 28, 46, 16),
  1158 +
  1159 + // 32
  1160 + array(17, 145, 115),
  1161 + array(10, 74, 46, 23, 75, 47),
  1162 + array(10, 54, 24, 35, 55, 25),
  1163 + array(19, 45, 15, 35, 46, 16),
  1164 +
  1165 + // 33
  1166 + array(17, 145, 115, 1, 146, 116),
  1167 + array(14, 74, 46, 21, 75, 47),
  1168 + array(29, 54, 24, 19, 55, 25),
  1169 + array(11, 45, 15, 46, 46, 16),
  1170 +
  1171 + // 34
  1172 + array(13, 145, 115, 6, 146, 116),
  1173 + array(14, 74, 46, 23, 75, 47),
  1174 + array(44, 54, 24, 7, 55, 25),
  1175 + array(59, 46, 16, 1, 47, 17),
  1176 +
  1177 + // 35
  1178 + array(12, 151, 121, 7, 152, 122),
  1179 + array(12, 75, 47, 26, 76, 48),
  1180 + array(39, 54, 24, 14, 55, 25),
  1181 + array(22, 45, 15, 41, 46, 16),
  1182 +
  1183 + // 36
  1184 + array(6, 151, 121, 14, 152, 122),
  1185 + array(6, 75, 47, 34, 76, 48),
  1186 + array(46, 54, 24, 10, 55, 25),
  1187 + array(2, 45, 15, 64, 46, 16),
  1188 +
  1189 + // 37
  1190 + array(17, 152, 122, 4, 153, 123),
  1191 + array(29, 74, 46, 14, 75, 47),
  1192 + array(49, 54, 24, 10, 55, 25),
  1193 + array(24, 45, 15, 46, 46, 16),
  1194 +
  1195 + // 38
  1196 + array(4, 152, 122, 18, 153, 123),
  1197 + array(13, 74, 46, 32, 75, 47),
  1198 + array(48, 54, 24, 14, 55, 25),
  1199 + array(42, 45, 15, 32, 46, 16),
  1200 +
  1201 + // 39
  1202 + array(20, 147, 117, 4, 148, 118),
  1203 + array(40, 75, 47, 7, 76, 48),
  1204 + array(43, 54, 24, 22, 55, 25),
  1205 + array(10, 45, 15, 67, 46, 16),
  1206 +
  1207 + // 40
  1208 + array(19, 148, 118, 6, 149, 119),
  1209 + array(18, 75, 47, 31, 76, 48),
  1210 + array(34, 54, 24, 34, 55, 25),
  1211 + array(20, 45, 15, 61, 46, 16)
  1212 +
  1213 + );
  1214 +
  1215 + function __construct($totalCount, $dataCount)
  1216 + {
  1217 + $this->totalCount = $totalCount;
  1218 + $this->dataCount = $dataCount;
  1219 + }
  1220 +
  1221 + function getDataCount()
  1222 + {
  1223 + return $this->dataCount;
  1224 + }
  1225 +
  1226 + function getTotalCount()
  1227 + {
  1228 + return $this->totalCount;
  1229 + }
  1230 +
  1231 + static function getRSBlocks($typeNumber, $errorCorrectLevel)
  1232 + {
  1233 +
  1234 + $rsBlock = QRRSBlock::getRsBlockTable($typeNumber, $errorCorrectLevel);
  1235 + $length = count($rsBlock) / 3;
  1236 +
  1237 + $list = array();
  1238 +
  1239 + for ($i = 0; $i < $length; $i++) {
  1240 +
  1241 + $count = $rsBlock[$i * 3 + 0];
  1242 + $totalCount = $rsBlock[$i * 3 + 1];
  1243 + $dataCount = $rsBlock[$i * 3 + 2];
  1244 +
  1245 + for ($j = 0; $j < $count; $j++) {
  1246 + $list[] = new QRRSBlock($totalCount, $dataCount);
  1247 + }
  1248 + }
  1249 +
  1250 + return $list;
  1251 + }
  1252 +
  1253 + static function getRsBlockTable($typeNumber, $errorCorrectLevel)
  1254 + {
  1255 +
  1256 + switch ($errorCorrectLevel) {
  1257 + case QR_ERROR_CORRECT_LEVEL_L :
  1258 + return self::$QR_RS_BLOCK_TABLE[($typeNumber - 1) * 4 + 0];
  1259 + case QR_ERROR_CORRECT_LEVEL_M :
  1260 + return self::$QR_RS_BLOCK_TABLE[($typeNumber - 1) * 4 + 1];
  1261 + case QR_ERROR_CORRECT_LEVEL_Q :
  1262 + return self::$QR_RS_BLOCK_TABLE[($typeNumber - 1) * 4 + 2];
  1263 + case QR_ERROR_CORRECT_LEVEL_H :
  1264 + return self::$QR_RS_BLOCK_TABLE[($typeNumber - 1) * 4 + 3];
  1265 + default :
  1266 + trigger_error("tn:$typeNumber/ecl:$errorCorrectLevel", E_USER_ERROR);
  1267 + }
  1268 + }
  1269 +}
  1270 +
  1271 +//---------------------------------------------------------------
  1272 +// QRNumber
  1273 +//---------------------------------------------------------------
  1274 +
  1275 +class QRNumber extends QRData
  1276 +{
  1277 +
  1278 + function __construct($data)
  1279 + {
  1280 + parent::__construct(QR_MODE_NUMBER, $data);
  1281 + }
  1282 +
  1283 + function write(&$buffer)
  1284 + {
  1285 +
  1286 + $data = $this->getData();
  1287 +
  1288 + $i = 0;
  1289 +
  1290 + while ($i + 2 < strlen($data)) {
  1291 + $num = QRNumber::parseInt(substr($data, $i, 3));
  1292 + $buffer->put($num, 10);
  1293 + $i += 3;
  1294 + }
  1295 +
  1296 + if ($i < strlen($data)) {
  1297 +
  1298 + if (strlen($data) - $i == 1) {
  1299 + $num = QRNumber::parseInt(substr($data, $i, $i + 1));
  1300 + $buffer->put($num, 4);
  1301 + } else if (strlen($data) - $i == 2) {
  1302 + $num = QRNumber::parseInt(substr($data, $i, $i + 2));
  1303 + $buffer->put($num, 7);
  1304 + }
  1305 + }
  1306 + }
  1307 +
  1308 + static function parseInt($s)
  1309 + {
  1310 +
  1311 + $num = 0;
  1312 + for ($i = 0; $i < strlen($s); $i++) {
  1313 + $num = $num * 10 + QRNumber::parseIntAt(ord($s[$i]));
  1314 + }
  1315 + return $num;
  1316 + }
  1317 +
  1318 + static function parseIntAt($c)
  1319 + {
  1320 +
  1321 + if (QRUtil::toCharCode('0') <= $c && $c <= QRUtil::toCharCode('9')) {
  1322 + return $c - QRUtil::toCharCode('0');
  1323 + }
  1324 +
  1325 + trigger_error("illegal char : $c", E_USER_ERROR);
  1326 + }
  1327 +}
  1328 +
  1329 +//---------------------------------------------------------------
  1330 +// QRKanji
  1331 +//---------------------------------------------------------------
  1332 +
  1333 +class QRKanji extends QRData
  1334 +{
  1335 +
  1336 + function __construct($data)
  1337 + {
  1338 + parent::__construct(QR_MODE_KANJI, $data);
  1339 + }
  1340 +
  1341 + function write(&$buffer)
  1342 + {
  1343 +
  1344 + $data = $this->getData();
  1345 +
  1346 + $i = 0;
  1347 +
  1348 + while ($i + 1 < strlen($data)) {
  1349 +
  1350 + $c = ((0xff & ord($data[$i])) << 8) | (0xff & ord($data[$i + 1]));
  1351 +
  1352 + if (0x8140 <= $c && $c <= 0x9FFC) {
  1353 + $c -= 0x8140;
  1354 + } else if (0xE040 <= $c && $c <= 0xEBBF) {
  1355 + $c -= 0xC140;
  1356 + } else {
  1357 + trigger_error("illegal char at " . ($i + 1) . "/$c", E_USER_ERROR);
  1358 + }
  1359 +
  1360 + $c = (($c >> 8) & 0xff) * 0xC0 + ($c & 0xff);
  1361 +
  1362 + $buffer->put($c, 13);
  1363 +
  1364 + $i += 2;
  1365 + }
  1366 +
  1367 + if ($i < strlen($data)) {
  1368 + trigger_error("illegal char at " . ($i + 1), E_USER_ERROR);
  1369 + }
  1370 + }
  1371 +
  1372 + function getLength()
  1373 + {
  1374 + return floor(strlen($this->getData()) / 2);
  1375 + }
  1376 +}
  1377 +
  1378 +//---------------------------------------------------------------
  1379 +// QRAlphaNum
  1380 +//---------------------------------------------------------------
  1381 +
  1382 +class QRAlphaNum extends QRData
  1383 +{
  1384 +
  1385 + function __construct($data)
  1386 + {
  1387 + parent::__construct(QR_MODE_ALPHA_NUM, $data);
  1388 + }
  1389 +
  1390 + function write(&$buffer)
  1391 + {
  1392 +
  1393 + $i = 0;
  1394 + $c = $this->getData();
  1395 +
  1396 + while ($i + 1 < strlen($c)) {
  1397 + $buffer->put(QRAlphaNum::getCode(ord($c[$i])) * 45
  1398 + + QRAlphaNum::getCode(ord($c[$i + 1])), 11);
  1399 + $i += 2;
  1400 + }
  1401 +
  1402 + if ($i < strlen($c)) {
  1403 + $buffer->put(QRAlphaNum::getCode(ord($c[$i])), 6);
  1404 + }
  1405 + }
  1406 +
  1407 + static function getCode($c)
  1408 + {
  1409 +
  1410 + if (QRUtil::toCharCode('0') <= $c
  1411 + && $c <= QRUtil::toCharCode('9')) {
  1412 + return $c - QRUtil::toCharCode('0');
  1413 + } else if (QRUtil::toCharCode('A') <= $c
  1414 + && $c <= QRUtil::toCharCode('Z')) {
  1415 + return $c - QRUtil::toCharCode('A') + 10;
  1416 + } else {
  1417 + switch ($c) {
  1418 + case QRUtil::toCharCode(' ') :
  1419 + return 36;
  1420 + case QRUtil::toCharCode('$') :
  1421 + return 37;
  1422 + case QRUtil::toCharCode('%') :
  1423 + return 38;
  1424 + case QRUtil::toCharCode('*') :
  1425 + return 39;
  1426 + case QRUtil::toCharCode('+') :
  1427 + return 40;
  1428 + case QRUtil::toCharCode('-') :
  1429 + return 41;
  1430 + case QRUtil::toCharCode('.') :
  1431 + return 42;
  1432 + case QRUtil::toCharCode('/') :
  1433 + return 43;
  1434 + case QRUtil::toCharCode(':') :
  1435 + return 44;
  1436 + default :
  1437 + trigger_error("illegal char : $c", E_USER_ERROR);
  1438 + }
  1439 + }
  1440 +
  1441 + }
  1442 +}
  1443 +
  1444 +//---------------------------------------------------------------
  1445 +// QR8BitByte
  1446 +//---------------------------------------------------------------
  1447 +
  1448 +class QR8BitByte extends QRData
  1449 +{
  1450 +
  1451 + function __construct($data)
  1452 + {
  1453 + parent::__construct(QR_MODE_8BIT_BYTE, $data);
  1454 + }
  1455 +
  1456 + function write(&$buffer)
  1457 + {
  1458 +
  1459 + $data = $this->getData();
  1460 + for ($i = 0; $i < strlen($data); $i++) {
  1461 + $buffer->put(ord($data[$i]), 8);
  1462 + }
  1463 + }
  1464 +
  1465 +}
  1466 +
  1467 +//---------------------------------------------------------------
  1468 +// QRData
  1469 +//---------------------------------------------------------------
  1470 +
  1471 +abstract class QRData
  1472 +{
  1473 +
  1474 + var $mode;
  1475 +
  1476 + var $data;
  1477 +
  1478 + function __construct($mode, $data)
  1479 + {
  1480 + $this->mode = $mode;
  1481 + $this->data = $data;
  1482 + }
  1483 +
  1484 + function getMode()
  1485 + {
  1486 + return $this->mode;
  1487 + }
  1488 +
  1489 + function getData()
  1490 + {
  1491 + return $this->data;
  1492 + }
  1493 +
  1494 + /**
  1495 + * @return int
  1496 + */
  1497 + function getLength()
  1498 + {
  1499 + return strlen($this->getData());
  1500 + }
  1501 +
  1502 + /**
  1503 + * @param \QRBitBuffer $buffer
  1504 + */
  1505 + abstract function write(&$buffer);
  1506 +
  1507 + function getLengthInBits($type)
  1508 + {
  1509 +
  1510 + if (1 <= $type && $type < 10) {
  1511 +
  1512 + // 1 - 9
  1513 +
  1514 + switch ($this->mode) {
  1515 + case QR_MODE_NUMBER :
  1516 + return 10;
  1517 + case QR_MODE_ALPHA_NUM :
  1518 + return 9;
  1519 + case QR_MODE_8BIT_BYTE :
  1520 + return 8;
  1521 + case QR_MODE_KANJI :
  1522 + return 8;
  1523 + default :
  1524 + trigger_error("mode:$this->mode", E_USER_ERROR);
  1525 + }
  1526 +
  1527 + } else if ($type < 27) {
  1528 +
  1529 + // 10 - 26
  1530 +
  1531 + switch ($this->mode) {
  1532 + case QR_MODE_NUMBER :
  1533 + return 12;
  1534 + case QR_MODE_ALPHA_NUM :
  1535 + return 11;
  1536 + case QR_MODE_8BIT_BYTE :
  1537 + return 16;
  1538 + case QR_MODE_KANJI :
  1539 + return 10;
  1540 + default :
  1541 + trigger_error("mode:$this->mode", E_USER_ERROR);
  1542 + }
  1543 +
  1544 + } else if ($type < 41) {
  1545 +
  1546 + // 27 - 40
  1547 +
  1548 + switch ($this->mode) {
  1549 + case QR_MODE_NUMBER :
  1550 + return 14;
  1551 + case QR_MODE_ALPHA_NUM :
  1552 + return 13;
  1553 + case QR_MODE_8BIT_BYTE :
  1554 + return 16;
  1555 + case QR_MODE_KANJI :
  1556 + return 12;
  1557 + default :
  1558 + trigger_error("mode:$this->mode", E_USER_ERROR);
  1559 + }
  1560 +
  1561 + } else {
  1562 + trigger_error("mode:$this->mode", E_USER_ERROR);
  1563 + }
  1564 + }
  1565 +
  1566 +}
  1567 +
  1568 +//---------------------------------------------------------------
  1569 +// QRMath
  1570 +//---------------------------------------------------------------
  1571 +
  1572 +class QRMath
  1573 +{
  1574 +
  1575 + static $QR_MATH_EXP_TABLE = null;
  1576 + static $QR_MATH_LOG_TABLE = null;
  1577 +
  1578 + static function init()
  1579 + {
  1580 +
  1581 + self::$QR_MATH_EXP_TABLE = QRMath::createNumArray(256);
  1582 +
  1583 + for ($i = 0; $i < 8; $i++) {
  1584 + self::$QR_MATH_EXP_TABLE[$i] = 1 << $i;
  1585 + }
  1586 +
  1587 + for ($i = 8; $i < 256; $i++) {
  1588 + self::$QR_MATH_EXP_TABLE[$i] = self::$QR_MATH_EXP_TABLE[$i - 4]
  1589 + ^ self::$QR_MATH_EXP_TABLE[$i - 5]
  1590 + ^ self::$QR_MATH_EXP_TABLE[$i - 6]
  1591 + ^ self::$QR_MATH_EXP_TABLE[$i - 8];
  1592 + }
  1593 +
  1594 + self::$QR_MATH_LOG_TABLE = QRMath::createNumArray(256);
  1595 +
  1596 + for ($i = 0; $i < 255; $i++) {
  1597 + self::$QR_MATH_LOG_TABLE[self::$QR_MATH_EXP_TABLE[$i]] = $i;
  1598 + }
  1599 + }
  1600 +
  1601 + static function createNumArray($length)
  1602 + {
  1603 + $num_array = array();
  1604 + for ($i = 0; $i < $length; $i++) {
  1605 + $num_array[] = 0;
  1606 + }
  1607 + return $num_array;
  1608 + }
  1609 +
  1610 + static function glog($n)
  1611 + {
  1612 +
  1613 + if ($n < 1) {
  1614 + trigger_error("log($n)", E_USER_ERROR);
  1615 + }
  1616 +
  1617 + return self::$QR_MATH_LOG_TABLE[$n];
  1618 + }
  1619 +
  1620 + static function gexp($n)
  1621 + {
  1622 +
  1623 + while ($n < 0) {
  1624 + $n += 255;
  1625 + }
  1626 +
  1627 + while ($n >= 256) {
  1628 + $n -= 255;
  1629 + }
  1630 +
  1631 + return self::$QR_MATH_EXP_TABLE[$n];
  1632 + }
  1633 +}
  1634 +
  1635 +// init static table
  1636 +QRMath::init();
  1637 +
  1638 +//---------------------------------------------------------------
  1639 +// QRPolynomial
  1640 +//---------------------------------------------------------------
  1641 +
  1642 +class QRPolynomial
  1643 +{
  1644 +
  1645 + var $num;
  1646 +
  1647 + function __construct($num, $shift = 0)
  1648 + {
  1649 +
  1650 + $offset = 0;
  1651 +
  1652 + while ($offset < count($num) && $num[$offset] == 0) {
  1653 + $offset++;
  1654 + }
  1655 +
  1656 + $this->num = QRMath::createNumArray(count($num) - $offset + $shift);
  1657 + for ($i = 0; $i < count($num) - $offset; $i++) {
  1658 + $this->num[$i] = $num[$i + $offset];
  1659 + }
  1660 + }
  1661 +
  1662 + function get($index)
  1663 + {
  1664 + return $this->num[$index];
  1665 + }
  1666 +
  1667 + function getLength()
  1668 + {
  1669 + return count($this->num);
  1670 + }
  1671 +
  1672 + // PHP5
  1673 + function __toString()
  1674 + {
  1675 + return $this->toString();
  1676 + }
  1677 +
  1678 + function toString()
  1679 + {
  1680 +
  1681 + $buffer = "";
  1682 +
  1683 + for ($i = 0; $i < $this->getLength(); $i++) {
  1684 + if ($i > 0) {
  1685 + $buffer .= ",";
  1686 + }
  1687 + $buffer .= $this->get($i);
  1688 + }
  1689 +
  1690 + return $buffer;
  1691 + }
  1692 +
  1693 + function toLogString()
  1694 + {
  1695 +
  1696 + $buffer = "";
  1697 +
  1698 + for ($i = 0; $i < $this->getLength(); $i++) {
  1699 + if ($i > 0) {
  1700 + $buffer .= ",";
  1701 + }
  1702 + $buffer .= QRMath::glog($this->get($i));
  1703 + }
  1704 +
  1705 + return $buffer;
  1706 + }
  1707 +
  1708 + /**
  1709 + * @param \QRPolynomial $e
  1710 + *
  1711 + * @return \QRPolynomial
  1712 + */
  1713 + function multiply($e)
  1714 + {
  1715 +
  1716 + $num = QRMath::createNumArray($this->getLength() + $e->getLength() - 1);
  1717 +
  1718 + for ($i = 0; $i < $this->getLength(); $i++) {
  1719 + $vi = QRMath::glog($this->get($i));
  1720 +
  1721 + for ($j = 0; $j < $e->getLength(); $j++) {
  1722 + $num[$i + $j] ^= QRMath::gexp($vi + QRMath::glog($e->get($j)));
  1723 + }
  1724 + }
  1725 +
  1726 + return new QRPolynomial($num);
  1727 + }
  1728 +
  1729 + /**
  1730 + * @param \QRPolynomial $e
  1731 + *
  1732 + * @return $this|\QRPolynomial
  1733 + */
  1734 + function mod($e)
  1735 + {
  1736 +
  1737 + if ($this->getLength() - $e->getLength() < 0) {
  1738 + return $this;
  1739 + }
  1740 +
  1741 + $ratio = QRMath::glog($this->get(0)) - QRMath::glog($e->get(0));
  1742 +
  1743 + $num = QRMath::createNumArray($this->getLength());
  1744 + for ($i = 0; $i < $this->getLength(); $i++) {
  1745 + $num[$i] = $this->get($i);
  1746 + }
  1747 +
  1748 + for ($i = 0; $i < $e->getLength(); $i++) {
  1749 + $num[$i] ^= QRMath::gexp(QRMath::glog($e->get($i)) + $ratio);
  1750 + }
  1751 +
  1752 + $newPolynomial = new QRPolynomial($num);
  1753 + return $newPolynomial->mod($e);
  1754 + }
  1755 +}
  1756 +
  1757 +//---------------------------------------------------------------
  1758 +// Mode
  1759 +//---------------------------------------------------------------
  1760 +
  1761 +define("QR_MODE_NUMBER", 1 << 0);
  1762 +define("QR_MODE_ALPHA_NUM", 1 << 1);
  1763 +define("QR_MODE_8BIT_BYTE", 1 << 2);
  1764 +define("QR_MODE_KANJI", 1 << 3);
  1765 +
  1766 +//---------------------------------------------------------------
  1767 +// MaskPattern
  1768 +//---------------------------------------------------------------
  1769 +
  1770 +define("QR_MASK_PATTERN000", 0);
  1771 +define("QR_MASK_PATTERN001", 1);
  1772 +define("QR_MASK_PATTERN010", 2);
  1773 +define("QR_MASK_PATTERN011", 3);
  1774 +define("QR_MASK_PATTERN100", 4);
  1775 +define("QR_MASK_PATTERN101", 5);
  1776 +define("QR_MASK_PATTERN110", 6);
  1777 +define("QR_MASK_PATTERN111", 7);
  1778 +
  1779 +//---------------------------------------------------------------
  1780 +// ErrorCorrectLevel
  1781 +
  1782 +// 7%.
  1783 +define("QR_ERROR_CORRECT_LEVEL_L", 1);
  1784 +// 15%.
  1785 +define("QR_ERROR_CORRECT_LEVEL_M", 0);
  1786 +// 25%.
  1787 +define("QR_ERROR_CORRECT_LEVEL_Q", 3);
  1788 +// 30%.
  1789 +define("QR_ERROR_CORRECT_LEVEL_H", 2);
  1790 +
  1791 +
  1792 +//---------------------------------------------------------------
  1793 +// QRBitBuffer
  1794 +//---------------------------------------------------------------
  1795 +
  1796 +class QRBitBuffer
  1797 +{
  1798 +
  1799 + var $buffer;
  1800 + var $length;
  1801 +
  1802 + function __construct()
  1803 + {
  1804 + $this->buffer = array();
  1805 + $this->length = 0;
  1806 + }
  1807 +
  1808 + function getBuffer()
  1809 + {
  1810 + return $this->buffer;
  1811 + }
  1812 +
  1813 + function getLengthInBits()
  1814 + {
  1815 + return $this->length;
  1816 + }
  1817 +
  1818 + function __toString()
  1819 + {
  1820 + $buffer = "";
  1821 + for ($i = 0; $i < $this->getLengthInBits(); $i++) {
  1822 + $buffer .= $this->get($i) ? '1' : '0';
  1823 + }
  1824 + return $buffer;
  1825 + }
  1826 +
  1827 + function get($index)
  1828 + {
  1829 + $bufIndex = (int)floor($index / 8);
  1830 + return (($this->buffer[$bufIndex] >> (7 - $index % 8)) & 1) == 1;
  1831 + }
  1832 +
  1833 + function put($num, $length)
  1834 + {
  1835 +
  1836 + for ($i = 0; $i < $length; $i++) {
  1837 + $this->putBit((($num >> ($length - $i - 1)) & 1) == 1);
  1838 + }
  1839 + }
  1840 +
  1841 + function putBit($bit)
  1842 + {
  1843 +
  1844 + $bufIndex = (int)floor($this->length / 8);
  1845 + if (count($this->buffer) <= $bufIndex) {
  1846 + $this->buffer[] = 0;
  1847 + }
  1848 +
  1849 + if ($bit) {
  1850 + $this->buffer[$bufIndex] |= (0x80 >> ($this->length % 8));
  1851 + }
  1852 +
  1853 + $this->length++;
  1854 + }
  1855 +}
  1856 +
  1 +<?php
  2 +
  3 +namespace addons\epay\library;
  4 +
  5 +class RedirectResponse extends \Symfony\Component\HttpFoundation\RedirectResponse implements \JsonSerializable, \Serializable
  6 +{
  7 + public function __toString()
  8 + {
  9 + return $this->getContent();
  10 + }
  11 +
  12 + public function setTargetUrl($url)
  13 + {
  14 + if ('' === ($url ?? '')) {
  15 + throw new \InvalidArgumentException('无法跳转到空页面');
  16 + }
  17 +
  18 + $this->targetUrl = $url;
  19 +
  20 + $this->setContent(
  21 + sprintf('<!DOCTYPE html>
  22 +<html>
  23 + <head>
  24 + <meta charset="UTF-8" />
  25 + <meta http-equiv="refresh" content="0;url=\'%1$s\'" />
  26 +
  27 + <title>正在跳转支付 %1$s</title>
  28 + </head>
  29 + <body>
  30 + <div id="redirect" style="display:none;">正在跳转支付 <a href="%1$s">%1$s</a></div>
  31 + <script type="text/javascript">
  32 + setTimeout(function(){
  33 + document.getElementById("redirect").style.display = "block";
  34 + }, 1000);
  35 + </script>
  36 + </body>
  37 +</html>', htmlspecialchars($url, \ENT_QUOTES, 'UTF-8')));
  38 +
  39 + $this->headers->set('Location', $url);
  40 +
  41 + return $this;
  42 + }
  43 +
  44 + public function jsonSerialize()
  45 + {
  46 + return $this->getContent();
  47 + }
  48 +
  49 + public function serialize()
  50 + {
  51 + return serialize($this->content);
  52 + }
  53 +
  54 + public function unserialize($serialized)
  55 + {
  56 + return $this->content = unserialize($serialized);
  57 + }
  58 +}
  1 +<?php
  2 +
  3 +namespace addons\epay\library;
  4 +
  5 +class Response extends \Symfony\Component\HttpFoundation\Response implements \JsonSerializable, \Serializable
  6 +{
  7 + public function __toString()
  8 + {
  9 + return $this->getContent();
  10 + }
  11 +
  12 + public function jsonSerialize()
  13 + {
  14 + return $this->getContent();
  15 + }
  16 +
  17 + public function serialize()
  18 + {
  19 + return serialize($this->content);
  20 + }
  21 +
  22 + public function unserialize($serialized)
  23 + {
  24 + return $this->content = unserialize($serialized);
  25 + }
  26 +}
  1 +<?php
  2 +
  3 +namespace addons\epay\library;
  4 +
  5 +use addons\third\model\Third;
  6 +use app\common\library\Auth;
  7 +use Exception;
  8 +use think\Session;
  9 +use Yansongda\Pay\Pay;
  10 +use Yansongda\Supports\Str;
  11 +
  12 +/**
  13 + * 订单服务类
  14 + *
  15 + * @package addons\epay\library
  16 + */
  17 +class Service
  18 +{
  19 +
  20 + /**
  21 + * 提交订单
  22 + * @param array|float $amount 订单金额
  23 + * @param string $orderid 订单号
  24 + * @param string $type 支付类型,可选alipay或wechat
  25 + * @param string $title 订单标题
  26 + * @param string $notifyurl 通知回调URL
  27 + * @param string $returnurl 跳转返回URL
  28 + * @param string $method 支付方法
  29 + * @return Response|RedirectResponse|Collection
  30 + * @throws Exception
  31 + */
  32 + public static function submitOrder($amount, $orderid = null, $type = null, $title = null, $notifyurl = null, $returnurl = null, $method = null, $openid = '')
  33 + {
  34 + if (!is_array($amount)) {
  35 + $params = [
  36 + 'amount' => $amount,
  37 + 'orderid' => $orderid,
  38 + 'type' => $type,
  39 + 'title' => $title,
  40 + 'notifyurl' => $notifyurl,
  41 + 'returnurl' => $returnurl,
  42 + 'method' => $method,
  43 + 'openid' => $openid,
  44 + ];
  45 + } else {
  46 + $params = $amount;
  47 + }
  48 + $type = isset($params['type']) && in_array($params['type'], ['alipay', 'wechat']) ? $params['type'] : 'wechat';
  49 + $method = isset($params['method']) ? $params['method'] : 'web';
  50 + $orderid = isset($params['orderid']) ? $params['orderid'] : date("YmdHis") . mt_rand(100000, 999999);
  51 + $amount = isset($params['amount']) ? $params['amount'] : 1;
  52 + $title = isset($params['title']) ? $params['title'] : "支付";
  53 + $auth_code = isset($params['auth_code']) ? $params['auth_code'] : '';
  54 + $openid = isset($params['openid']) ? $params['openid'] : '';
  55 +
  56 + $request = request();
  57 + $notifyurl = isset($params['notifyurl']) ? $params['notifyurl'] : $request->root(true) . '/addons/epay/index/' . $type . 'notify';
  58 + $returnurl = isset($params['returnurl']) ? $params['returnurl'] : $request->root(true) . '/addons/epay/index/' . $type . 'return/out_trade_no/' . $orderid;
  59 + $html = '';
  60 + $config = Service::getConfig($type);
  61 + $config['notify_url'] = $notifyurl;
  62 + $config['return_url'] = $returnurl;
  63 + $isWechat = strpos($request->server('HTTP_USER_AGENT'), 'MicroMessenger') !== false;
  64 +
  65 + $result = null;
  66 + if ($type == 'alipay') {
  67 + //如果是PC支付,判断当前环境,进行跳转
  68 + if ($method == 'web') {
  69 + //如果是微信环境或后台配置PC使用扫码支付
  70 + if ($isWechat || $config['scanpay']) {
  71 + Session::set("alipayorderdata", $params);
  72 + $url = addon_url('epay/api/alipay', [], true, true);
  73 + return RedirectResponse::create($url);
  74 + } elseif ($request->isMobile()) {
  75 + $method = 'wap';
  76 + }
  77 + }
  78 + //创建支付对象
  79 + $pay = Pay::alipay($config);
  80 + $params = [
  81 + 'out_trade_no' => $orderid,//你的订单号
  82 + 'total_amount' => $amount,//单位元
  83 + 'subject' => $title,
  84 + ];
  85 +
  86 + switch ($method) {
  87 + case 'web':
  88 + //电脑支付
  89 + $result = $pay->web($params);
  90 + break;
  91 + case 'wap':
  92 + //手机网页支付
  93 + $result = $pay->wap($params);
  94 + break;
  95 + case 'app':
  96 + //APP支付
  97 + $result = $pay->app($params);
  98 + break;
  99 + case 'scan':
  100 + //扫码支付
  101 + $result = $pay->scan($params);
  102 + break;
  103 + case 'pos':
  104 + //刷卡支付必须要有auth_code
  105 + $params['auth_code'] = $auth_code;
  106 + $result = $pay->pos($params);
  107 + break;
  108 + default:
  109 + }
  110 + } else {
  111 + //如果是PC支付,判断当前环境,进行跳转
  112 + if ($method == 'web') {
  113 + //如果是移动端,但不是微信环境
  114 + if ($request->isMobile() && !$isWechat) {
  115 + $method = 'wap';
  116 + } else {
  117 + Session::set("wechatorderdata", $params);
  118 + $url = addon_url('epay/api/wechat', [], true, true);
  119 + return RedirectResponse::create($url);
  120 + }
  121 + }
  122 +
  123 + //创建支付对象
  124 + $pay = Pay::wechat($config);
  125 + $params = [
  126 + 'out_trade_no' => $orderid,//你的订单号
  127 + 'body' => $title,
  128 + 'total_fee' => $amount * 100, //单位分
  129 + ];
  130 + switch ($method) {
  131 + //case 'web':
  132 + // //电脑支付,跳转到自定义展示页面(FastAdmin独有)
  133 + // $result = $pay->web($params);
  134 + // break;
  135 + case 'mp':
  136 + //公众号支付
  137 + //公众号支付必须有openid
  138 + $params['openid'] = $openid;
  139 + $result = $pay->mp($params);
  140 + break;
  141 + case 'wap':
  142 + //手机网页支付,跳转
  143 + $params['spbill_create_ip'] = $request->ip(0, false);
  144 + $result = $pay->wap($params);
  145 + break;
  146 + case 'app':
  147 + //APP支付,直接返回字符串
  148 + $result = $pay->app($params);
  149 + break;
  150 + case 'scan':
  151 + //扫码支付,直接返回字符串
  152 + $result = $pay->scan($params);
  153 + break;
  154 + case 'pos':
  155 + //刷卡支付,直接返回字符串
  156 + //刷卡支付必须要有auth_code
  157 + $params['auth_code'] = $auth_code;
  158 + $result = $pay->pos($params);
  159 + break;
  160 + case 'miniapp':
  161 + //小程序支付,直接返回字符串
  162 + //小程序支付必须要有openid
  163 + $params['openid'] = $openid;
  164 + $result = $pay->miniapp($params);
  165 + break;
  166 + default:
  167 + }
  168 + }
  169 +
  170 + //使用重写的Response类、RedirectResponse、Collection类
  171 + if ($result instanceof \Symfony\Component\HttpFoundation\RedirectResponse) {
  172 + $result = RedirectResponse::create($result->getTargetUrl());
  173 + } elseif ($result instanceof \Symfony\Component\HttpFoundation\Response) {
  174 + $result = Response::create($result->getContent());
  175 + } elseif ($result instanceof \Yansongda\Supports\Collection) {
  176 + $result = Collection::make($result->all());
  177 + }
  178 +
  179 + return $result;
  180 + }
  181 +
  182 + /**
  183 + * 验证回调是否成功
  184 + * @param string $type 支付类型
  185 + * @param array $config 配置信息
  186 + * @return bool|\Yansongda\Pay\Gateways\Alipay|\Yansongda\Pay\Gateways\Wechat
  187 + */
  188 + public static function checkNotify($type, $config = [])
  189 + {
  190 + $type = strtolower($type);
  191 + if (!in_array($type, ['wechat', 'alipay'])) {
  192 + return false;
  193 + }
  194 + try {
  195 + $config = self::getConfig($type);
  196 + $pay = $type == 'wechat' ? Pay::wechat($config) : Pay::alipay($config);
  197 + $data = $pay->verify();
  198 +
  199 + if ($type == 'alipay') {
  200 + if (in_array($data['trade_status'], ['TRADE_SUCCESS', 'TRADE_FINISHED'])) {
  201 + return $pay;
  202 + }
  203 + } else {
  204 + return $pay;
  205 + }
  206 + } catch (Exception $e) {
  207 + return false;
  208 + }
  209 +
  210 + return false;
  211 + }
  212 +
  213 + /**
  214 + * 验证返回是否成功,请勿用于判断是否支付成功的逻辑验证
  215 + * 已弃用
  216 + *
  217 + * @param string $type 支付类型
  218 + * @param array $config 配置信息
  219 + * @return bool
  220 + * @deprecated 已弃用,请勿用于逻辑验证
  221 + */
  222 + public static function checkReturn($type, $config = [])
  223 + {
  224 + //由于PC及移动端无法获取请求的参数信息,取消return验证,均返回true
  225 + return true;
  226 + }
  227 +
  228 + /**
  229 + * 获取配置
  230 + * @param string $type 支付类型
  231 + * @return array|mixed
  232 + */
  233 + public static function getConfig($type = 'wechat')
  234 + {
  235 + $config = get_addon_config('epay');
  236 + $config = isset($config[$type]) ? $config[$type] : $config['wechat'];
  237 + if ($config['log']) {
  238 + $config['log'] = [
  239 + 'file' => LOG_PATH . 'epaylogs' . DS . $type . '-' . date("Y-m-d") . '.log',
  240 + 'level' => 'debug'
  241 + ];
  242 + }
  243 + if (isset($config['cert_client']) && substr($config['cert_client'], 0, 8) == '/addons/') {
  244 + $config['cert_client'] = ROOT_PATH . str_replace('/', DS, substr($config['cert_client'], 1));
  245 + }
  246 + if (isset($config['cert_key']) && substr($config['cert_key'], 0, 8) == '/addons/') {
  247 + $config['cert_key'] = ROOT_PATH . str_replace('/', DS, substr($config['cert_key'], 1));
  248 + }
  249 + if (isset($config['app_cert_public_key']) && substr($config['app_cert_public_key'], 0, 8) == '/addons/') {
  250 + $config['app_cert_public_key'] = ROOT_PATH . str_replace('/', DS, substr($config['app_cert_public_key'], 1));
  251 + }
  252 + if (isset($config['alipay_root_cert']) && substr($config['alipay_root_cert'], 0, 8) == '/addons/') {
  253 + $config['alipay_root_cert'] = ROOT_PATH . str_replace('/', DS, substr($config['alipay_root_cert'], 1));
  254 + }
  255 + if (isset($config['ali_public_key']) && (Str::endsWith($config['ali_public_key'], '.crt') || Str::endsWith($config['ali_public_key'], '.pem'))) {
  256 + $config['ali_public_key'] = ROOT_PATH . str_replace('/', DS, substr($config['ali_public_key'], 1));
  257 + }
  258 + // 可选
  259 + $config['http'] = [
  260 + 'timeout' => 10,
  261 + 'connect_timeout' => 10,
  262 + // 更多配置项请参考 [Guzzle](https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html)
  263 + ];
  264 +
  265 + $config['notify_url'] = empty($config['notify_url']) ? addon_url('epay/api/notifyx', [], false) . '/type/' . $type : $config['notify_url'];
  266 + $config['notify_url'] = !preg_match("/^(http:\/\/|https:\/\/)/i", $config['notify_url']) ? request()->root(true) . $config['notify_url'] : $config['notify_url'];
  267 + $config['return_url'] = empty($config['return_url']) ? addon_url('epay/api/returnx', [], false) . '/type/' . $type : $config['return_url'];
  268 + $config['return_url'] = !preg_match("/^(http:\/\/|https:\/\/)/i", $config['return_url']) ? request()->root(true) . $config['return_url'] : $config['return_url'];
  269 + return $config;
  270 + }
  271 +
  272 + /**
  273 + * 获取微信Openid
  274 + *
  275 + * @return mixed|string
  276 + */
  277 + public static function getOpenid()
  278 + {
  279 + $config = self::getConfig('wechat');
  280 + $openid = '';
  281 + $auth = Auth::instance();
  282 + if ($auth->isLogin()) {
  283 + $third = get_addon_info('third');
  284 + if ($third && $third['state']) {
  285 + $thirdInfo = Third::where('user_id', $auth->id)->where('platform', 'wechat')->where('apptype', 'mp')->find();
  286 + $openid = $thirdInfo ? $thirdInfo['openid'] : '';
  287 + }
  288 + }
  289 + if (!$openid) {
  290 + $openid = Session::get("openid");
  291 +
  292 + //如果未传openid,则去读取openid
  293 + if (!$openid) {
  294 + $wechat = new Wechat($config['app_id'], $config['app_secret']);
  295 + $openid = $wechat->getOpenid();
  296 + }
  297 + }
  298 + return $openid;
  299 + }
  300 +
  301 +}
  1 +<?php
  2 +
  3 +namespace addons\epay\library;
  4 +
  5 +use fast\Http;
  6 +use think\Cache;
  7 +use think\Session;
  8 +
  9 +/**
  10 + * 微信授权
  11 + *
  12 + */
  13 +class Wechat
  14 +{
  15 + private $app_id = '';
  16 + private $app_secret = '';
  17 + private $scope = 'snsapi_userinfo';
  18 +
  19 + public function __construct($app_id, $app_secret)
  20 + {
  21 + $this->app_id = $app_id;
  22 + $this->app_secret = $app_secret;
  23 + }
  24 +
  25 + /**
  26 + * 获取微信授权链接
  27 + *
  28 + * @return string
  29 + */
  30 + public function getAuthorizeUrl()
  31 + {
  32 + $redirect_uri = addon_url('epay/api/wechat', [], true, true);
  33 + $redirect_uri = urlencode($redirect_uri);
  34 + $state = \fast\Random::alnum();
  35 + Session::set('state', $state);
  36 + return "https://open.weixin.qq.com/connect/oauth2/authorize?appid={$this->app_id}&redirect_uri={$redirect_uri}&response_type=code&scope={$this->scope}&state={$state}#wechat_redirect";
  37 + }
  38 +
  39 + /**
  40 + * 获取微信openid
  41 + *
  42 + * @return mixed|string
  43 + */
  44 + public function getOpenid()
  45 + {
  46 + $openid = Session::get('openid');
  47 + if (!$openid) {
  48 + if (!isset($_GET['code'])) {
  49 + $url = $this->getAuthorizeUrl();
  50 +
  51 + Header("Location: $url");
  52 + exit();
  53 + } else {
  54 + $state = Session::get('state');
  55 + if ($state == $_GET['state']) {
  56 + $code = $_GET['code'];
  57 + $token = $this->getAccessToken($code);
  58 + if (!isset($token['openid']) && isset($token['errmsg'])) {
  59 + exception($token['errmsg']);
  60 + }
  61 + $openid = isset($token['openid']) ? $token['openid'] : '';
  62 + if ($openid) {
  63 + Session::set("openid", $openid);
  64 + }
  65 + }
  66 + }
  67 + }
  68 + return $openid;
  69 + }
  70 +
  71 + /**
  72 + * 获取授权token网页授权
  73 + *
  74 + * @param string $code
  75 + * @return mixed|string
  76 + */
  77 + public function getAccessToken($code = '')
  78 + {
  79 + $params = [
  80 + 'appid' => $this->app_id,
  81 + 'secret' => $this->app_secret,
  82 + 'code' => $code,
  83 + 'grant_type' => 'authorization_code'
  84 + ];
  85 + $ret = Http::sendRequest('https://api.weixin.qq.com/sns/oauth2/access_token', $params, 'GET');
  86 + if ($ret['ret']) {
  87 + $ar = json_decode($ret['msg'], true);
  88 + return $ar;
  89 + }
  90 + return [];
  91 + }
  92 +
  93 + public function getJsticket($code = '')
  94 + {
  95 + $jsticket = Session::get('jsticket');
  96 + if (!$jsticket) {
  97 + $token = $this->getAccessToken($code);
  98 + $params = [
  99 + 'access_token' => 'token',
  100 + 'type' => 'jsapi',
  101 + ];
  102 + $ret = Http::sendRequest('https://api.weixin.qq.com/cgi-bin/ticket/getticket', $params, 'GET');
  103 + if ($ret['ret']) {
  104 + $ar = json_decode($ret['msg'], true);
  105 + return $ar;
  106 + }
  107 + }
  108 + return $jsticket;
  109 + }
  110 +}
  1 +<?php
  2 +
  3 +namespace Yansongda\Pay\Contracts;
  4 +
  5 +use Symfony\Component\HttpFoundation\Response;
  6 +use Yansongda\Supports\Collection;
  7 +
  8 +interface GatewayApplicationInterface
  9 +{
  10 + /**
  11 + * To pay.
  12 + *
  13 + * @author yansongda <me@yansonga.cn>
  14 + *
  15 + * @param string $gateway
  16 + * @param array $params
  17 + *
  18 + * @return Collection|Response
  19 + */
  20 + public function pay($gateway, $params);
  21 +
  22 + /**
  23 + * Query an order.
  24 + *
  25 + * @author yansongda <me@yansongda.cn>
  26 + *
  27 + * @param string|array $order
  28 + *
  29 + * @return Collection
  30 + */
  31 + public function find($order, string $type);
  32 +
  33 + /**
  34 + * Refund an order.
  35 + *
  36 + * @author yansongda <me@yansongda.cn>
  37 + *
  38 + * @return Collection
  39 + */
  40 + public function refund(array $order);
  41 +
  42 + /**
  43 + * Cancel an order.
  44 + *
  45 + * @author yansongda <me@yansongda.cn>
  46 + *
  47 + * @param string|array $order
  48 + *
  49 + * @return Collection
  50 + */
  51 + public function cancel($order);
  52 +
  53 + /**
  54 + * Close an order.
  55 + *
  56 + * @author yansongda <me@yansongda.cn>
  57 + *
  58 + * @param string|array $order
  59 + *
  60 + * @return Collection
  61 + */
  62 + public function close($order);
  63 +
  64 + /**
  65 + * Verify a request.
  66 + *
  67 + * @author yansongda <me@yansongda.cn>
  68 + *
  69 + * @param string|array|null $content
  70 + *
  71 + * @return Collection
  72 + */
  73 + public function verify($content, bool $refund);
  74 +
  75 + /**
  76 + * Echo success to server.
  77 + *
  78 + * @author yansongda <me@yansongda.cn>
  79 + *
  80 + * @return Response
  81 + */
  82 + public function success();
  83 +}
  1 +<?php
  2 +
  3 +namespace Yansongda\Pay\Contracts;
  4 +
  5 +use Symfony\Component\HttpFoundation\Response;
  6 +use Yansongda\Supports\Collection;
  7 +
  8 +interface GatewayInterface
  9 +{
  10 + /**
  11 + * Pay an order.
  12 + *
  13 + * @author yansongda <me@yansongda.cn>
  14 + *
  15 + * @param string $endpoint
  16 + *
  17 + * @return Collection|Response
  18 + */
  19 + public function pay($endpoint, array $payload);
  20 +}
  1 +<?php
  2 +
  3 +namespace Yansongda\Pay;
  4 +
  5 +use Exception;
  6 +use Symfony\Component\EventDispatcher\EventDispatcher;
  7 +use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  8 +use Symfony\Contracts\EventDispatcher\Event;
  9 +
  10 +/**
  11 + * @author yansongda <me@yansongda.cn>
  12 + *
  13 + * @method static Event dispatch(Event $event) Dispatches an event to all registered listeners
  14 + * @method static array getListeners($eventName = null) Gets the listeners of a specific event or all listeners sorted by descending priority.
  15 + * @method static int|void getListenerPriority($eventName, $listener) Gets the listener priority for a specific event.
  16 + * @method static bool hasListeners($eventName = null) Checks whether an event has any registered listeners.
  17 + * @method static void addListener($eventName, $listener, $priority = 0) Adds an event listener that listens on the specified events.
  18 + * @method static removeListener($eventName, $listener) Removes an event listener from the specified events.
  19 + * @method static void addSubscriber(EventSubscriberInterface $subscriber) Adds an event subscriber.
  20 + * @method static void removeSubscriber(EventSubscriberInterface $subscriber)
  21 + */
  22 +class Events
  23 +{
  24 + /**
  25 + * dispatcher.
  26 + *
  27 + * @var EventDispatcher
  28 + */
  29 + protected static $dispatcher;
  30 +
  31 + /**
  32 + * Forward call.
  33 + *
  34 + * @author yansongda <me@yansongda.cn>
  35 + *
  36 + * @param string $method
  37 + * @param array $args
  38 + *
  39 + * @throws Exception
  40 + *
  41 + * @return mixed
  42 + */
  43 + public static function __callStatic($method, $args)
  44 + {
  45 + return call_user_func_array([self::getDispatcher(), $method], $args);
  46 + }
  47 +
  48 + /**
  49 + * Forward call.
  50 + *
  51 + * @author yansongda <me@yansongda.cn>
  52 + *
  53 + * @param string $method
  54 + * @param array $args
  55 + *
  56 + * @throws Exception
  57 + *
  58 + * @return mixed
  59 + */
  60 + public function __call($method, $args)
  61 + {
  62 + return call_user_func_array([self::getDispatcher(), $method], $args);
  63 + }
  64 +
  65 + /**
  66 + * setDispatcher.
  67 + *
  68 + * @author yansongda <me@yansongda.cn>
  69 + */
  70 + public static function setDispatcher(EventDispatcher $dispatcher)
  71 + {
  72 + self::$dispatcher = $dispatcher;
  73 + }
  74 +
  75 + /**
  76 + * getDispatcher.
  77 + *
  78 + * @author yansongda <me@yansongda.cn>
  79 + */
  80 + public static function getDispatcher(): EventDispatcher
  81 + {
  82 + if (self::$dispatcher) {
  83 + return self::$dispatcher;
  84 + }
  85 +
  86 + return self::$dispatcher = self::createDispatcher();
  87 + }
  88 +
  89 + /**
  90 + * createDispatcher.
  91 + *
  92 + * @author yansongda <me@yansongda.cn>
  93 + */
  94 + public static function createDispatcher(): EventDispatcher
  95 + {
  96 + return new EventDispatcher();
  97 + }
  98 +}
  1 +<?php
  2 +
  3 +namespace Yansongda\Pay\Events;
  4 +
  5 +class ApiRequested extends Event
  6 +{
  7 + /**
  8 + * Endpoint.
  9 + *
  10 + * @var string
  11 + */
  12 + public $endpoint;
  13 +
  14 + /**
  15 + * Result.
  16 + *
  17 + * @var array
  18 + */
  19 + public $result;
  20 +
  21 + /**
  22 + * Bootstrap.
  23 + */
  24 + public function __construct(string $driver, string $gateway, string $endpoint, array $result)
  25 + {
  26 + $this->endpoint = $endpoint;
  27 + $this->result = $result;
  28 +
  29 + parent::__construct($driver, $gateway);
  30 + }
  31 +}
  1 +<?php
  2 +
  3 +namespace Yansongda\Pay\Events;
  4 +
  5 +class ApiRequesting extends Event
  6 +{
  7 + /**
  8 + * Endpoint.
  9 + *
  10 + * @var string
  11 + */
  12 + public $endpoint;
  13 +
  14 + /**
  15 + * Payload.
  16 + *
  17 + * @var array
  18 + */
  19 + public $payload;
  20 +
  21 + /**
  22 + * Bootstrap.
  23 + */
  24 + public function __construct(string $driver, string $gateway, string $endpoint, array $payload)
  25 + {
  26 + $this->endpoint = $endpoint;
  27 + $this->payload = $payload;
  28 +
  29 + parent::__construct($driver, $gateway);
  30 + }
  31 +}
  1 +<?php
  2 +
  3 +namespace Yansongda\Pay\Events;
  4 +
  5 +use Symfony\Contracts\EventDispatcher\Event as SymfonyEvent;
  6 +
  7 +class Event extends SymfonyEvent
  8 +{
  9 + /**
  10 + * Driver.
  11 + *
  12 + * @var string
  13 + */
  14 + public $driver;
  15 +
  16 + /**
  17 + * Method.
  18 + *
  19 + * @var string
  20 + */
  21 + public $gateway;
  22 +
  23 + /**
  24 + * Extra attributes.
  25 + *
  26 + * @var mixed
  27 + */
  28 + public $attributes;
  29 +
  30 + /**
  31 + * Bootstrap.
  32 + *
  33 + * @author yansongda <me@yansongda.cn>
  34 + */
  35 + public function __construct(string $driver, string $gateway)
  36 + {
  37 + $this->driver = $driver;
  38 + $this->gateway = $gateway;
  39 + }
  40 +}
  1 +<?php
  2 +
  3 +namespace Yansongda\Pay\Events;
  4 +
  5 +class MethodCalled extends Event
  6 +{
  7 + /**
  8 + * endpoint.
  9 + *
  10 + * @var string
  11 + */
  12 + public $endpoint;
  13 +
  14 + /**
  15 + * payload.
  16 + *
  17 + * @var array
  18 + */
  19 + public $payload;
  20 +
  21 + /**
  22 + * Bootstrap.
  23 + *
  24 + * @author yansongda <me@yansongda.cn>
  25 + */
  26 + public function __construct(string $driver, string $gateway, string $endpoint, array $payload = [])
  27 + {
  28 + $this->endpoint = $endpoint;
  29 + $this->payload = $payload;
  30 +
  31 + parent::__construct($driver, $gateway);
  32 + }
  33 +}
  1 +<?php
  2 +
  3 +namespace Yansongda\Pay\Events;
  4 +
  5 +class PayStarted extends Event
  6 +{
  7 + /**
  8 + * Endpoint.
  9 + *
  10 + * @var string
  11 + */
  12 + public $endpoint;
  13 +
  14 + /**
  15 + * Payload.
  16 + *
  17 + * @var array
  18 + */
  19 + public $payload;
  20 +
  21 + /**
  22 + * Bootstrap.
  23 + */
  24 + public function __construct(string $driver, string $gateway, string $endpoint, array $payload)
  25 + {
  26 + $this->endpoint = $endpoint;
  27 + $this->payload = $payload;
  28 +
  29 + parent::__construct($driver, $gateway);
  30 + }
  31 +}
  1 +<?php
  2 +
  3 +namespace Yansongda\Pay\Events;
  4 +
  5 +class PayStarting extends Event
  6 +{
  7 + /**
  8 + * Params.
  9 + *
  10 + * @var array
  11 + */
  12 + public $params;
  13 +
  14 + /**
  15 + * Bootstrap.
  16 + */
  17 + public function __construct(string $driver, string $gateway, array $params)
  18 + {
  19 + $this->params = $params;
  20 +
  21 + parent::__construct($driver, $gateway);
  22 + }
  23 +}
  1 +<?php
  2 +
  3 +namespace Yansongda\Pay\Events;
  4 +
  5 +class RequestReceived extends Event
  6 +{
  7 + /**
  8 + * Received data.
  9 + *
  10 + * @var array
  11 + */
  12 + public $data;
  13 +
  14 + /**
  15 + * Bootstrap.
  16 + *
  17 + * @author yansongda <me@yansongda.cn>
  18 + */
  19 + public function __construct(string $driver, string $gateway, array $data)
  20 + {
  21 + $this->data = $data;
  22 +
  23 + parent::__construct($driver, $gateway);
  24 + }
  25 +}
  1 +<?php
  2 +
  3 +namespace Yansongda\Pay\Events;
  4 +
  5 +class SignFailed extends Event
  6 +{
  7 + /**
  8 + * Received data.
  9 + *
  10 + * @var array
  11 + */
  12 + public $data;
  13 +
  14 + /**
  15 + * Bootstrap.
  16 + *
  17 + * @author yansongda <me@yansongda.cn>
  18 + */
  19 + public function __construct(string $driver, string $gateway, array $data)
  20 + {
  21 + $this->data = $data;
  22 +
  23 + parent::__construct($driver, $gateway);
  24 + }
  25 +}
  1 +<?php
  2 +
  3 +namespace Yansongda\Pay\Exceptions;
  4 +
  5 +class BusinessException extends GatewayException
  6 +{
  7 + /**
  8 + * Bootstrap.
  9 + *
  10 + * @author yansongda <me@yansonga.cn>
  11 + *
  12 + * @param string $message
  13 + * @param array|string $raw
  14 + */
  15 + public function __construct($message, $raw = [])
  16 + {
  17 + parent::__construct('ERROR_BUSINESS: '.$message, $raw, self::ERROR_BUSINESS);
  18 + }
  19 +}
  1 +<?php
  2 +
  3 +namespace Yansongda\Pay\Exceptions;
  4 +
  5 +class Exception extends \Exception
  6 +{
  7 + const UNKNOWN_ERROR = 9999;
  8 +
  9 + const INVALID_GATEWAY = 1;
  10 +
  11 + const INVALID_CONFIG = 2;
  12 +
  13 + const INVALID_ARGUMENT = 3;
  14 +
  15 + const ERROR_GATEWAY = 4;
  16 +
  17 + const INVALID_SIGN = 5;
  18 +
  19 + const ERROR_BUSINESS = 6;
  20 +
  21 + /**
  22 + * Raw error info.
  23 + *
  24 + * @var array
  25 + */
  26 + public $raw;
  27 +
  28 + /**
  29 + * Bootstrap.
  30 + *
  31 + * @author yansongda <me@yansonga.cn>
  32 + *
  33 + * @param string $message
  34 + * @param array|string $raw
  35 + * @param int|string $code
  36 + */
  37 + public function __construct($message = '', $raw = [], $code = self::UNKNOWN_ERROR)
  38 + {
  39 + $message = '' === $message ? 'Unknown Error' : $message;
  40 + $this->raw = is_array($raw) ? $raw : [$raw];
  41 +
  42 + parent::__construct($message, intval($code));
  43 + }
  44 +}
  1 +<?php
  2 +
  3 +namespace Yansongda\Pay\Exceptions;
  4 +
  5 +class GatewayException extends Exception
  6 +{
  7 + /**
  8 + * Bootstrap.
  9 + *
  10 + * @author yansongda <me@yansonga.cn>
  11 + *
  12 + * @param string $message
  13 + * @param array|string $raw
  14 + * @param int $code
  15 + */
  16 + public function __construct($message, $raw = [], $code = self::ERROR_GATEWAY)
  17 + {
  18 + parent::__construct('ERROR_GATEWAY: '.$message, $raw, $code);
  19 + }
  20 +}
  1 +<?php
  2 +
  3 +namespace Yansongda\Pay\Exceptions;
  4 +
  5 +class InvalidArgumentException extends Exception
  6 +{
  7 + /**
  8 + * Bootstrap.
  9 + *
  10 + * @author yansongda <me@yansonga.cn>
  11 + *
  12 + * @param string $message
  13 + * @param array|string $raw
  14 + */
  15 + public function __construct($message, $raw = [])
  16 + {
  17 + parent::__construct('INVALID_ARGUMENT: '.$message, $raw, self::INVALID_ARGUMENT);
  18 + }
  19 +}
  1 +<?php
  2 +
  3 +namespace Yansongda\Pay\Exceptions;
  4 +
  5 +class InvalidConfigException extends Exception
  6 +{
  7 + /**
  8 + * Bootstrap.
  9 + *
  10 + * @author yansongda <me@yansonga.cn>
  11 + *
  12 + * @param string $message
  13 + * @param array|string $raw
  14 + */
  15 + public function __construct($message, $raw = [])
  16 + {
  17 + parent::__construct('INVALID_CONFIG: '.$message, $raw, self::INVALID_CONFIG);
  18 + }
  19 +}
  1 +<?php
  2 +
  3 +namespace Yansongda\Pay\Exceptions;
  4 +
  5 +class InvalidGatewayException extends Exception
  6 +{
  7 + /**
  8 + * Bootstrap.
  9 + *
  10 + * @author yansongda <me@yansonga.cn>
  11 + *
  12 + * @param string $message
  13 + * @param array|string $raw
  14 + */
  15 + public function __construct($message, $raw = [])
  16 + {
  17 + parent::__construct('INVALID_GATEWAY: '.$message, $raw, self::INVALID_GATEWAY);
  18 + }
  19 +}
  1 +<?php
  2 +
  3 +namespace Yansongda\Pay\Exceptions;
  4 +
  5 +class InvalidSignException extends Exception
  6 +{
  7 + /**
  8 + * Bootstrap.
  9 + *
  10 + * @author yansongda <me@yansonga.cn>
  11 + *
  12 + * @param string $message
  13 + * @param array|string $raw
  14 + */
  15 + public function __construct($message, $raw = [])
  16 + {
  17 + parent::__construct('INVALID_SIGN: '.$message, $raw, self::INVALID_SIGN);
  18 + }
  19 +}
  1 +<?php
  2 +
  3 +namespace Yansongda\Pay\Gateways;
  4 +
  5 +use Symfony\Component\HttpFoundation\Request;
  6 +use Symfony\Component\HttpFoundation\Response;
  7 +use Yansongda\Pay\Contracts\GatewayApplicationInterface;
  8 +use Yansongda\Pay\Contracts\GatewayInterface;
  9 +use Yansongda\Pay\Events;
  10 +use Yansongda\Pay\Exceptions\GatewayException;
  11 +use Yansongda\Pay\Exceptions\InvalidArgumentException;
  12 +use Yansongda\Pay\Exceptions\InvalidConfigException;
  13 +use Yansongda\Pay\Exceptions\InvalidGatewayException;
  14 +use Yansongda\Pay\Exceptions\InvalidSignException;
  15 +use Yansongda\Pay\Gateways\Alipay\Support;
  16 +use Yansongda\Supports\Collection;
  17 +use Yansongda\Supports\Config;
  18 +use Yansongda\Supports\Str;
  19 +
  20 +/**
  21 + * @method Response app(array $config) APP 支付
  22 + * @method Collection pos(array $config) 刷卡支付
  23 + * @method Collection scan(array $config) 扫码支付
  24 + * @method Collection transfer(array $config) 帐户转账
  25 + * @method Response wap(array $config) 手机网站支付
  26 + * @method Response web(array $config) 电脑支付
  27 + * @method Collection mini(array $config) 小程序支付
  28 + */
  29 +class Alipay implements GatewayApplicationInterface
  30 +{
  31 + /**
  32 + * Const mode_normal.
  33 + */
  34 + const MODE_NORMAL = 'normal';
  35 +
  36 + /**
  37 + * Const mode_dev.
  38 + */
  39 + const MODE_DEV = 'dev';
  40 +
  41 + /**
  42 + * Const mode_service.
  43 + */
  44 + const MODE_SERVICE = 'service';
  45 +
  46 + /**
  47 + * Const url.
  48 + */
  49 + const URL = [
  50 + self::MODE_NORMAL => 'https://openapi.alipay.com/gateway.do?charset=utf-8',
  51 + self::MODE_DEV => 'https://openapi.alipaydev.com/gateway.do?charset=utf-8',
  52 + ];
  53 +
  54 + /**
  55 + * Alipay payload.
  56 + *
  57 + * @var array
  58 + */
  59 + protected $payload;
  60 +
  61 + /**
  62 + * Alipay gateway.
  63 + *
  64 + * @var string
  65 + */
  66 + protected $gateway;
  67 +
  68 + /**
  69 + * extends.
  70 + *
  71 + * @var array
  72 + */
  73 + protected $extends;
  74 +
  75 + /**
  76 + * Bootstrap.
  77 + *
  78 + * @author yansongda <me@yansongda.cn>
  79 + *
  80 + * @throws \Exception
  81 + */
  82 + public function __construct(Config $config)
  83 + {
  84 + $this->gateway = Support::create($config)->getBaseUri();
  85 + $this->payload = [
  86 + 'app_id' => $config->get('app_id'),
  87 + 'method' => '',
  88 + 'format' => 'JSON',
  89 + 'charset' => 'utf-8',
  90 + 'sign_type' => 'RSA2',
  91 + 'version' => '1.0',
  92 + 'return_url' => $config->get('return_url'),
  93 + 'notify_url' => $config->get('notify_url'),
  94 + 'timestamp' => date('Y-m-d H:i:s'),
  95 + 'sign' => '',
  96 + 'biz_content' => '',
  97 + 'app_auth_token' => $config->get('app_auth_token'),
  98 + ];
  99 +
  100 + if ($config->get('app_cert_public_key') && $config->get('alipay_root_cert')) {
  101 + $this->payload['app_cert_sn'] = Support::getCertSN($config->get('app_cert_public_key'));
  102 + $this->payload['alipay_root_cert_sn'] = Support::getRootCertSN($config->get('alipay_root_cert'));
  103 + }
  104 + }
  105 +
  106 + /**
  107 + * Magic pay.
  108 + *
  109 + * @author yansongda <me@yansongda.cn>
  110 + *
  111 + * @param string $method
  112 + * @param array $params
  113 + *
  114 + * @throws GatewayException
  115 + * @throws InvalidArgumentException
  116 + * @throws InvalidConfigException
  117 + * @throws InvalidGatewayException
  118 + * @throws InvalidSignException
  119 + *
  120 + * @return Response|Collection
  121 + */
  122 + public function __call($method, $params)
  123 + {
  124 + if (isset($this->extends[$method])) {
  125 + return $this->makeExtend($method, ...$params);
  126 + }
  127 +
  128 + return $this->pay($method, ...$params);
  129 + }
  130 +
  131 + /**
  132 + * Pay an order.
  133 + *
  134 + * @author yansongda <me@yansongda.cn>
  135 + *
  136 + * @param string $gateway
  137 + * @param array $params
  138 + *
  139 + * @throws InvalidGatewayException
  140 + *
  141 + * @return Response|Collection
  142 + */
  143 + public function pay($gateway, $params = [])
  144 + {
  145 + Events::dispatch(new Events\PayStarting('Alipay', $gateway, $params));
  146 +
  147 + $this->payload['return_url'] = $params['return_url'] ?? $this->payload['return_url'];
  148 + $this->payload['notify_url'] = $params['notify_url'] ?? $this->payload['notify_url'];
  149 +
  150 + unset($params['return_url'], $params['notify_url']);
  151 +
  152 + $this->payload['biz_content'] = json_encode($params);
  153 +
  154 + $gateway = get_class($this).'\\'.Str::studly($gateway).'Gateway';
  155 +
  156 + if (class_exists($gateway)) {
  157 + return $this->makePay($gateway);
  158 + }
  159 +
  160 + throw new InvalidGatewayException("Pay Gateway [{$gateway}] not exists");
  161 + }
  162 +
  163 + /**
  164 + * Verify sign.
  165 + *
  166 + * @author yansongda <me@yansongda.cn>
  167 + *
  168 + * @param array|null $data
  169 + *
  170 + * @throws InvalidSignException
  171 + * @throws InvalidConfigException
  172 + */
  173 + public function verify($data = null, bool $refund = false): Collection
  174 + {
  175 + if (is_null($data)) {
  176 + $request = Request::createFromGlobals();
  177 +
  178 + $data = $request->request->count() > 0 ? $request->request->all() : $request->query->all();
  179 + }
  180 +
  181 + if (isset($data['fund_bill_list'])) {
  182 + $data['fund_bill_list'] = htmlspecialchars_decode($data['fund_bill_list']);
  183 + }
  184 +
  185 + Events::dispatch(new Events\RequestReceived('Alipay', '', $data));
  186 +
  187 + if (Support::verifySign($data)) {
  188 + return new Collection($data);
  189 + }
  190 +
  191 + Events::dispatch(new Events\SignFailed('Alipay', '', $data));
  192 +
  193 + throw new InvalidSignException('Alipay Sign Verify FAILED', $data);
  194 + }
  195 +
  196 + /**
  197 + * Query an order.
  198 + *
  199 + * @author yansongda <me@yansongda.cn>
  200 + *
  201 + * @param string|array $order
  202 + *
  203 + * @throws GatewayException
  204 + * @throws InvalidConfigException
  205 + * @throws InvalidSignException
  206 + */
  207 + public function find($order, string $type = 'wap'): Collection
  208 + {
  209 + $gateway = get_class($this).'\\'.Str::studly($type).'Gateway';
  210 +
  211 + if (!class_exists($gateway) || !is_callable([new $gateway(), 'find'])) {
  212 + throw new GatewayException("{$gateway} Done Not Exist Or Done Not Has FIND Method");
  213 + }
  214 +
  215 + $config = call_user_func([new $gateway(), 'find'], $order);
  216 +
  217 + $this->payload['method'] = $config['method'];
  218 + $this->payload['biz_content'] = $config['biz_content'];
  219 + $this->payload['sign'] = Support::generateSign($this->payload);
  220 +
  221 + Events::dispatch(new Events\MethodCalled('Alipay', 'Find', $this->gateway, $this->payload));
  222 +
  223 + return Support::requestApi($this->payload);
  224 + }
  225 +
  226 + /**
  227 + * Refund an order.
  228 + *
  229 + * @author yansongda <me@yansongda.cn>
  230 + *
  231 + * @throws GatewayException
  232 + * @throws InvalidConfigException
  233 + * @throws InvalidSignException
  234 + */
  235 + public function refund(array $order): Collection
  236 + {
  237 + $this->payload['method'] = 'alipay.trade.refund';
  238 + $this->payload['biz_content'] = json_encode($order);
  239 + $this->payload['sign'] = Support::generateSign($this->payload);
  240 +
  241 + Events::dispatch(new Events\MethodCalled('Alipay', 'Refund', $this->gateway, $this->payload));
  242 +
  243 + return Support::requestApi($this->payload);
  244 + }
  245 +
  246 + /**
  247 + * Cancel an order.
  248 + *
  249 + * @author yansongda <me@yansongda.cn>
  250 + *
  251 + * @param array|string $order
  252 + *
  253 + * @throws GatewayException
  254 + * @throws InvalidConfigException
  255 + * @throws InvalidSignException
  256 + */
  257 + public function cancel($order): Collection
  258 + {
  259 + $this->payload['method'] = 'alipay.trade.cancel';
  260 + $this->payload['biz_content'] = json_encode(is_array($order) ? $order : ['out_trade_no' => $order]);
  261 + $this->payload['sign'] = Support::generateSign($this->payload);
  262 +
  263 + Events::dispatch(new Events\MethodCalled('Alipay', 'Cancel', $this->gateway, $this->payload));
  264 +
  265 + return Support::requestApi($this->payload);
  266 + }
  267 +
  268 + /**
  269 + * Close an order.
  270 + *
  271 + * @param string|array $order
  272 + *
  273 + * @author yansongda <me@yansongda.cn>
  274 + *
  275 + * @throws GatewayException
  276 + * @throws InvalidConfigException
  277 + * @throws InvalidSignException
  278 + */
  279 + public function close($order): Collection
  280 + {
  281 + $this->payload['method'] = 'alipay.trade.close';
  282 + $this->payload['biz_content'] = json_encode(is_array($order) ? $order : ['out_trade_no' => $order]);
  283 + $this->payload['sign'] = Support::generateSign($this->payload);
  284 +
  285 + Events::dispatch(new Events\MethodCalled('Alipay', 'Close', $this->gateway, $this->payload));
  286 +
  287 + return Support::requestApi($this->payload);
  288 + }
  289 +
  290 + /**
  291 + * Download bill.
  292 + *
  293 + * @author yansongda <me@yansongda.cn>
  294 + *
  295 + * @param string|array $bill
  296 + *
  297 + * @throws GatewayException
  298 + * @throws InvalidConfigException
  299 + * @throws InvalidSignException
  300 + */
  301 + public function download($bill): string
  302 + {
  303 + $this->payload['method'] = 'alipay.data.dataservice.bill.downloadurl.query';
  304 + $this->payload['biz_content'] = json_encode(is_array($bill) ? $bill : ['bill_type' => 'trade', 'bill_date' => $bill]);
  305 + $this->payload['sign'] = Support::generateSign($this->payload);
  306 +
  307 + Events::dispatch(new Events\MethodCalled('Alipay', 'Download', $this->gateway, $this->payload));
  308 +
  309 + $result = Support::requestApi($this->payload);
  310 +
  311 + return ($result instanceof Collection) ? $result->get('bill_download_url') : '';
  312 + }
  313 +
  314 + /**
  315 + * Reply success to alipay.
  316 + *
  317 + * @author yansongda <me@yansongda.cn>
  318 + */
  319 + public function success(): Response
  320 + {
  321 + Events::dispatch(new Events\MethodCalled('Alipay', 'Success', $this->gateway));
  322 +
  323 + return new Response('success');
  324 + }
  325 +
  326 + /**
  327 + * extend.
  328 + *
  329 + * @author yansongda <me@yansongda.cn>
  330 + *
  331 + * @throws GatewayException
  332 + * @throws InvalidConfigException
  333 + * @throws InvalidSignException
  334 + * @throws InvalidArgumentException
  335 + */
  336 + public function extend(string $method, callable $function, bool $now = true): ?Collection
  337 + {
  338 + if (!$now && !method_exists($this, $method)) {
  339 + $this->extends[$method] = $function;
  340 +
  341 + return null;
  342 + }
  343 +
  344 + $customize = $function($this->payload);
  345 +
  346 + if (!is_array($customize) && !($customize instanceof Collection)) {
  347 + throw new InvalidArgumentException('Return Type Must Be Array Or Collection');
  348 + }
  349 +
  350 + Events::dispatch(new Events\MethodCalled('Alipay', 'extend', $this->gateway, $customize));
  351 +
  352 + if (is_array($customize)) {
  353 + $this->payload = $customize;
  354 + $this->payload['sign'] = Support::generateSign($this->payload);
  355 +
  356 + return Support::requestApi($this->payload);
  357 + }
  358 +
  359 + return $customize;
  360 + }
  361 +
  362 + /**
  363 + * Make pay gateway.
  364 + *
  365 + * @author yansongda <me@yansongda.cn>
  366 + *
  367 + * @throws InvalidGatewayException
  368 + *
  369 + * @return Response|Collection
  370 + */
  371 + protected function makePay(string $gateway)
  372 + {
  373 + $app = new $gateway();
  374 +
  375 + if ($app instanceof GatewayInterface) {
  376 + return $app->pay($this->gateway, array_filter($this->payload, function ($value) {
  377 + return '' !== $value && !is_null($value);
  378 + }));
  379 + }
  380 +
  381 + throw new InvalidGatewayException("Pay Gateway [{$gateway}] Must Be An Instance Of GatewayInterface");
  382 + }
  383 +
  384 + /**
  385 + * makeExtend.
  386 + *
  387 + * @author yansongda <me@yansongda.cn>
  388 + *
  389 + * @throws GatewayException
  390 + * @throws InvalidArgumentException
  391 + * @throws InvalidConfigException
  392 + * @throws InvalidSignException
  393 + */
  394 + protected function makeExtend(string $method, array ...$params): Collection
  395 + {
  396 + $params = count($params) >= 1 ? $params[0] : $params;
  397 +
  398 + $function = $this->extends[$method];
  399 +
  400 + $customize = $function($this->payload, $params);
  401 +
  402 + if (!is_array($customize) && !($customize instanceof Collection)) {
  403 + throw new InvalidArgumentException('Return Type Must Be Array Or Collection');
  404 + }
  405 +
  406 + Events::dispatch(new Events\MethodCalled(
  407 + 'Alipay',
  408 + 'extend - '.$method,
  409 + $this->gateway,
  410 + is_array($customize) ? $customize : $customize->toArray()
  411 + ));
  412 +
  413 + if (is_array($customize)) {
  414 + $this->payload = $customize;
  415 + $this->payload['sign'] = Support::generateSign($this->payload);
  416 +
  417 + return Support::requestApi($this->payload);
  418 + }
  419 +
  420 + return $customize;
  421 + }
  422 +}
  1 +<?php
  2 +
  3 +namespace Yansongda\Pay\Gateways\Alipay;
  4 +
  5 +use Symfony\Component\HttpFoundation\Response;
  6 +use Yansongda\Pay\Events;
  7 +use Yansongda\Pay\Exceptions\InvalidArgumentException;
  8 +use Yansongda\Pay\Exceptions\InvalidConfigException;
  9 +use Yansongda\Pay\Gateways\Alipay;
  10 +
  11 +class AppGateway extends Gateway
  12 +{
  13 + /**
  14 + * Pay an order.
  15 + *
  16 + * @author yansongda <me@yansongda.cn>
  17 + *
  18 + * @param string $endpoint
  19 + *
  20 + * @throws InvalidConfigException
  21 + * @throws InvalidArgumentException
  22 + */
  23 + public function pay($endpoint, array $payload): Response
  24 + {
  25 + $payload['method'] = 'alipay.trade.app.pay';
  26 +
  27 + $biz_array = json_decode($payload['biz_content'], true);
  28 + if ((Alipay::MODE_SERVICE === $this->mode) && (!empty(Support::getInstance()->pid))) {
  29 + $biz_array['extend_params'] = is_array($biz_array['extend_params']) ? array_merge(['sys_service_provider_id' => Support::getInstance()->pid], $biz_array['extend_params']) : ['sys_service_provider_id' => Support::getInstance()->pid];
  30 + }
  31 + $payload['biz_content'] = json_encode(array_merge($biz_array, ['product_code' => 'QUICK_MSECURITY_PAY']));
  32 + $payload['sign'] = Support::generateSign($payload);
  33 +
  34 + Events::dispatch(new Events\PayStarted('Alipay', 'App', $endpoint, $payload));
  35 +
  36 + return new Response(http_build_query($payload));
  37 + }
  38 +}