MiniProgramService.php 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2016~2023 https://www.crmeb.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  8. // +----------------------------------------------------------------------
  9. // | Author: CRMEB Team <admin@crmeb.com>
  10. // +----------------------------------------------------------------------
  11. namespace crmeb\services\app;
  12. use app\services\order\StoreOrderTakeServices;
  13. use app\services\pay\PayServices;
  14. use crmeb\exceptions\AdminException;
  15. use crmeb\services\CacheService;
  16. use crmeb\services\easywechat\orderShipping\MiniOrderService;
  17. use crmeb\services\SystemConfigService;
  18. use app\services\pay\PayNotifyServices;
  19. use crmeb\services\easywechat\Application;
  20. use EasyWeChat\Payment\Order;
  21. use think\facade\Event;
  22. use think\facade\Log;
  23. use crmeb\utils\Hook;
  24. use think\Response;
  25. /**
  26. * 微信小程序接口
  27. * Class WechatMinService
  28. * @package service
  29. */
  30. class MiniProgramService
  31. {
  32. const MSG_CODE = [
  33. '1' => '未创建直播间',
  34. '1003' => '商品id不存在',
  35. '47001' => '入参格式不符合规范',
  36. '200002' => '入参错误',
  37. '300001' => '禁止创建/更新商品 或 禁止编辑&更新房间',
  38. '300002' => '名称长度不符合规则',
  39. '300006' => '图片上传失败',
  40. '300022' => '此房间号不存在',
  41. '300023' => '房间状态 拦截',
  42. '300024' => '商品不存在',
  43. '300025' => '商品审核未通过',
  44. '300026' => '房间商品数量已经满额',
  45. '300027' => '导入商品失败',
  46. '300028' => '房间名称违规',
  47. '300029' => '主播昵称违规',
  48. '300030' => '主播微信号不合法',
  49. '300031' => '直播间封面图不合规',
  50. '300032' => '直播间分享图违规',
  51. '300033' => '添加商品超过直播间上限',
  52. '300034' => '主播微信昵称长度不符合要求',
  53. '300035' => '主播微信号不存在',
  54. '300036' => '主播微信号未实名认证',
  55. '300037' => '购物直播频道封面图不合规',
  56. '300038' => '未在小程序管理后台配置客服',
  57. '9410000' => '直播间列表为空',
  58. '9410001' => '获取房间失败',
  59. '9410002' => '获取商品失败',
  60. '9410003' => '获取回放失败',
  61. '300003' => '价格输入不合规',
  62. '300004' => '商品名称存在违规违法内容',
  63. '300005' => '商品图片存在违规违法内容',
  64. '300007' => '线上小程序版本不存在该链接',
  65. '300008' => '添加商品失败',
  66. '300009' => '商品审核撤回失败',
  67. '300010' => '商品审核状态不对',
  68. '300011' => '操作非法',
  69. '300012' => '没有提审额度',
  70. '300013' => '提审失败',
  71. '300014' => '审核中,无法删除',
  72. '300017' => '商品未提审',
  73. '300018' => '图片尺寸不符合要求',
  74. '300021' => '商品添加成功,审核失败',
  75. '40001' => 'AppSecret错误或者AppSecret不属于这个小程序,请确认AppSecret 的正确性',
  76. '40002' => '请确保grant_type字段值为client_credential',
  77. '40013' => '不合法的AppID,请检查AppID的正确性,避免异常字符,注意大小写',
  78. '40125' => '小程序配置无效,请检查配置',
  79. '41002' => '缺少appid参数',
  80. '41004' => '缺少secret参数',
  81. '43104' => 'appid与openid不匹配',
  82. '48001' => '微信接口暂无权限,请先去获取',
  83. '-1' => '系统错误',
  84. ];
  85. /**
  86. * @var Application
  87. */
  88. protected static $instance;
  89. /**
  90. * @return array
  91. */
  92. public static function options()
  93. {
  94. $wechat = SystemConfigService::more(['wechat_app_appsecret', 'wechat_app_appid', 'site_url', 'routine_appId', 'routine_appsecret', 'routine_token', 'routine_encodingaeskey']);
  95. $payment = SystemConfigService::more(['pay_weixin_mchid', 'pay_weixin_key', 'pay_weixin_client_cert', 'pay_weixin_client_key', 'pay_weixin_open', 'pay_new_weixin_open', 'pay_new_weixin_mchid']);
  96. $config = [];
  97. if (request()->isApp()) {
  98. $appId = isset($wechat['wechat_app_appid']) ? trim($wechat['wechat_app_appid']) : '';
  99. $appsecret = isset($wechat['wechat_app_appsecret']) ? trim($wechat['wechat_app_appsecret']) : '';
  100. } else {
  101. $appId = isset($wechat['routine_appId']) ? trim($wechat['routine_appId']) : '';
  102. $appsecret = isset($wechat['routine_appsecret']) ? trim($wechat['routine_appsecret']) : '';
  103. }
  104. $config = [
  105. 'token' => isset($wechat['routine_token']) ? trim($wechat['routine_token']) : '',
  106. 'aes_key' => isset($wechat['routine_encodingaeskey']) ? trim($wechat['routine_encodingaeskey']) : '',
  107. ];
  108. $config['mini_program'] = [
  109. 'app_id' => $appId,
  110. 'secret' => $appsecret,
  111. 'token' => isset($wechat['routine_token']) ? trim($wechat['routine_token']) : '',
  112. 'aes_key' => isset($wechat['routine_encodingaeskey']) ? trim($wechat['routine_encodingaeskey']) : ''
  113. ];
  114. $config['payment'] = [
  115. 'app_id' => $appId,
  116. 'merchant_id' => empty($payment['pay_new_weixin_open']) ? trim($payment['pay_weixin_mchid']) : trim($payment['pay_new_weixin_mchid']),
  117. 'key' => trim($payment['pay_weixin_key']),
  118. 'cert_path' => substr(public_path(parse_url($payment['pay_weixin_client_cert'])['path']), 0, strlen(public_path(parse_url($payment['pay_weixin_client_cert'])['path'])) - 1),
  119. 'key_path' => substr(public_path(parse_url($payment['pay_weixin_client_key'])['path']), 0, strlen(public_path(parse_url($payment['pay_weixin_client_key'])['path'])) - 1),
  120. 'notify_url' => trim($wechat['site_url']) . '/api/pay/notify/routine'
  121. ];
  122. return $config;
  123. }
  124. /**
  125. * 初始化
  126. * @param bool $cache
  127. * @return Application
  128. */
  129. public static function application($cache = false)
  130. {
  131. (self::$instance === null || $cache === true) && (self::$instance = new Application(self::options()));
  132. return self::$instance;
  133. }
  134. /**
  135. * 小程序接口
  136. * @return \EasyWeChat\MiniProgram\MiniProgram
  137. */
  138. public static function miniprogram()
  139. {
  140. return self::application()->mini_program;
  141. }
  142. /**
  143. * 获得用户信息 根据code 获取session_key
  144. * @param array|string $openid
  145. * @return $userInfo
  146. */
  147. public static function getUserInfo($code)
  148. {
  149. try {
  150. return self::miniprogram()->sns->getSessionKey($code);
  151. } catch (\Throwable $e) {
  152. throw new AdminException($e->getMessage());
  153. }
  154. }
  155. /**
  156. * 加密数据解密
  157. * @param $sessionKey
  158. * @param $iv
  159. * @param $encryptData
  160. * @return $userInfo
  161. */
  162. public static function encryptor($sessionKey, $iv, $encryptData)
  163. {
  164. return self::miniprogram()->encryptor->decryptData($sessionKey, $iv, $encryptData);
  165. }
  166. /**
  167. * 上传临时素材接口
  168. * @return \EasyWeChat\Material\Temporary
  169. */
  170. public static function materialTemporaryService()
  171. {
  172. return self::miniprogram()->material_temporary;
  173. }
  174. /**
  175. * 客服消息接口
  176. * @param null $to
  177. * @param null $message
  178. */
  179. public static function staffService()
  180. {
  181. return self::miniprogram()->staff;
  182. }
  183. /**
  184. * 微信小程序二维码生成接口
  185. * @return \EasyWeChat\QRCode\QRCode
  186. */
  187. public static function qrcodeService()
  188. {
  189. return self::miniprogram()->qrcode;
  190. }
  191. /**微信小程序二维码生成接口不限量永久
  192. * @param $scene
  193. * @param null $page
  194. * @param null $width
  195. * @param null $autoColor
  196. * @param array $lineColor
  197. * @return \Psr\Http\Message\StreamInterface
  198. */
  199. public static function appCodeUnlimitService($scene, $page = null, $width = 430, $autoColor = false, $lineColor = ['r' => 0, 'g' => 0, 'b' => 0])
  200. {
  201. return self::qrcodeService()->appCodeUnlimit($scene, $page, $width, $autoColor, $lineColor);
  202. }
  203. /**
  204. * 模板消息接口
  205. * @return \EasyWeChat\Notice\Notice
  206. */
  207. public static function noticeService()
  208. {
  209. return self::miniprogram()->notice;
  210. }
  211. /**
  212. * 订阅模板消息接口
  213. * @return \crmeb\services\subscribe\ProgramSubscribe
  214. */
  215. public static function SubscribenoticeService()
  216. {
  217. return self::miniprogram()->now_notice;
  218. }
  219. /**
  220. * 发送订阅消息
  221. * @param string $touser 接收者(用户)的 openid
  222. * @param string $templateId 所需下发的订阅模板id
  223. * @param array $data 模板内容,格式形如 { "key1": { "value": any }, "key2": { "value": any } }
  224. * @param string $link 击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转。
  225. * @return \EasyWeChat\Support\Collection|null
  226. * @throws \EasyWeChat\Core\Exceptions\HttpException
  227. * @throws \EasyWeChat\Core\Exceptions\InvalidArgumentException
  228. */
  229. public static function sendSubscribeTemlate(string $touser, string $templateId, array $data, string $link = '')
  230. {
  231. return self::SubscribenoticeService()->to($touser)->template($templateId)->andData($data)->withUrl($link)->send();
  232. }
  233. /**
  234. * 添加订阅消息模版
  235. * @param string $tid
  236. * @param array $kidList
  237. * @param string $sceneDesc
  238. * @return mixed
  239. */
  240. public static function addSubscribeTemplate(string $tid, array $kidList, string $sceneDesc = '')
  241. {
  242. try {
  243. $res = self::SubscribenoticeService()->addTemplate($tid, $kidList, $sceneDesc);
  244. if (isset($res['errcode']) && $res['errcode'] == 0 && isset($res['priTmplId'])) {
  245. return $res['priTmplId'];
  246. } else {
  247. Log::error('添加订阅消息模版失败:' . $res['errmsg']);
  248. }
  249. } catch (\Throwable $e) {
  250. Log::error('添加订阅消息模版失败:' . $e->getMessage());
  251. }
  252. return true;
  253. }
  254. /**
  255. * 删除订阅消息模版
  256. * @param string $tid
  257. * @param array $kidList
  258. * @param string $sceneDesc
  259. * @return mixed
  260. */
  261. public static function delSubscribeTemplate(string $priTmplId)
  262. {
  263. try {
  264. $res = self::SubscribenoticeService()->delTemplate($priTmplId);
  265. if (isset($res['errcode']) && $res['errcode'] == 0) {
  266. return true;
  267. } else {
  268. Log::error('删除订阅消息模版失败:' . $res['errmsg']);
  269. }
  270. } catch (\Throwable $e) {
  271. Log::error('删除订阅消息模版失败:' . $e->getMessage());
  272. }
  273. return true;
  274. }
  275. /**
  276. * 获取模版标题的关键词列表
  277. * @param string $tid
  278. * @return mixed
  279. */
  280. public static function getSubscribeTemplateKeyWords(string $tid)
  281. {
  282. try {
  283. $res = self::SubscribenoticeService()->getPublicTemplateKeywords($tid);
  284. if (isset($res['errcode']) && $res['errcode'] == 0 && isset($res['data'])) {
  285. return $res['data'];
  286. } else {
  287. throw new AdminException($res['errmsg']);
  288. }
  289. } catch (\Throwable $e) {
  290. throw new AdminException($e);
  291. }
  292. }
  293. /**
  294. * 获取订阅消息列表
  295. * @return mixed
  296. */
  297. public static function getSubscribeTemplateList()
  298. {
  299. try {
  300. return self::SubscribenoticeService()->getTemplateList();
  301. } catch (\Exception $e) {
  302. throw new AdminException($e->getMessage());
  303. }
  304. }
  305. /**
  306. * 支付
  307. * @return \EasyWeChat\Payment\Payment
  308. */
  309. public static function paymentService()
  310. {
  311. return self::application()->payment;
  312. }
  313. /**
  314. * 生成支付订单对象
  315. * @param $openid
  316. * @param $out_trade_no
  317. * @param $total_fee
  318. * @param $attach
  319. * @param $body
  320. * @param string $detail
  321. * @param string $trade_type
  322. * @param array $options
  323. * @return Order
  324. */
  325. protected static function paymentOrder($openid, $out_trade_no, $total_fee, $attach, $body, $detail = '', $trade_type = 'JSAPI', $options = [])
  326. {
  327. $total_fee = bcmul($total_fee, 100, 0);
  328. $order = array_merge(compact('openid', 'out_trade_no', 'total_fee', 'attach', 'body', 'detail', 'trade_type'), $options);
  329. if ($order['detail'] == '') unset($order['detail']);
  330. return new Order($order);
  331. }
  332. /**
  333. * 获得下单ID
  334. * @param $openid
  335. * @param $out_trade_no
  336. * @param $total_fee
  337. * @param $attach
  338. * @param $body
  339. * @param string $detail
  340. * @param string $trade_type
  341. * @param array $options
  342. * @return mixed
  343. */
  344. public static function paymentPrepare($openid, $out_trade_no, $total_fee, $attach, $body, $detail = '', $trade_type = 'JSAPI', $options = [])
  345. {
  346. $key = 'pay_' . $out_trade_no;
  347. $result = CacheService::get($key);
  348. if ($result) {
  349. return $result;
  350. } else {
  351. $order = self::paymentOrder($openid, $out_trade_no, $total_fee, $attach, $body, $detail, $trade_type, $options);
  352. $result = self::paymentService()->prepare($order);
  353. if ($result->return_code == 'SUCCESS' && $result->result_code == 'SUCCESS') {
  354. CacheService::set($key, $result->prepay_id, 7000);
  355. return $result->prepay_id;
  356. } else {
  357. if ($result->return_code == 'FAIL') {
  358. exception('微信支付错误返回:' . $result->return_msg);
  359. } else if (isset($result->err_code)) {
  360. exception('微信支付错误返回:' . $result->err_code_des);
  361. } else {
  362. exception('没有获取微信支付的预支付ID,请重新发起支付!');
  363. }
  364. exit;
  365. }
  366. }
  367. }
  368. /**
  369. * 获得下单ID
  370. * @param $openid
  371. * @param $out_trade_no
  372. * @param $total_fee
  373. * @param $attach
  374. * @param $body
  375. * @param string $detail
  376. * @param string $trade_type
  377. * @param array $options
  378. * @return mixed
  379. */
  380. public static function newPaymentPrepare($openid, $out_trade_no, $total_fee, $attach, $body, $detail = '', $options = [])
  381. {
  382. $key = 'pay_' . $out_trade_no;
  383. $result = CacheService::get($key);
  384. if ($result) {
  385. return $result;
  386. } else {
  387. $order = self::paymentOrder($openid, $out_trade_no, $total_fee, $attach, $body, $detail, $options);
  388. $result = self::application()->minipay->createorder($order);
  389. if ($result->errcode === 0) {
  390. CacheService::set($key, $result->payment_params, 7000);
  391. return $result->payment_params;
  392. } else {
  393. exception('微信支付错误返回:' . '[' . $result->errcode . ']' . $result->errmsg);
  394. exit;
  395. }
  396. }
  397. }
  398. /**
  399. * 获得jsSdk支付参数
  400. * @param $openid
  401. * @param $out_trade_no
  402. * @param $total_fee
  403. * @param $attach
  404. * @param $body
  405. * @param string $detail
  406. * @param string $trade_type
  407. * @param array $options
  408. * @return array|string
  409. */
  410. public static function jsPay($openid, $out_trade_no, $total_fee, $attach, $body, $detail = '', $trade_type = 'JSAPI', $options = [])
  411. {
  412. return self::paymentService()->configForJSSDKPayment(self::paymentPrepare($openid, $out_trade_no, $total_fee, $attach, $body, $detail, $trade_type, $options));
  413. }
  414. /**
  415. * 获得jsSdk支付参数
  416. * @param $openid
  417. * @param $out_trade_no
  418. * @param $total_fee
  419. * @param $attach
  420. * @param $body
  421. * @param string $detail
  422. * @param string $trade_type
  423. * @param array $options
  424. * @return array|string
  425. */
  426. public static function newJsPay($openid, $out_trade_no, $total_fee, $attach, $body, $detail = '', $options = [])
  427. {
  428. $config = self::newPaymentPrepare($openid, $out_trade_no, $total_fee, $attach, $body, $detail, $options);
  429. $config['timestamp'] = $config['timeStamp'];
  430. unset($config['timeStamp']);
  431. return $config;
  432. }
  433. /**
  434. * 获得App支付参数
  435. * @param $openid
  436. * @param $out_trade_no
  437. * @param $total_fee
  438. * @param $attach
  439. * @param $body
  440. * @param string $detail
  441. * @param string $trade_type
  442. * @param array $options
  443. * @return array|string
  444. */
  445. public static function appPay($openid, $out_trade_no, $total_fee, $attach, $body, $detail = '', $trade_type = Order::APP, $options = [])
  446. {
  447. return self::paymentService()->configForAppPayment(self::paymentPrepare($openid, $out_trade_no, $total_fee, $attach, $body, $detail, $trade_type, $options));
  448. }
  449. /**
  450. * 使用商户订单号退款
  451. * @param $orderNo
  452. * @param $refundNo
  453. * @param $totalFee
  454. * @param null $refundFee
  455. * @param null $opUserId
  456. * @param string $refundReason
  457. * @param string $type
  458. * @param string $refundAccount
  459. */
  460. public static function refund($orderNo, $refundNo, $totalFee, $refundFee = null, $opUserId = null, $refundReason = '', $type = 'out_trade_no', $refundAccount = 'REFUND_SOURCE_UNSETTLED_FUNDS')
  461. {
  462. $totalFee = floatval($totalFee);
  463. $refundFee = floatval($refundFee);
  464. if ($type == 'out_trade_no') {
  465. return self::paymentService()->refund($orderNo, $refundNo, $totalFee, $refundFee, $opUserId, $type, $refundAccount, $refundReason);
  466. } else {
  467. return self::paymentService()->refundByTransactionId($orderNo, $refundNo, $totalFee, $refundFee, $opUserId, $refundAccount, $refundReason);
  468. }
  469. }
  470. /**
  471. * 使用商户订单号退款
  472. * @param $orderNo
  473. * @param $refundNo
  474. * @param $totalFee
  475. * @param null $refundFee
  476. * @param null $opUserId
  477. * @param string $refundReason
  478. * @param string $type
  479. * @param string $refundAccount
  480. */
  481. public static function miniRefund($orderNo, $totalFee, $refundFee = null, $opt)
  482. {
  483. $totalFee = floatval($totalFee);
  484. $refundFee = floatval($refundFee);
  485. $order = [
  486. 'openid' => $opt['open_id'],
  487. 'trade_no' => $opt['order_id'],
  488. 'transaction_id' => $opt['trade_no'],
  489. 'refund_no' => $opt['refund_no'],
  490. 'total_amount' => $totalFee,
  491. 'refund_amount' => $refundFee,
  492. ];
  493. return self::application()->minipay->refundorder($order);
  494. }
  495. /** 根据订单号退款
  496. * @param $orderNo
  497. * @param array $opt
  498. * @return bool
  499. */
  500. public static function payOrderRefund($orderNo, array $opt)
  501. {
  502. if (!isset($opt['pay_price'])) throw new AdminException(400730);
  503. if (sys_config('pay_weixin_client_key') == '' || sys_config('pay_weixin_client_cert') == '') throw new AdminException(400739);
  504. $totalFee = floatval(bcmul($opt['pay_price'], 100, 0));
  505. $refundFee = isset($opt['refund_price']) ? floatval(bcmul($opt['refund_price'], 100, 0)) : null;
  506. $refundReason = $opt['desc'] ?? '';
  507. $refundNo = $opt['refund_id'] ?? $orderNo;
  508. $opUserId = $opt['op_user_id'] ?? null;
  509. $type = $opt['type'] ?? 'out_trade_no';
  510. /*仅针对老资金流商户使用
  511. REFUND_SOURCE_UNSETTLED_FUNDS---未结算资金退款(默认使用未结算资金退款)
  512. REFUND_SOURCE_RECHARGE_FUNDS---可用余额退款*/
  513. $refundAccount = $opt['refund_account'] ?? 'REFUND_SOURCE_UNSETTLED_FUNDS';
  514. try {
  515. $res = (self::refund($orderNo, $refundNo, $totalFee, $refundFee, $opUserId, $refundReason, $type, $refundAccount));
  516. if ($res->return_code == 'FAIL') throw new AdminException(400731, ['msg' => $res->return_msg]);
  517. if (isset($res->err_code)) throw new AdminException(400731, ['msg' => $res->err_code_des]);
  518. } catch (\Exception $e) {
  519. throw new AdminException($e->getMessage());
  520. }
  521. return true;
  522. }
  523. /**
  524. * 微信支付成功回调接口
  525. * @return \Symfony\Component\HttpFoundation\Response
  526. * @throws \EasyWeChat\Core\Exceptions\FaultException
  527. */
  528. public static function handleNotify()
  529. {
  530. return self::paymentService()->handleNotify(function ($notify, $successful) {
  531. if ($successful && isset($notify->out_trade_no)) {
  532. if (isset($notify->attach) && $notify->attach) {
  533. if (($count = strpos($notify->out_trade_no, '_')) !== false) {
  534. $notify->out_trade_no = substr($notify->out_trade_no, $count + 1);
  535. }
  536. (new Hook(PayNotifyServices::class, 'wechat'))->listen($notify->attach, $notify->out_trade_no, $notify->transaction_id);
  537. }
  538. return false;
  539. }
  540. });
  541. }
  542. /**
  543. * 作为客服消息发送
  544. * @param $to
  545. * @param $message
  546. * @return bool
  547. */
  548. public static function staffTo($to, $message)
  549. {
  550. $staff = self::staffService();
  551. $staff = is_callable($message) ? $staff->message($message()) : $staff->message($message);
  552. $res = $staff->to($to)->send();
  553. return $res;
  554. }
  555. /**
  556. * 获取直播列表
  557. * @param int $page
  558. * @param int $limit
  559. * @return array
  560. */
  561. public static function getLiveInfo(int $page = 1, int $limit = 10)
  562. {
  563. try {
  564. $res = self::miniprogram()->wechat_live->getLiveInfo($page, $limit);
  565. if (isset($res['errcode']) && $res['errcode'] == 0 && isset($res['room_info']) && $res['room_info']) {
  566. return $res['room_info'];
  567. } else {
  568. return [];
  569. }
  570. } catch (\Throwable $e) {
  571. return [];
  572. }
  573. }
  574. /**
  575. * 获取直播回放
  576. * @param int $room_id
  577. * @param int $page
  578. * @param int $limit
  579. * @return mixed
  580. */
  581. public static function getLivePlayback(int $room_id, int $page = 1, int $limit = 10)
  582. {
  583. try {
  584. $res = self::miniprogram()->wechat_live->getLivePlayback($room_id, $page, $limit);
  585. if (isset($res['errcode']) && $res['errcode'] == 0 && isset($res['live_replay'])) {
  586. return $res['live_replay'];
  587. } else {
  588. throw new AdminException($res['errmsg']);
  589. }
  590. } catch (\Throwable $e) {
  591. throw new AdminException(self::getValidMessgae($e));
  592. }
  593. }
  594. /**
  595. * 创建直播间
  596. * @param array $data
  597. * @return mixed
  598. */
  599. public static function createLiveRoom(array $data)
  600. {
  601. try {
  602. $res = self::miniprogram()->wechat_live->createRoom($data);
  603. if (isset($res['errcode']) && $res['errcode'] == 0 && isset($res['roomId'])) {
  604. unset($res['errcode']);
  605. return $res;
  606. } else {
  607. throw new AdminException($res['errmsg']);
  608. }
  609. } catch (\Throwable $e) {
  610. throw new AdminException(self::getValidMessgae($e));
  611. }
  612. }
  613. /**
  614. * 直播间添加商品
  615. * @param int $roomId
  616. * @param $ids
  617. * @return bool
  618. */
  619. public static function roomAddGoods(int $roomId, $ids)
  620. {
  621. try {
  622. $res = self::miniprogram()->wechat_live->roomAddGoods($roomId, $ids);
  623. if (isset($res['errcode']) && $res['errcode'] == 0) {
  624. return true;
  625. } else {
  626. throw new AdminException($res['errmsg']);
  627. }
  628. } catch (\Throwable $e) {
  629. throw new AdminException(self::getValidMessgae($e));
  630. }
  631. }
  632. /**
  633. * 获取商品列表
  634. * @param int $status
  635. * @param int $page
  636. * @param int $limit
  637. * @return mixed
  638. */
  639. public static function getGoodsList(int $status = 2, int $page = 1, int $limit = 10)
  640. {
  641. try {
  642. $res = self::miniprogram()->wechat_live->getGoodsList($status, $page, $limit);
  643. if (isset($res['errcode']) && $res['errcode'] == 0 && isset($res['goods'])) {
  644. return $res['goods'];
  645. } else {
  646. throw new AdminException($res['errmsg']);
  647. }
  648. } catch (\Throwable $e) {
  649. throw new AdminException(self::getValidMessgae($e));
  650. }
  651. }
  652. /**
  653. * 获取商品详情
  654. * @param $goods_ids
  655. * @return mixed
  656. */
  657. public static function getGooodsInfo($goods_ids)
  658. {
  659. try {
  660. $res = self::miniprogram()->wechat_live->getGooodsInfo($goods_ids);
  661. if (isset($res['errcode']) && $res['errcode'] == 0 && isset($res['goods'])) {
  662. return $res['goods'];
  663. } else {
  664. throw new AdminException($res['errmsg']);
  665. }
  666. } catch (\Throwable $e) {
  667. throw new AdminException(self::getValidMessgae($e));
  668. }
  669. }
  670. /**
  671. * 添加商品
  672. * @param string $coverImgUrl
  673. * @param string $name
  674. * @param int $priceType
  675. * @param string $url
  676. * @param $price
  677. * @param string $price2
  678. * @return mixed
  679. */
  680. public static function addGoods(string $coverImgUrl, string $name, int $priceType, string $url, $price, $price2 = '')
  681. {
  682. try {
  683. $res = self::miniprogram()->wechat_live->addGoods($coverImgUrl, $name, $priceType, $url, $price, $price2);
  684. if (isset($res['errcode']) && $res['errcode'] == 0 && isset($res['goodsId'])) {
  685. unset($res['errcode']);
  686. return $res;
  687. } else {
  688. throw new AdminException($res['errmsg']);
  689. }
  690. } catch (\Throwable $e) {
  691. throw new AdminException(self::getValidMessgae($e));
  692. }
  693. }
  694. /**
  695. * 商品撤回审核
  696. * @param int $goodsId
  697. * @param $auditId
  698. * @return bool
  699. */
  700. public static function resetauditGoods(int $goodsId, $auditId)
  701. {
  702. try {
  703. $res = self::miniprogram()->wechat_live->resetauditGoods($goodsId, $auditId);
  704. if (isset($res['errcode']) && $res['errcode'] == 0) {
  705. return true;
  706. } else {
  707. throw new AdminException($res['errmsg']);
  708. }
  709. } catch (\Throwable $e) {
  710. throw new AdminException(self::getValidMessgae($e));
  711. }
  712. }
  713. /**
  714. * 商品重新提交审核
  715. * @param int $goodsId
  716. * @return mixed
  717. */
  718. public static function auditGoods(int $goodsId)
  719. {
  720. try {
  721. $res = self::miniprogram()->wechat_live->auditGoods($goodsId);
  722. if (isset($res['errcode']) && $res['errcode'] == 0 && isset($res['auditId'])) {
  723. return $res['auditId'];
  724. } else {
  725. throw new AdminException($res['errmsg']);
  726. }
  727. } catch (\Throwable $e) {
  728. throw new AdminException(self::getValidMessgae($e));
  729. }
  730. }
  731. /**
  732. * 删除商品
  733. * @param int $goodsId
  734. * @return bool
  735. */
  736. public static function deleteGoods(int $goodsId)
  737. {
  738. try {
  739. $res = self::miniprogram()->wechat_live->deleteGoods($goodsId);
  740. if (isset($res['errcode']) && $res['errcode'] == 0) {
  741. return true;
  742. } else {
  743. throw new AdminException($res['errmsg']);
  744. }
  745. } catch (\Throwable $e) {
  746. throw new AdminException(self::getValidMessgae($e));
  747. }
  748. }
  749. /**
  750. * 更新商品
  751. * @param int $goodsId
  752. * @param string $coverImgUrl
  753. * @param string $name
  754. * @param int $priceType
  755. * @param string $url
  756. * @param $price
  757. * @param string $price2
  758. * @return bool
  759. */
  760. public static function updateGoods(int $goodsId, string $coverImgUrl, string $name, int $priceType, string $url, $price, $price2 = '')
  761. {
  762. try {
  763. $res = self::miniprogram()->wechat_live->updateGoods($goodsId, $coverImgUrl, $name, $priceType, $url, $price, $price2);
  764. if (isset($res['errcode']) && $res['errcode'] == 0) {
  765. return true;
  766. } else {
  767. throw new AdminException($res['errmsg']);
  768. }
  769. } catch (\Throwable $e) {
  770. throw new AdminException(self::getValidMessgae($e));
  771. }
  772. }
  773. /**
  774. * 更新商品
  775. * @param int $goodsId
  776. * @param string $coverImgUrl
  777. * @param string $name
  778. * @param int $priceType
  779. * @param string $url
  780. * @param $price
  781. * @param string $price2
  782. * @return bool
  783. */
  784. public static function getRoleList($role = 2, int $page = 0, int $limit = 30, $keyword = '')
  785. {
  786. try {
  787. $res = self::miniprogram()->wechat_live->getRoleList($role, $page, $limit, $keyword);
  788. if (isset($res['errcode']) && $res['errcode'] == 0 && isset($res['list'])) {
  789. return $res['list'];
  790. } else {
  791. throw new AdminException($res['errmsg']);
  792. }
  793. } catch (\Throwable $e) {
  794. throw new AdminException(self::getValidMessgae($e));
  795. }
  796. }
  797. public static function getValidMessgae(\Throwable $e)
  798. {
  799. $message = '';
  800. if (!isset(self::MSG_CODE[$e->getCode()]) && strstr($e->getMessage(), 'Request AccessToken fail') !== false) {
  801. $message = str_replace('Request AccessToken fail. response:', '', $e->getMessage());
  802. $message = json_decode($message, true) ?: [];
  803. $errcode = $message['errcode'] ?? false;
  804. if ($errcode) {
  805. $message = self::MSG_CODE[$errcode] ?? $message;
  806. }
  807. }
  808. return $message ?: self::MSG_CODE[$e->getCode()] ?? $e->getMessage();
  809. }
  810. /**
  811. * @return Response
  812. * @throws \EasyWeChat\Server\BadRequestException
  813. */
  814. public static function serve(): Response
  815. {
  816. $wechat = self::application(true);
  817. $server = $wechat->server;
  818. self::hook($server);
  819. $response = $server->serve();
  820. return response($response->getContent());
  821. }
  822. private static function hook($server)
  823. {
  824. $server->setMessageHandler(function ($message) {
  825. switch ($message->MsgType) {
  826. case 'event':
  827. switch (strtolower($message->Event)) {
  828. case 'funds_order_pay': // 小程序支付管理的
  829. if (($count = strpos($message['order_info']['trade_no'], '_')) !== false) {
  830. $trade_no = substr($message['order_info']['trade_no'], $count + 1);
  831. } else {
  832. $trade_no = $message['order_info']['trade_no'];
  833. }
  834. $prefix = substr($trade_no, 0, 2);
  835. //处理一下参数
  836. switch ($prefix) {
  837. case 'cp':
  838. $data['attach'] = 'Product';
  839. break;
  840. case 'hy':
  841. $data['attach'] = 'Member';
  842. break;
  843. case 'cz':
  844. $data['attach'] = 'UserRecharge';
  845. break;
  846. }
  847. $data['out_trade_no'] = $message['order_info']['trade_no'];
  848. $data['transaction_id'] = $message['order_info']['transaction_id'];
  849. $data['opneid'] = $message['FromUserName'];
  850. if (Event::until('NotifyListener', [$data, PayServices::WEIXIN_PAY])) {
  851. $response = 'success';
  852. } else {
  853. $response = 'faild';
  854. }
  855. Log::error(['data' => $data, 'res' => $response, 'message' => $message]);
  856. break;
  857. case 'trade_manage_remind_access_api': // 小程序完成账期授权时 小程序产生第一笔交易时 已产生交易但从未发货的小程序,每天一次
  858. break;
  859. case 'trade_manage_remind_shipping': // 曾经发过货的小程序,订单超过48小时未发货时
  860. break;
  861. case 'trade_manage_order_settlement': // 订单完成发货时 订单结算时
  862. if (isset($message['confirm_receive_method'])) { // 订单结算时
  863. /** @var StoreOrderTakeServices $StoreOrderTakeServices */
  864. $storeOrderTakeServices = app()->make(StoreOrderTakeServices::class);
  865. $storeOrderTakeServices->miniOrderTakeOrder($message['merchant_trade_no']);
  866. }
  867. break;
  868. };
  869. break;
  870. };
  871. });
  872. }
  873. public static function getUrlScheme($jumpWxa = [], $expireType = -1, $expireNum = 0)
  874. {
  875. try {
  876. $res = self::miniprogram()->mini_scheme->getUrlScheme($jumpWxa, $expireType, $expireNum);
  877. if (isset($res['errcode']) && $res['errcode'] == 0) {
  878. return $res['openlink'];
  879. } else {
  880. return '';
  881. }
  882. } catch (\Throwable $e) {
  883. return $e->getMessage();
  884. }
  885. }
  886. }