正在显示
53 个修改的文件
包含
4825 行增加
和
0 行删除
.bowerrc
0 → 100644
.env.sample
0 → 100644
.gitignore
0 → 100644
LICENSE
0 → 100644
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. |
README.md
0 → 100644
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。 |
addons/.gitkeep
0 → 100644
1 | + |
addons/.htaccess
0 → 100644
1 | +deny from all |
addons/command/.addonrc
0 → 100644
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"]} |
addons/command/Command.php
0 → 100644
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 | +} |
addons/command/config.php
0 → 100644
addons/command/controller/Index.php
0 → 100644
addons/command/info.ini
0 → 100644
addons/command/install.sql
0 → 100644
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 = '在线命令表'; |
addons/command/library/Output.php
0 → 100644
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 | +} |
addons/epay/.addonrc
0 → 100644
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"]} |
addons/epay/Epay.php
0 → 100644
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 | +} |
addons/epay/certs/alipayCertPublicKey.crt
0 → 100644
addons/epay/certs/alipayRootCert.crt
0 → 100644
addons/epay/certs/apiclient_cert.pem
0 → 100644
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----- |
addons/epay/certs/apiclient_key.pem
0 → 100644
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----- |
addons/epay/certs/appCertPublicKey.crt
0 → 100644
addons/epay/config.html
0 → 100644
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> |
addons/epay/config.php
0 → 100644
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 | +]; |
addons/epay/controller/Api.php
0 → 100644
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 | +} |
addons/epay/controller/Index.php
0 → 100644
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 | +} |
addons/epay/info.ini
0 → 100644
addons/epay/library/Collection.php
0 → 100644
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 | +} |
addons/epay/library/OrderException.php
0 → 100644
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 | +} |
addons/epay/library/QRCode.php
0 → 100644
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 | + |
addons/epay/library/RedirectResponse.php
0 → 100644
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 | +} |
addons/epay/library/Response.php
0 → 100644
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 | +} |
addons/epay/library/Service.php
0 → 100644
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 | +} |
addons/epay/library/Wechat.php
0 → 100644
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 | +} |
addons/epay/library/Yansongda/Pay/Events.php
0 → 100644
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 | +} |
-
请 注册 或 登录 后发表评论