PaymentTemplate.php 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2017~2024 https://www.yiovo.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
  8. // +----------------------------------------------------------------------
  9. // | Author: 萤火科技 <admin@yiovo.com>
  10. // +----------------------------------------------------------------------
  11. declare (strict_types=1);
  12. namespace app\store\model;
  13. use think\facade\Filesystem;
  14. use cores\exception\BaseException;
  15. use app\common\library\helper;
  16. use app\store\model\Payment as PaymentModel;
  17. use app\common\enum\payment\Method as PaymentMethodEnum;
  18. use app\common\model\PaymentTemplate as PaymentTemplateModel;
  19. use app\common\library\wechat\payment\PlatformCertDown;
  20. /**
  21. * 模型类:支付方式记录
  22. * Class PaymentTemplate
  23. * @package app\store\model
  24. */
  25. class PaymentTemplate extends PaymentTemplateModel
  26. {
  27. /**
  28. * 获取列表
  29. * @return \think\Paginator
  30. * @throws \think\db\exception\DbException
  31. */
  32. public function getList(): \think\Paginator
  33. {
  34. return $this->where('is_delete', '=', 0)
  35. ->order(['sort' => 'asc', $this->getPk()])
  36. ->paginate(15);
  37. }
  38. /**
  39. * 新增记录
  40. * @param array $data
  41. * @return bool|false
  42. * @throws BaseException
  43. */
  44. public function add(array $data): bool
  45. {
  46. // 整理保存的数据
  47. $config = helper::jsonDecode(htmlspecialchars_decode($data['config']));
  48. $methodConfig = $config[$data['method']];
  49. $data['store_id'] = self::$storeId;
  50. // 写入证书文件
  51. $data['config'] = [$data['method'] => $this->certFileName($data['method'], $methodConfig)];
  52. // 记录微信支付v3平台证书序号
  53. if ($data['method'] === 'wechat' && $data['config']['wechat']['version'] === 'v3') {
  54. $data['wechatpay_serial'] = $data['config']['wechat'][$data['config']['wechat']['mchType']]['platformCertSerial'];
  55. }
  56. // 写入到数据库
  57. return $this->save($data);
  58. }
  59. /**
  60. * 更新记录
  61. * @param array $data
  62. * @return bool
  63. * @throws BaseException
  64. */
  65. public function edit(array $data): bool
  66. {
  67. // 整理保存的数据
  68. $config = helper::jsonDecode(htmlspecialchars_decode($data['config']));
  69. $methodConfig = $config[$data['method']];
  70. $data['store_id'] = self::$storeId;
  71. // 记录证书文件名
  72. $data['config'] = [
  73. $data['method'] => array_merge($methodConfig, $this->certFileName($data['method'], $methodConfig))
  74. ];
  75. // 记录微信支付v3平台证书序号
  76. if ($data['method'] === 'wechat' && $data['config']['wechat']['version'] === 'v3') {
  77. $data['wechatpay_serial'] = $data['config']['wechat'][$data['config']['wechat']['mchType']]['platformCertSerial'];
  78. }
  79. // 更新到数据库
  80. return $this->save($data) !== false;
  81. }
  82. /**
  83. * 记录证书文件名
  84. * @param string $method 支付方式类型
  85. * @param array $config 支付配置
  86. * @return array
  87. * @throws BaseException
  88. */
  89. private function certFileName(string $method, array $config): array
  90. {
  91. if ($method === PaymentMethodEnum::WECHAT) {
  92. $config['normal'] = $this->saveCertFileNames($config['normal'], $method, [
  93. 'apiclientCert',
  94. 'apiclientKey'
  95. ], 'pem');
  96. $config['provider'] = $this->saveCertFileNames($config['provider'], $method, [
  97. 'spApiclientCert',
  98. 'spApiclientKey'
  99. ], 'pem');
  100. // 微信支付v3下载 [微信平台证书]
  101. $config['version'] === 'v3' && $this->downloadPlatformCert($config);
  102. }
  103. if ($method === PaymentMethodEnum::ALIPAY && $config['signMode'] == 10) {
  104. $config = $this->saveCertFileNames($config, $method, [
  105. 'appCertPublicKey',
  106. 'alipayCertPublicKey',
  107. 'alipayRootCert'
  108. ], 'crt');
  109. }
  110. return $config;
  111. }
  112. /**
  113. * 微信支付v3下载 [微信平台证书]
  114. * @param array $config
  115. * @return void
  116. * @throws BaseException
  117. */
  118. private function downloadPlatformCert(array &$config): void
  119. {
  120. // 配置项的字段(用于区分普通商户和子商户)
  121. $field = $config['mchType'] === 'normal' ? ['normal', 'apiclientCert', 'apiclientKey', 'apiKey', 'mchId']
  122. : ['provider', 'spApiclientCert', 'spApiclientKey', 'spApiKey', 'spMchId'];
  123. // 微信商户API证书 [公钥]
  124. $publicKey = parent::realPathCertFile(PaymentMethodEnum::WECHAT, $config[$field[0]][$field[1]], self::$storeId);
  125. // 微信商户API证书 [私钥]
  126. $privatekey = parent::realPathCertFile(PaymentMethodEnum::WECHAT, $config[$field[0]][$field[2]], self::$storeId);
  127. // 生成平台证书文件名
  128. $platformCertName = $this->buildCertFileName('platformCert', 'pem');
  129. // 证书保存的目录
  130. $certFolder = parent::certFolder(PaymentMethodEnum::WECHAT, self::$storeId);
  131. // 下载平台证书
  132. $downloader = new PlatformCertDown([
  133. 'key' => $config[$field[0]][$field[3]],
  134. 'mchid' => $config[$field[0]][$field[4]],
  135. 'privatekey' => $privatekey,
  136. 'publicKey' => $publicKey,
  137. 'output' => $certFolder,
  138. 'fileName' => $platformCertName
  139. ]);
  140. $downloader->run();
  141. // 记录平台证书
  142. $config[$field[0]]['platformCert'] = $platformCertName;
  143. $config[$field[0]]['platformCertSerial'] = $downloader->getPlatformCertSerial();
  144. }
  145. /**
  146. * 批量设置证书文件名
  147. * @param array $config 配置项
  148. * @param string $method 支付方式类型
  149. * @param array $certNames
  150. * @param string $ext 文件后缀
  151. * @return array
  152. */
  153. private function saveCertFileNames(array $config, string $method, array $certNames, string $ext): array
  154. {
  155. foreach ($certNames as $name) {
  156. $config[$name] = $this->writeFile($method, $name, $ext) ?: $config[$name];
  157. }
  158. return $config;
  159. }
  160. /**
  161. * 写入证书文件
  162. * @param string $method 支付方式类型
  163. * @param string $name 证书名称
  164. * @param string $ext 文件后缀
  165. * @return string|false
  166. */
  167. private function writeFile(string $method, string $name, string $ext)
  168. {
  169. // 接收上传的文件
  170. $file = request()->file($name);
  171. if (empty($file)) {
  172. return false;
  173. }
  174. // 证书文件夹路径
  175. $dirPath = "payment/{$method}/" . self::$storeId;
  176. // 证书文件名
  177. $fileName = $this->buildCertFileName($name, $ext);
  178. // 写入到本地服务器
  179. Filesystem::disk('data')->putFileAs($dirPath, $file, $fileName);
  180. return $fileName;
  181. }
  182. /**
  183. * 生成证书文件名
  184. * @param string $name
  185. * @param string $ext
  186. * @return string
  187. */
  188. private function buildCertFileName(string $name, string $ext): string
  189. {
  190. $hash = str_substr(md5(get_guid_v4()), 8);
  191. return "{$name}-{$hash}.{$ext}";
  192. }
  193. /**
  194. * 删除记录
  195. * @return bool
  196. */
  197. public function setDelete(): bool
  198. {
  199. // 验证模板是否被引用
  200. if (PaymentModel::existsTemplateId($this['template_id'])) {
  201. $this->error = '当前支付模板已被引用,需解除后才可删除';
  202. return false;
  203. }
  204. // 标记为已删除
  205. return $this->save(['is_delete' => 1]);
  206. }
  207. }