WeChat.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | A3Mall
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2020 http://www.a3-mall.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Author: xzncit <158373108@qq.com>
  8. // +----------------------------------------------------------------------
  9. namespace app\common\library\payment\wechat;
  10. use mall\basic\Users;
  11. use mall\utils\Tool;
  12. use think\facade\Db;
  13. use xzncit\Factory;
  14. use app\common\models\Setting as SettingModel;
  15. use app\common\library\wechat\Factory as FactoryWeChat;
  16. use app\common\library\payment\PaymentException;
  17. use app\common\models\Payment as PaymentModel;
  18. use app\common\service\order\Order;
  19. class WeChat {
  20. protected $config = [];
  21. /**
  22. * 公众号支付
  23. * @param array $order
  24. * @return array
  25. * @throws PaymentException
  26. * @throws \think\db\exception\DataNotFoundException
  27. * @throws \think\db\exception\DbException
  28. * @throws \think\db\exception\ModelNotFoundException
  29. */
  30. public function mp($order=[]){
  31. $users = Db::name("wechat_users")->where("user_id",Users::get("id"))->find();
  32. if(empty($users)){
  33. throw new PaymentException("用户授权获取OPENID失败",[
  34. "pay" => 99,
  35. "order_id" => $order["id"],
  36. "msg" => "用户授权获取OPENID失败。"
  37. ]);
  38. }
  39. try{
  40. $result = $this->setConfig()->payment()->order->create([
  41. 'body' => $order["web_title"],
  42. 'openid' => $users["openid"],
  43. 'total_fee' => $order["order_amount"] * 100,
  44. 'trade_type' => 'JSAPI',
  45. 'notify_url' => createUrl('api/wechat/notify', [], false, true),
  46. 'out_trade_no' => $order["order_no"],
  47. 'spbill_create_ip' => getIP(),
  48. ]);
  49. return [
  50. "pay" => 1,
  51. "order_id" => $order["id"],
  52. "msg" => "ok",
  53. "result"=>[
  54. "options" => $this->setConfig()->payment()->order->jsApi($result["prepay_id"]),
  55. "config" => FactoryWeChat::wechat()->script->getJsSign(input("post.url","") ? input("post.url") : getDomain())
  56. ]
  57. ];
  58. }catch (\Exception $ex){
  59. throw new PaymentException($ex->getMessage(),0,[
  60. "pay" => 99,
  61. "order_id" => $order["id"],
  62. "msg" => $ex->getMessage()
  63. ]);
  64. }
  65. }
  66. /**
  67. * 移动端网页支付
  68. * @param array $order
  69. * @return array
  70. * @throws PaymentException
  71. */
  72. public function h5($order=[]){
  73. try{
  74. $result = $this->setConfig()->payment()->order->create([
  75. 'body' => $order["web_title"],
  76. 'total_fee' => $order["order_amount"] * 100,
  77. 'trade_type' => 'MWEB',
  78. 'notify_url' => createUrl('api/wechat/notify', [], false, true),
  79. 'out_trade_no' => $order["order_no"],
  80. 'spbill_create_ip' => getIP(),
  81. 'scene_info' => json_encode([
  82. "h5_info"=>[
  83. "type" => "Wap",
  84. "wap_url" => getDomain(),
  85. "wap_name" => explode("-",$order["web_title"])[0]
  86. ]
  87. ],JSON_UNESCAPED_UNICODE)
  88. ]);
  89. return [
  90. "pay" => 2,
  91. "order_id" => $order["id"],
  92. "msg" => "ok",
  93. "result"=>[
  94. "url" => $result["mweb_url"]
  95. ]
  96. ];
  97. }catch (\Exception $ex){
  98. throw new PaymentException($ex->getMessage(),$ex->getCode(),[
  99. "pay" => 99,
  100. "order_id" => $order["id"],
  101. "msg" => $ex->getMessage()
  102. ]);
  103. }
  104. }
  105. /**
  106. * APP支付
  107. * @param array $order
  108. * @return array
  109. * @throws PaymentException
  110. */
  111. public function app($order=[]){
  112. $config = (new PaymentModel())->getPayConfig("wechat-app");
  113. if(empty($config)){
  114. throw new \Exception("请配置微信支付");
  115. }
  116. if(empty($config["app_id"])){
  117. throw new \Exception("请配置微信支付 - appid");
  118. }
  119. if(empty($config["mch_id"])){
  120. throw new \Exception("请配置微信支付 - mch_id");
  121. }
  122. try{
  123. $result = $this->setConfig()->payment([
  124. "appid" => trim($config["app_id"]),
  125. "mch_id" => trim($config["mch_id"]),
  126. "mch_key" => trim($config["mch_key"]),
  127. "ssl_cer" => Tool::getRootPath() . trim($config["cert_url"],"/"),
  128. "ssl_key" => Tool::getRootPath() . trim($config["key_url"],"/")
  129. ])->order->create([
  130. 'body' => $order["web_title"],
  131. 'total_fee' => $order["order_amount"] * 100,
  132. 'trade_type' => 'APP',
  133. 'notify_url' => createUrl('api/wechat/notify', [], false, true),
  134. 'out_trade_no' => $order["order_no"],
  135. 'spbill_create_ip' => getDomain(),
  136. ]);
  137. return [
  138. "pay" => 1,
  139. "order_id" => $order["id"],
  140. "msg" => "ok",
  141. "result" => [
  142. "params" => $this->setConfig()->payment()->order->appApi($result["prepay_id"])
  143. ]
  144. ];
  145. }catch (\Exception $ex){
  146. throw new PaymentException($ex->getMessage(),$ex->getCode(),[
  147. "pay" => 99,
  148. "order_id" => $order["id"],
  149. "msg" => $ex->getMessage()
  150. ]);
  151. }
  152. }
  153. public function notify(){
  154. $obj = $this->setConfig()->payment()->order;
  155. $data = $obj->getNotify();
  156. // 签名验证sign是否存在
  157. if(!isset($data["sign"])){
  158. return $obj->getNotifyErrorReply();
  159. }
  160. // 检查订单号是否存在
  161. if(!isset($data["out_trade_no"])){
  162. return $obj->getNotifyErrorReply();
  163. }
  164. $prefix = substr($data["out_trade_no"],0,1);
  165. // 检查是否为充值订单
  166. if($prefix == "P"){
  167. $rechange = Db::name("users_rechange")->where("order_no",$data["out_trade_no"])->find();
  168. if(empty($rechange)) return $obj->getNotifyErrorReply();
  169. // 已充值成功的订单直接返回通知微信成功
  170. if($rechange["status"] == 1) return $obj->getNotifySuccessReply();
  171. // 按支付方式对签名验证,并校验返回的订单金额是否与商户侧的订单金额一致
  172. if(($rechange["order_amount"] * 100) != $data["total_fee"]){
  173. return $obj->getNotifyErrorReply();
  174. }
  175. try{
  176. // 开启事务
  177. Db::startTrans();
  178. Db::name("users_rechange")->where("order_no",$data["out_trade_no"])->update([
  179. "status"=>1,
  180. "transaction_id"=>$data["transaction_id"],
  181. "pay_time"=>time()
  182. ]);
  183. Db::name("users")->where("id",$rechange["user_id"])->inc("amount",$rechange["order_amount"])->update();
  184. Db::name("users_log")->insert([
  185. "order_no"=>$data["out_trade_no"],
  186. "user_id"=>$rechange["user_id"],
  187. "action"=>0,
  188. "operation"=>0,
  189. "amount"=>$rechange["order_amount"],
  190. "description"=>"充值成功,订单号:" . $data["out_trade_no"],
  191. "create_time"=>time()
  192. ]);
  193. Db::commit();
  194. return $obj->getNotifySuccessReply();
  195. }catch(\Exception $ex){
  196. Db::rollback();
  197. Db::name("users_rechange")->where("order_no",$data["out_trade_no"])->update([
  198. "status"=>2,
  199. "transaction_id"=>$data["transaction_id"],
  200. "pay_time"=>time()
  201. ]);
  202. return $obj->getNotifyErrorReply();
  203. }
  204. }
  205. // 签名验证,并校验返回的订单金额是否与商户侧的订单金额一致
  206. $order = Db::name("order")->where("order_no",$data["out_trade_no"])->find();
  207. if(empty($order)) return $obj->getNotifyErrorReply();
  208. // 充值成功直接通知微信成功
  209. if($order["pay_status"] == 1) return $obj->getNotifySuccessReply();
  210. // 按支付方式对签名验证,并校验返回的订单金额是否与商户侧的订单金额一致
  211. if(($order["order_amount"] * 100) != $data["total_fee"]){
  212. return $obj->getNotifyErrorReply();
  213. }
  214. try{
  215. // 开启事务
  216. Db::startTrans();
  217. Order::payment($data["out_trade_no"],0,"",$data["transaction_id"]);
  218. Db::name("order_log")->insert([
  219. 'order_id' => $order["id"],
  220. 'username' => "system",
  221. 'action' => '付款',
  222. 'result' => '成功',
  223. 'note' => '订单【' . $order["order_no"] . '】付款' . $order["order_amount"] . '元',
  224. 'create_time' => time()
  225. ]);
  226. Db::commit();
  227. sendSMS(["mobile"=>$order["mobile"],"order_no"=>$order["order_no"]],"payment_success");
  228. return $obj->getNotifySuccessReply();
  229. }catch(\Exception $ex){
  230. Db::rollback();
  231. return $obj->getNotifyErrorReply();
  232. }
  233. }
  234. /**
  235. * 创建支付对象
  236. * @param array $config
  237. * @return \xzncit\payment\Payment
  238. */
  239. public function payment(array $config=[]){
  240. if(empty($config)) $config = $this->config;
  241. return Factory::Payment($config);
  242. }
  243. /**
  244. * 获取支付配置
  245. * @return $this
  246. * @throws \think\db\exception\DataNotFoundException
  247. * @throws \think\db\exception\DbException
  248. * @throws \think\db\exception\ModelNotFoundException
  249. */
  250. public function setConfig(){
  251. $config = SettingModel::getArrayData("wepay");
  252. $config["appid"] = SettingModel::getArrayData("wechat.appid");
  253. $this->config = [
  254. "appid" => trim($config["appid"]),
  255. "mch_id" => trim($config["mch_id"]),
  256. "mch_key" => trim($config["mch_key"]),
  257. "ssl_cer" => Tool::getRootPath() . trim($config["cert_url"],"/"),
  258. "ssl_key" => Tool::getRootPath() . trim($config["key_url"],"/")
  259. ];
  260. return $this;
  261. }
  262. }