WechatTemplate.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. <?php
  2. namespace common\helpers\wechat;
  3. use common\components\WechatCache;
  4. use common\enums\StatusEnum;
  5. use common\models\user\UserExtra;
  6. use common\models\user\UserInfo;
  7. use common\models\wechat\WechatSendMessageLog;
  8. use EasyWeChat\Factory;
  9. use common\models\common\MallSetting;
  10. use GuzzleHttp\Exception\GuzzleException;
  11. class WechatTemplate
  12. {
  13. private $wechat = null;
  14. private $mall_id;
  15. public $openid;
  16. private $user_id;
  17. private $notice_wechat; //['order_create'=>['tem_key' => 'OPENTM401424715', 'name' => '订单生成通知', 'tempid'=>'Fpjmfna8511']];
  18. private $is_logging = null;
  19. private $is_miniapp_priority = null;
  20. private $is_open = null;
  21. private $app_id = null;
  22. private $wechat_app_id = null;
  23. const PLATFORM_MP_MX = 'mp-wx'; //小程序
  24. const PLATFORM_WECHAT = 'wechat'; //公众号
  25. const ERRCODE_INVALID_TEMPLATE_ID = 40037; //微信公众号没有该模板id
  26. const ORDER_CREATE = 'order_create'; //订单创建
  27. const ORDER_CANCEL = 'order_cancel'; //订单取消通知
  28. const ORDER_SENT = 'order_sent'; //订单发货
  29. const ORDER_CONFIRMED = 'order_confirmed'; //订单确认收货
  30. const ORDER_PAY_SUCCESS = 'order_pay_success'; //订单支付成功
  31. const ORDER_REFUND_AGREE = 'order_refund_agree'; //退货申请通过提醒
  32. const COMMISSION_RECEIPT = 'commission_receipt';//佣金到账通知
  33. const TEAM_ADD = 'team_add';//渠道人员增加通知
  34. const WITHDRAWAL_SUCCESS = 'withdrawal_success';//提现成功通知
  35. const INVITATION_TO_REGISTER_SUCCESS = 'Invitation_to_register_success';//邀请注册成功通知
  36. const RECOMMEND_PAY_SUCCESS = 'recommend_pay_success';// 被推荐人支付成功模版
  37. const ORDER_REFUND_RESULT = 'order_refund_result';// 售后订单处理结果模板
  38. const STORE_ORDER_PAY_SUCCESS = 'store_order_pay_success';// 订单支付成功通知
  39. const ORDER_COMMISSION_SETTLE = 'order_commission_settle'; //佣金到账通知
  40. const DEFAULT_PAGE = 'default_page';// 默认页面
  41. // const GROUP_BY_SUCCESS = 'group_buy_success'; //拼团成功通知
  42. // const GROUP_BY_FAILED = 'group_buy_failed'; //拼团失败通知
  43. // const BALANCE_CHANE = 'balance_change'; //账户余额通知
  44. // const INCOME_CHANGE = 'income_change'; //账户余额通知
  45. // const SCORE_CHANGE = 'score_change'; //积分交易提醒
  46. // const ORDER_REFUND_AGREE = 'order_refund_agree'; //退货申请通过提醒
  47. // const ORDER_REFUND_REFUSE = 'order_refund_refuse'; //售后申请审核通知
  48. // const ORDER_REFUND_MONEY = 'order_refund_money'; //退款成功通知
  49. // const USER_REGISTER = 'userRegister'; //下线加入通知
  50. // const UP_LEVEL = 'upLevel'; //会员等级变更通知
  51. // const E_COUPON_SEND = 'e_coupon_send'; //电子券通知
  52. // const ACHIEVEMENT_GRANT_PUSH = 'achievement_grant_push'; //定期业绩通知
  53. public static function getPagePath($key)
  54. {
  55. $result = [
  56. self::DEFAULT_PAGE => 'pages/index/index',
  57. self::ORDER_CREATE => 'plugins/pages/order/detail',
  58. self::COMMISSION_RECEIPT => 'public/promotion/index?title=',
  59. self::TEAM_ADD => 'plugins/public/promotion/client',
  60. self::WITHDRAWAL_SUCCESS => 'plugins/pages/assets/balance/list?type=2',
  61. self::ORDER_REFUND_AGREE => 'plugins/pages/refund/detail',
  62. self::ORDER_COMMISSION_SETTLE => 'plugins/public/promotion/index?title=',
  63. ];
  64. if (isset($result[$key])) return $result[$key];
  65. return '';
  66. }
  67. public function __construct($mall_id)
  68. {
  69. $this->mall_id = $mall_id;
  70. $notice_wechat = MallSetting::getConfByGroup($this->mall_id, 'notice_wechat', true);
  71. if (empty($notice_wechat) || !isset($notice_wechat['template_message'])) {
  72. $this->notice_wechat = false;
  73. } else {
  74. $this->is_open = $notice_wechat['is_open'];
  75. $this->is_logging = $notice_wechat['is_logging'];
  76. $this->is_miniapp_priority = $notice_wechat['is_miniapp_priority'];
  77. $template_massage = $notice_wechat['template_message'];
  78. $this->notice_wechat = json_decode($template_massage['value'], true);
  79. $wechat_setting = \Yii::$app->services->setting->mall->get($mall_id, 'wechat');
  80. $mini_program = \Yii::$app->services->setting->mall->get($mall_id, 'mini_program');
  81. $this->app_id = $mini_program['app_id'] ?? '';
  82. $this->wechat_app_id = $wechat_setting['app_id'] ?? '';
  83. if (empty($wechat_setting)) {
  84. $this->notice_wechat = false;
  85. } else {
  86. unset($wechat_setting['name']);
  87. unset($wechat_setting['qrcode']);
  88. $platform = $this->getPlatForm();
  89. // switch ($platform) {
  90. // case self::PLATFORM_WECHAT:
  91. $this->wechat = Factory::officialAccount($wechat_setting);
  92. $this->wechat->access_token->setCache(new WechatCache());
  93. // break;
  94. // case self::PLATFORM_MP_MX:
  95. // $this->wechat = Factory::miniProgram($mini_program);
  96. // $this->wechat->access_token->setCache(new WechatCache());
  97. //
  98. // break;
  99. // }
  100. }
  101. }
  102. }
  103. public static function getMap(): array
  104. {
  105. return [
  106. self::ORDER_CREATE => '订单生成通知',
  107. self::ORDER_CANCEL => '订单取消通知',
  108. self::ORDER_SENT => '订单发货通知',
  109. self::ORDER_CONFIRMED => '订单确认收货通知',
  110. self::ORDER_PAY_SUCCESS => '订单支付成功通知',
  111. // self::ORDER_REFUND_AGREE => '退货申请通过通知',
  112. // self::COMMISSION_RECEIPT => '佣金到账通知',
  113. // self::TEAM_ADD => '渠道人员增加通知',
  114. self::WITHDRAWAL_SUCCESS => '提现成功通知',
  115. self::INVITATION_TO_REGISTER_SUCCESS => '邀请注册成功通知',
  116. self::RECOMMEND_PAY_SUCCESS => '好友付款成功通知',
  117. self::ORDER_REFUND_RESULT => '售后订单处理结果通知',
  118. self::STORE_ORDER_PAY_SUCCESS => '订单支付成功通知',
  119. self::ORDER_COMMISSION_SETTLE => '佣金到账通知',
  120. ];
  121. }
  122. /**
  123. * 开启消息模板推送
  124. * @return bool
  125. */
  126. public function is_open()
  127. {
  128. if ($this->wechat === null) return false;
  129. return empty($this->is_open) ? false : ($this->is_open['value'] == 1 ? true : false);
  130. }
  131. /**
  132. * 是否记录数据库日志
  133. * @return bool
  134. */
  135. public function is_logging()
  136. {
  137. if ($this->wechat === null) return false;
  138. return empty($this->is_logging) ? false : ($this->is_logging['value'] == 1 ? true : false);
  139. }
  140. /**
  141. * 用户是否开启推送
  142. */
  143. public function is_notice($user_id)
  144. {
  145. $user_extra = UserExtra::find()->where(['user_id' => $user_id])->one();
  146. return empty($user_extra) ? false : ($user_extra['is_wechat_notice'] == 1 ? true : false);
  147. }
  148. /**
  149. * @param $user_id
  150. * @return bool
  151. */
  152. public function is_platform_wechat($user_id)
  153. {
  154. $platform = $this->getPlatForm();
  155. $platform_key = self::PLATFORM_WECHAT;
  156. // switch ($platform) {
  157. // case self::PLATFORM_WECHAT:
  158. $platform_key = self::PLATFORM_WECHAT;
  159. // break;
  160. // case self::PLATFORM_MP_MX:
  161. // $platform_key = self::PLATFORM_MP_MX;
  162. // break;
  163. // }
  164. $user_info = UserInfo::find()->where(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED, 'user_id' => $user_id, 'platform' => $platform_key])->one();
  165. return !empty($user_info) && $this->openid = $user_info['openid'];
  166. }
  167. /**
  168. * 发送模板消息
  169. * @param $user_id
  170. * @param $template_key
  171. * @param $url
  172. * @param $data
  173. * @param null $miniapp_page
  174. * @param null $template_id
  175. * @return bool|void
  176. * @throws GuzzleException
  177. */
  178. public function sendTemplate($user_id, $template_key, $url, $data, $miniapp_page = null, $template_id = null)
  179. {
  180. if ($this->wechat == null) return;
  181. //商城是否开启推送模板消息
  182. if ($this->is_logging() === false) return false;
  183. //用户是否开启模板消息
  184. if ($this->is_notice($user_id) === false) return false;
  185. //是否公众号用户
  186. if ($this->is_platform_wechat($user_id) === false) return false;
  187. try {
  188. if ($template_id == null) {
  189. $template_id = $this->checkTemplate($template_key);
  190. if (!$template_id) return false;
  191. }
  192. $this->user_id = $user_id;
  193. $result = [];
  194. $platform = $this->getPlatForm();
  195. switch ($platform) {
  196. case self::PLATFORM_WECHAT:
  197. $result = $this->sendWechat($this->openid, $template_id, $url, $data);
  198. break;
  199. case self::PLATFORM_MP_MX:
  200. $result = $this->sendMiniApp($this->openid, $template_id, $url, $data, $miniapp_page);
  201. break;
  202. }
  203. //微信公众号数据没有该模板,删除本地数据库中的数据
  204. if (!empty($result['errcode']) && $result['errcode'] != 0) {
  205. \Yii::error('发送模板消息失败, error_code=' . $result['errcode'] . ',error_message=' . $result['errmsg']);
  206. }
  207. } catch (\Exception $e) {
  208. var_dump($e->getMessage());
  209. \Yii::error('发送模板消息失败,失败原因:' . $e->getMessage() . $e->getFile() . ',错误行数:' . $e->getLine());
  210. return false;
  211. }
  212. return true;
  213. }
  214. /**
  215. * 添加模板
  216. * 参考:https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Template_Message_Interface.html#获得模板ID
  217. * @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
  218. * @throws GuzzleException
  219. * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
  220. */
  221. public function addTemplate($key,$template_id)
  222. {
  223. if(!empty($template_id)){
  224. $url = "cgi-bin/template/del_private_template";
  225. $post_data = ['template_id'=>$template_id];
  226. $this->wechat->template_message->httpPostJson($url, $post_data);
  227. }
  228. $params = $this->getTemplateInfo($key);
  229. $url = "cgi-bin/template/api_add_template";
  230. return $this->wechat->template_message->httpPostJson($url, $params);
  231. }
  232. /**
  233. * 获取对应模版消息
  234. * @return array
  235. */
  236. public function getTemplateInfo($key = null)
  237. {
  238. $data = [
  239. self::ORDER_CREATE => [// 用户下单成功通知-美妆
  240. 'template_id_short' => 48176,
  241. 'keyword_name_list' => ['订单编号','商品名称','下单时间','购买人','收货地址']
  242. ],
  243. self::ORDER_CANCEL => [// 订单取消通知-美妆
  244. 'template_id_short' => 48102,
  245. 'keyword_name_list' => ['订单编号','订单状态','取消原因']
  246. ],
  247. self::ORDER_PAY_SUCCESS => [// 付款成功通知-机械电子产品
  248. 'template_id_short' => 48847,
  249. 'keyword_name_list' => ['支付金额','商品名称','收货地址','订单编号']
  250. ],
  251. self::ORDER_SENT => [// 订单发货提醒-机械电子产品
  252. 'template_id_short' => 49296,
  253. 'keyword_name_list' => ['商品名称','订单编号','快递公司','快递单号','客户地址']
  254. ],
  255. self::ORDER_CONFIRMED => [// 确认收货通知参数-软件/建站/技术开发
  256. 'template_id_short' => 50439,
  257. 'keyword_name_list' => ['订单编号','商品名称','收货时间']
  258. ],
  259. self::WITHDRAWAL_SUCCESS => [// 提现成功通知-软件/建站/技术开发
  260. 'template_id_short' => 49581,
  261. 'keyword_name_list' => ['提现金额','提现日期']
  262. ],
  263. self::INVITATION_TO_REGISTER_SUCCESS => [// 用户注册成功通知-软件/建站/技术开发
  264. 'template_id_short' => 50810,
  265. 'keyword_name_list' => ['注册姓名','注册手机号','注册时间','注册来源']
  266. ],
  267. self::RECOMMEND_PAY_SUCCESS => [// 好友支付成功通知-软件/建站/技术开发
  268. 'template_id_short' => 49101,
  269. 'keyword_name_list' => ['订单编号','客户名称','付款日期','付款时间']
  270. ],
  271. self::ORDER_REFUND_RESULT => [// 商家自营-机械电子产品
  272. 'template_id_short' => 51562,
  273. 'keyword_name_list' => ['订单号', '处理时间', '处理结果']
  274. ],
  275. self::STORE_ORDER_PAY_SUCCESS => [// 商家自营-母婴用品
  276. 'template_id_short' => 61082,
  277. 'keyword_name_list' => ['订单号', '结算金', '结算时间']
  278. ],
  279. self::ORDER_COMMISSION_SETTLE => [ //佣金到账通知
  280. 'template_id_short' => 55697,
  281. 'keyword_name_list' => ['结算金额', '结算时间'],
  282. ],
  283. ];
  284. if (empty($data[$key])) throw new \Exception('暂无此消息模板');
  285. return $key ? $data[$key] : $data;
  286. }
  287. /**
  288. * @param $template_type
  289. * @return false|mixed
  290. * @throws \GuzzleHttp\Exception\GuzzleException
  291. */
  292. public function checkTemplate($template_type)
  293. {
  294. try {
  295. if (empty($this->notice_wechat)) return false;
  296. $template_massage = $this->notice_wechat;
  297. if (!isset($this->notice_wechat[$template_type])) return false;
  298. //获取
  299. if (!isset($this->notice_wechat[$template_type]['tempid'])) {
  300. if (!isset($this->notice_wechat[$template_type]['tem_key'])) return false;
  301. $data = $this->wechat->template_message->addTemplate($this->notice_wechat[$template_type]['tem_key']);
  302. if ($data['errcode'] == 0) {
  303. $template_massage[$template_type]['tempid'] = $data['template_id'];
  304. $setting = ['notice_wechat' => ['template_message' => json_encode($template_massage, JSON_UNESCAPED_UNICODE)]];
  305. \Yii::$app->services->setting->mall->set($this->mall_id, $setting);
  306. return $data['template_id'];
  307. } else {
  308. \Yii::error('发送模板消息失败,获取template_id失败,失败原因:' . $data['errmsg']);
  309. return false;
  310. }
  311. }
  312. return $this->notice_wechat[$template_type]['tempid'];
  313. } catch (\Exception $e) {
  314. \Yii::error('发送模板消息失败,获取template_id失败,失败原因:' . $e->getMessage() . ',错误行数:' . $e->getLine());
  315. return false;
  316. }
  317. }
  318. /**
  319. * 跳转平台
  320. * @return string
  321. */
  322. public function getPlatForm()
  323. {
  324. return $this->is_miniapp_priority['value'] == 1 ? 'mp-wx' : 'wechat';
  325. }
  326. /**
  327. * @param $touser
  328. * @param $template_id
  329. * @param $url
  330. * @param $data
  331. * [
  332. * 'touser' => 'owdRztxk_XOtVZ0xglXuGv_MsRbA',
  333. * 'template_id' => '7rTBQadzdA4qn2RSR0_6lYUJxBlR6HloEMl03AuPRK4',
  334. * 'url' => 'http://jxmall.com',
  335. * 'data' => [
  336. * 'productType' => '商品名称',
  337. * 'name' => '小米手机',
  338. * 'number' => 12,
  339. * 'expDate' => '永久有效',
  340. * 'remark' => '恭喜你,购买成功'
  341. * ],
  342. * ]
  343. *
  344. * @return array
  345. * {
  346. * "errcode": 0,
  347. * "errmsg": "ok",
  348. * "msgid": 1552413574407520257
  349. * }
  350. */
  351. public function sendWechat($touser, $template_id, $url, $data)
  352. {
  353. return $this->wechat->template_message->send([
  354. 'touser' => $touser,
  355. 'template_id' => $template_id,
  356. 'url' => $url,
  357. 'data' => $data
  358. ]);
  359. }
  360. /**
  361. * @param $touser
  362. * @param $template_id
  363. * @param $url
  364. * @param $data
  365. * @param $pagepath
  366. * @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
  367. * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
  368. * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
  369. * @throws \GuzzleHttp\Exception\GuzzleException
  370. */
  371. public function sendMiniApp($touser, $template_id, $url, $data, $pagepath)
  372. {
  373. $app_id = $this->app_id;
  374. $send_data = [
  375. 'touser' => $touser,
  376. 'template_id' => $template_id,
  377. 'url' => $url,
  378. 'data' => $data,
  379. 'miniprogram' => [
  380. 'appid' => $app_id,
  381. 'pagepath' => $pagepath,
  382. ],
  383. ];
  384. $result = $this->wechat->template_message->send($send_data);
  385. \Yii::error('================'.json_encode($send_data));
  386. if ($this->is_open()) {
  387. $params = [
  388. 'mall_id' => $this->mall_id,
  389. 'user_id' => $this->user_id,
  390. 'openid' => $this->openid,
  391. 'send_data' => json_encode($send_data),
  392. 'result_data' => json_encode($result),
  393. ];
  394. WechatSendMessageLog::add($params);
  395. return $result;
  396. }
  397. return $result;
  398. }
  399. }