Pay.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. <?php
  2. /**
  3. * Niushop商城系统 - 团队十年电商经验汇集巨献!
  4. * =========================================================
  5. * Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
  6. * ----------------------------------------------
  7. * 官方网址: https://www.niushop.com
  8. * =========================================================
  9. */
  10. namespace addon\wechatpay\model;
  11. use addon\shopcomponent\model\Weapp;
  12. use app\exception\ApiException;
  13. use app\model\order\OrderMessage;
  14. use app\model\system\Cron;
  15. use app\model\BaseModel;
  16. use addon\weapp\model\Config as WeappConfig;
  17. use addon\wechat\model\Config as WechatConfig;
  18. use app\model\system\Pay as PayModel;
  19. use EasyWeChat\Factory;
  20. use think\facade\Log;
  21. /**
  22. * 微信支付配置
  23. * 版本 1.0.4
  24. */
  25. class Pay extends BaseModel
  26. {
  27. /**
  28. * 支付接口实例
  29. * @var
  30. */
  31. private $app;
  32. /**
  33. * 是否是小程序端
  34. * @var int
  35. */
  36. private $is_weapp = 0;
  37. /**
  38. * 支付配置
  39. * @var array|mixed
  40. */
  41. private $config = [];
  42. /**
  43. * 微信支付接口版本
  44. * @var string
  45. */
  46. private $api = 'v2';
  47. /**
  48. * 站点id
  49. * @var
  50. */
  51. private $site_id;
  52. public function __construct($is_weapp = 0, $site_id = 1)
  53. {
  54. $this->is_weapp = $is_weapp;
  55. $this->site_id = $site_id;
  56. // 支付配置
  57. $config_model = new Config();
  58. $this->config = $config_model->getPayConfig($site_id)[ 'data' ][ 'value' ];
  59. if (empty($this->config)) throw new ApiException(-1, "平台未配置微信支付");
  60. $this->api = $this->config[ 'api_type' ];
  61. $this->config[ 'site_id' ] = $site_id;
  62. if ($is_weapp == 0) {
  63. $wechat_config = ( new WechatConfig() )->getWechatConfig($this->site_id)[ 'data' ][ 'value' ];
  64. $this->config[ 'appid' ] = $wechat_config[ 'appid' ] ?? '';
  65. } else {
  66. $weapp_config = ( new WeappConfig() )->getWeappConfig($this->site_id)[ 'data' ][ 'value' ];
  67. $this->config[ 'appid' ] = $weapp_config[ 'appid' ] ?? '';
  68. }
  69. $this->factory();
  70. }
  71. /**
  72. * 实例化支付接口
  73. */
  74. public function factory()
  75. {
  76. $class = 'addon\\wechatpay\\model\\' . ucfirst($this->api);
  77. if (!class_exists($class)) throw new ApiException(-1, "Class '{$class}' not found");
  78. try {
  79. $this->app = new $class($this->config);
  80. } catch (\Exception $e) {
  81. Log::write('微信支付配置错误:' . $e->getMessage() . $e->getFile() . $e->getLine());
  82. throw new ApiException(-1, "微信支付配置错误");
  83. }
  84. }
  85. /**
  86. * 生成支付
  87. * @param $param
  88. */
  89. public function pay($param)
  90. {
  91. if (!$this->config[ 'pay_status' ]) return $this->error([], '平台未启用微信支付');
  92. ///绑定商户数据
  93. $pay_model = new PayModel();
  94. $pay_model->bindMchPay($param[ "out_trade_no" ], [ "app_id" => $this->config[ "appid" ] ]);
  95. if ($param[ 'trade_type' ] == 'JSAPI' || $param[ 'trade_type' ] == 'APPLET') {
  96. $member_info = model('member')->getInfo([ [ "member_id", "=", $param[ "member_id" ] ] ], 'weapp_openid,wx_openid');
  97. if (empty($member_info)) return $this->error(-1, '未获取到会员信息');
  98. $param[ 'openid' ] = $this->is_weapp ? $member_info[ 'weapp_openid' ] : $member_info[ 'wx_openid' ];
  99. }
  100. try {
  101. $result = $this->app->pay($param);
  102. // 对视频号订单做处理
  103. if ($result[ 'code' ] == 0 && in_array($param[ 'scene' ], [ 1175, 1176, 1177, 1191, 1195 ])) {
  104. $weapp_model = new Weapp($param[ 'site_id' ]);
  105. $prepay_id = str_replace('prepay_id=', '', $result[ 'data' ][ 'data' ][ 'package' ]);
  106. $order_info = $this->getOrderInfo($param[ "out_trade_no" ], $param[ 'openid' ], $prepay_id, $param[ 'scene' ]);
  107. $res = $weapp_model->createOrder($order_info);
  108. if ($res[ 'code' ] >= 0) {
  109. $order_params = [
  110. "order_id" => $res[ 'data' ][ 'data' ][ 'order_id' ],
  111. "out_order_id" => $res[ 'data' ][ 'data' ][ 'out_order_id' ],
  112. "openid" => $param[ "openid" ]
  113. ];
  114. $config = $weapp_model->getPaymentParams($order_params);
  115. $result = $this->success([
  116. 'type' => 'jsapi',
  117. 'data' => $config[ 'data' ][ 'payment_params' ]
  118. ]);
  119. }
  120. }
  121. return $result;
  122. } catch (\Exception $e) {
  123. Log::write('微信支付接口调用失败,请求参数:' . json_encode($param) . ' 错误原因:' . $e->getMessage() . $e->getFile() . $e->getLine());
  124. return $this->error([], '微信支付接口调用失败');
  125. }
  126. }
  127. /**
  128. * 支付异步通知
  129. * @param $param
  130. * @return mixed
  131. */
  132. public function payNotify()
  133. {
  134. return $this->app->payNotify();
  135. }
  136. /**
  137. * 关闭支付
  138. * @param $param
  139. */
  140. public function close($param)
  141. {
  142. try {
  143. return $this->app->payClose($param);
  144. } catch (\Exception $e) {
  145. Log::write('微信订单关闭失败,请求参数:' . json_encode($param) . ' 错误原因:' . $e->getMessage() . $e->getFile() . $e->getLine());
  146. return $this->error([], '微信订单关闭失败');
  147. }
  148. }
  149. /**
  150. * 微信原路退款
  151. * @param $param
  152. */
  153. public function refund($param)
  154. {
  155. if (!$this->config[ 'refund_status' ]) return $this->error([], '平台未启用微信退款');
  156. try {
  157. $mch_info = empty($param[ "pay_info" ][ 'mch_info' ]) ? [] : json_decode($param[ "pay_info" ][ 'mch_info' ], true);
  158. $this->config[ "appid" ] = $mch_info[ "app_id" ] ?? $this->config[ "appid" ];//替换为商户自己的appid
  159. $this->factory();
  160. return $this->app->refund($param);
  161. } catch (\Exception $e) {
  162. Log::write('微信退款失败,请求参数:' . json_encode($param) . ' 错误原因:' . $e->getMessage() . $e->getFile() . $e->getLine());
  163. return $this->error([], '微信退款失败');
  164. }
  165. }
  166. /**
  167. * 微信转账到零钱
  168. * @param array $param
  169. */
  170. public function transfer(array $param)
  171. {
  172. if (!$this->config[ 'transfer_status' ]) return $this->error([], '平台未启用微信转账');
  173. if ($this->api != $this->config[ 'transfer_type' ]) {
  174. $this->api = $this->config[ 'transfer_type' ];
  175. $this->factory();
  176. }
  177. try {
  178. $result = $this->app->transfer($param);
  179. if ($result[ 'code' ] == 0 && $this->api == 'v3') {
  180. ( new Cron() )->addCron(1, 0, "查询转账结果", "TransferResult", ( time() + 10 ), $param[ 'id' ]);
  181. }
  182. return $result;
  183. } catch (\Exception $e) {
  184. Log::write('微信转账接口调用失败,错误原因:' . $e->getMessage() . $e->getFile() . $e->getLine());
  185. return $this->error([], '微信转账接口调用失败');
  186. }
  187. }
  188. /**
  189. * 获取支付所需orderinfo
  190. * @param $out_trade_no
  191. * @param $openid
  192. * @param $prepay_id
  193. * @return array
  194. */
  195. private function getOrderInfo($out_trade_no, $openid, $prepay_id, $scene = 0)
  196. {
  197. $order_info = model('order')->getInfo([ [ 'out_trade_no', '=', $out_trade_no ] ], 'site_id,create_time,order_id,order_type,member_id,order_money,delivery_money,name,full_address,mobile,delivery_store_info,delivery_store_name');
  198. $data = [
  199. 'create_time' => date("Y-m-d H:i:s", $order_info[ 'create_time' ]),
  200. 'out_order_id' => $order_info[ 'order_id' ],
  201. 'openid' => $openid,
  202. 'path' => ( new OrderMessage() )->handleUrl($order_info[ 'order_type' ], $order_info[ 'order_id' ]),
  203. 'out_user_id' => $order_info[ 'member_id' ],
  204. 'order_detail' => [
  205. 'product_infos' => [],
  206. 'pay_info' => [
  207. 'pay_method' => '微信支付',
  208. 'prepay_id' => $prepay_id,
  209. 'prepay_time' => date('Y-m-d H:i:s', time())
  210. ],
  211. 'price_info' => [
  212. 'order_price' => $order_info[ 'order_money' ] * 100,
  213. 'freight' => $order_info[ 'delivery_money' ] * 100,
  214. 'discounted_price' => 0,
  215. 'additional_price' => 0
  216. ]
  217. ],
  218. 'delivery_detail' => [],
  219. 'fund_type' => 1,
  220. 'expire_time' => time() + 3600
  221. ];
  222. // 配送方式
  223. switch ( $order_info[ 'order_type' ] ) {
  224. case 2:
  225. $delivery_store_info = json_decode($order_info[ 'delivery_store_info' ], true);
  226. $data[ 'delivery_detail' ][ 'delivery_type' ] = 4; //用户自提
  227. $data[ 'address_info' ] = [
  228. 'receiver_name' => $order_info[ 'delivery_store_name' ],
  229. 'detailed_address' => $delivery_store_info[ 'full_address' ],
  230. 'tel_number' => $delivery_store_info[ 'telphone' ]
  231. ];
  232. break;
  233. case 3:
  234. $data[ 'delivery_detail' ][ 'delivery_type' ] = 3; //线下配送
  235. break;
  236. case 4:
  237. $data[ 'delivery_detail' ][ 'delivery_type' ] = 2; //无需快递
  238. break;
  239. default:
  240. $data[ 'delivery_detail' ][ 'delivery_type' ] = 1; //正常快递
  241. $data[ 'address_info' ] = [
  242. 'receiver_name' => $order_info[ 'name' ],
  243. 'detailed_address' => $order_info[ 'full_address' ],
  244. 'tel_number' => $order_info[ 'mobile' ]
  245. ];
  246. break;
  247. }
  248. $order_goods = model('order_goods')->getList([ [ 'order_id', '=', $order_info[ 'order_id' ] ] ], 'goods_id,sku_id,num,price,sku_name,sku_image');
  249. foreach ($order_goods as $goods) {
  250. array_push($data[ 'order_detail' ][ 'product_infos' ], [
  251. 'out_product_id' => $goods[ 'goods_id' ],
  252. 'out_sku_id' => $goods[ 'sku_id' ],
  253. 'product_cnt' => numberFormat($goods[ 'num' ]),
  254. 'sale_price' => $goods[ 'price' ] * 100,
  255. 'path' => 'pages/goods/detail?sku_id=' . $goods[ 'sku_id' ],
  256. 'title' => $goods[ 'sku_name' ],
  257. 'head_img' => img($goods[ 'sku_image' ]),
  258. 'sku_real_price' => $goods[ 'price' ] * 100
  259. ]);
  260. }
  261. $video_number_scene = [ 1175, 1176, 1177, 1191, 1195 ]; // 视频号场景值
  262. if (in_array($scene, $video_number_scene)) model('order')->update([ 'is_video_number' => 1 ], [ [ 'order_id', '=', $order_info[ 'order_id' ] ] ]);
  263. return $data;
  264. }
  265. /**
  266. * 付款码支付
  267. * @param $param
  268. * @return array
  269. */
  270. public function micropay($param)
  271. {
  272. if (!$this->config[ 'pay_status' ]) return $this->error([], '平台未启用微信支付');
  273. if ($this->api != 'v2') {
  274. $this->api = 'v2';
  275. $this->factory();
  276. }
  277. try {
  278. $res = $this->app->micropay($param);
  279. if ($res[ 'code' ] != 0) return $res;
  280. $pay_model = new PayModel();
  281. $pay_model->bindMchPay($param[ "out_trade_no" ], [ "app_id" => $this->config[ "appid" ] ]);
  282. $res = $pay_model->onlinePay($param[ 'out_trade_no' ], 'wechatpay', $res[ 'data' ][ 'transaction_id' ], 'wechatpay');
  283. return $res;
  284. } catch (\Exception $e) {
  285. Log::write('微信付款码支付失败,请求参数:' . json_encode($param) . ' 错误原因:' . $e->getMessage() . $e->getFile() . $e->getLine());
  286. return $this->error([], '微信付款码支付失败');
  287. }
  288. }
  289. }