Payment.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  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\common\model;
  13. use cores\BaseModel;
  14. use cores\exception\BaseException;
  15. use think\facade\Cache;
  16. use think\model\relation\BelongsTo;
  17. use app\common\library\helper;
  18. use app\common\model\PaymentTemplate as PaymentTemplateModel;
  19. use app\common\enum\Client as ClientEnum;
  20. use app\common\enum\payment\Method as PaymentMethodEnum;
  21. /**
  22. * 模型类:支付方式记录
  23. * Class Payment
  24. * @package app\common\model
  25. */
  26. class Payment extends BaseModel
  27. {
  28. // 定义表名
  29. protected $name = 'payment';
  30. // 定义主键
  31. protected $pk = 'payment_id';
  32. /**
  33. * 关联支付模板记录表
  34. * @return BelongsTo
  35. */
  36. public function template(): BelongsTo
  37. {
  38. return $this->belongsTo('PaymentTemplate');
  39. }
  40. /**
  41. * 获取器:其他选项
  42. * @param $value
  43. * @return array
  44. */
  45. public function getOthersAttr($value): array
  46. {
  47. return helper::jsonDecode($value);
  48. }
  49. /**
  50. * 修改器:其他选项
  51. * @param $value
  52. * @return string
  53. */
  54. public function setOthersAttr($value): string
  55. {
  56. return helper::jsonEncode($value);
  57. }
  58. /**
  59. * 支付方式详情
  60. * @param int $paymentId
  61. * @return static|array|null
  62. */
  63. public static function detail(int $paymentId)
  64. {
  65. return self::get($paymentId);
  66. }
  67. /**
  68. * 获取支付方式配置
  69. * @throws \think\db\exception\ModelNotFoundException
  70. * @throws \think\db\exception\DbException
  71. * @throws \think\db\exception\DataNotFoundException
  72. */
  73. public static function getAll(int $storeId): array
  74. {
  75. // 实例化当前模型
  76. $model = new static;
  77. // 默认的支付方式数据
  78. $defaultData = $model->defaultData();
  79. if (!$data = Cache::get("payment_{$storeId}")) {
  80. // 获取所有支付方式
  81. $data = $model->dataByStorage($storeId, $defaultData);
  82. // 写入缓存中
  83. Cache::tag('cache')->set("payment_{$storeId}", $data);
  84. }
  85. return static::resetOptions($defaultData, $data);
  86. }
  87. /**
  88. * 获取指定客户端的支付方式
  89. * @param string $client
  90. * @param int $storeId
  91. * @return array
  92. * @throws \think\db\exception\DataNotFoundException
  93. * @throws \think\db\exception\DbException
  94. * @throws \think\db\exception\ModelNotFoundException
  95. */
  96. public static function getItem(string $client, int $storeId): array
  97. {
  98. return static::getAll($storeId)[$client];
  99. }
  100. /**
  101. * 重组缓存数据 (多维)
  102. * @param array $defaultData
  103. * @param array $data
  104. * @return array
  105. */
  106. private static function resetOptions(array $defaultData, array $data): array
  107. {
  108. $data = \resetOptions($defaultData, $data);
  109. foreach ($data as &$item) {
  110. $item['methods'] = array_values($item['methods']);
  111. }
  112. return $data;
  113. }
  114. /**
  115. * 获取所有支付方式 (从数据库中)
  116. * @param int $storeId
  117. * @param array $defaultData
  118. * @return array
  119. * @throws \think\db\exception\DataNotFoundException
  120. * @throws \think\db\exception\DbException
  121. * @throws \think\db\exception\ModelNotFoundException
  122. */
  123. private function dataByStorage(int $storeId, array $defaultData): array
  124. {
  125. // 获取数据库中所有的支付方式
  126. $list = $this->where('store_id', '=', $storeId)->select();
  127. if ($list->isEmpty()) {
  128. return [];
  129. }
  130. // 客户端标识合集
  131. $clientKeys = helper::getArrayColumn($defaultData, 'client');
  132. // 整理数据格式
  133. $data = [];
  134. $listArr = $list->toArray();
  135. foreach ($clientKeys as $client) {
  136. $listAsClient = $this->listAsClient($listArr, $client);
  137. $methods = empty($listAsClient) ? [] : $this->buildMethods($client, $listAsClient);
  138. if (!empty($methods)) {
  139. $data[$client] = ['client' => $client, 'methods' => $this->buildMethods($client, $listAsClient)];
  140. }
  141. }
  142. return $data;
  143. }
  144. /**
  145. * 格式化支付方式数据 (赋值key用于合并默认数据)
  146. * @param string $client
  147. * @param array $listAsClient
  148. * @return array
  149. */
  150. private function buildMethods(string $client, array $listAsClient): array
  151. {
  152. $methods = helper::getArrayColumns($listAsClient, [
  153. 'method', 'client', 'is_must_template', 'template_id',
  154. 'is_enable', 'is_default', 'others'
  155. ]);
  156. $data = [];
  157. foreach ($methods as $item) {
  158. if ($item['client'] === $client) {
  159. $data["$client-{$item['method']}"] = $item;
  160. }
  161. }
  162. return $data;
  163. }
  164. /**
  165. * 默认的支付方式数据
  166. * @return array[]
  167. */
  168. protected function defaultData(): array
  169. {
  170. $data = [
  171. ClientEnum::MP_WEIXIN => $this->defaultGroup(ClientEnum::MP_WEIXIN, [
  172. PaymentMethodEnum::WECHAT,
  173. PaymentMethodEnum::BALANCE,
  174. ]),
  175. ClientEnum::H5 => $this->defaultGroup(ClientEnum::H5, [
  176. PaymentMethodEnum::WECHAT,
  177. PaymentMethodEnum::ALIPAY,
  178. PaymentMethodEnum::BALANCE,
  179. ]),
  180. ClientEnum::APP => $this->defaultGroup(ClientEnum::APP, [
  181. PaymentMethodEnum::WECHAT,
  182. PaymentMethodEnum::ALIPAY,
  183. PaymentMethodEnum::BALANCE,
  184. ]),
  185. ];
  186. return $data;
  187. }
  188. /**
  189. * 默认的支付客户端分组
  190. * @param string $client
  191. * @param array $designated
  192. * @return array
  193. */
  194. private function defaultGroup(string $client, array $designated): array
  195. {
  196. return [
  197. 'client' => $client,
  198. 'name' => ClientEnum::getName($client),
  199. 'desc' => '在' . ClientEnum::getName($client) . '付款时使用',
  200. 'methods' => $this->defaultMethods($client, $designated)
  201. ];
  202. }
  203. /**
  204. * 默认的methods数据
  205. * @param string $client
  206. * @param array $designated
  207. * @return array
  208. */
  209. private function defaultMethods(string $client, array $designated): array
  210. {
  211. $record = [
  212. 'key' => 1,
  213. 'method' => PaymentMethodEnum::WECHAT, // 支付方式
  214. 'is_must_template' => true, // 是否必须使用模板
  215. 'template_id' => 0, // 模板ID
  216. 'is_enable' => false, // 是否启用该支付方式
  217. 'is_default' => false, // 是否为默认支付方式
  218. 'others' => [] // 其他配置
  219. ];
  220. $defaultMethods = [];
  221. foreach ($designated as $key => $method) {
  222. $defaultMethods["{$client}-{$method}"] = array_merge($record, [
  223. 'key' => $key + 1,
  224. 'method' => $method,
  225. 'is_must_template' => $method != PaymentMethodEnum::BALANCE,
  226. 'is_enable' => $method == PaymentMethodEnum::BALANCE,
  227. ]);
  228. }
  229. return $defaultMethods;
  230. }
  231. /**
  232. * 根据client过滤list
  233. * @param array $listArr
  234. * @param string $client
  235. * @return array|iterable
  236. */
  237. private function listAsClient(array $listArr, string $client)
  238. {
  239. $listAsClient = helper::arrayFilterAsVal($listArr, 'client', $client);
  240. foreach ($listAsClient as &$item) {
  241. $item['is_must_template'] = (bool)$item['is_must_template'];
  242. $item['is_enable'] = (bool)$item['is_enable'];
  243. $item['is_default'] = (bool)$item['is_default'];
  244. }
  245. return $listAsClient;
  246. }
  247. /**
  248. * 根据指定客户端获取可用的支付方式
  249. * @param string $client 客户端来源
  250. * @param bool $balance 是否支持余额支付
  251. * @param int|null $storeId 商城ID
  252. * @return array
  253. * @throws \think\db\exception\DataNotFoundException
  254. * @throws \think\db\exception\DbException
  255. * @throws \think\db\exception\ModelNotFoundException
  256. * @throws BaseException
  257. */
  258. public function getMethodsByClient(string $client, bool $balance = true, int $storeId = null): array
  259. {
  260. $storeId = $storeId ?: self::$storeId;
  261. $group = static::getItem($client, $storeId);
  262. $methods = [];
  263. foreach ($group['methods'] as $method) {
  264. if ($method['is_enable'] && ($balance || $method['method'] !== PaymentMethodEnum::BALANCE)) {
  265. $methods[] = $method;
  266. }
  267. }
  268. if (empty($methods)) {
  269. throwError('很抱歉,当前没有可用的支付方式,请检查后台支付设置');
  270. }
  271. return $methods;
  272. }
  273. /**
  274. * 获取指定的支付方式及模板
  275. * @param string $method 支付方式
  276. * @param string $client 客户端来源
  277. * @param int|null $storeId 商城ID
  278. * @return bool|mixed
  279. * @throws BaseException
  280. * @throws \think\db\exception\DataNotFoundException
  281. * @throws \think\db\exception\DbException
  282. * @throws \think\db\exception\ModelNotFoundException
  283. */
  284. public function getPaymentInfo(string $method, string $client, int $storeId = null)
  285. {
  286. // 获取当前指定的支付方式
  287. $methodInfo = $this->getCurrentMethod($method, $client, $storeId);
  288. // 获取支付模板信息
  289. $methodInfo['template'] = !$methodInfo['is_must_template'] ? [] : $this->getTemplateInfo($methodInfo['template_id']);
  290. return $methodInfo;
  291. }
  292. /**
  293. * 获取支付模板
  294. * @param int $templateId 支付模板ID
  295. * @return array
  296. * @throws BaseException
  297. */
  298. private function getTemplateInfo(int $templateId): array
  299. {
  300. return (new PaymentTemplateModel)->getTemplateInfo($templateId);
  301. }
  302. /**
  303. * 获取当前指定的支付方式
  304. * @param string $method 指定的支付方式
  305. * @param string $client 客户端来源
  306. * @param int|null $storeId 商城ID
  307. * @return bool|mixed
  308. * @throws BaseException
  309. * @throws \think\db\exception\DataNotFoundException
  310. * @throws \think\db\exception\DbException
  311. * @throws \think\db\exception\ModelNotFoundException
  312. */
  313. private function getCurrentMethod(string $method, string $client, int $storeId = null)
  314. {
  315. $methods = $this->getMethodsByClient($client, true, $storeId);
  316. $method = helper::arraySearch($methods, 'method', $method);
  317. if (empty($method)) {
  318. throwError('很抱歉,当前未找到指定的支付方式');
  319. }
  320. return $method;
  321. }
  322. }