WechatJsapiPayService.php 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: yunzhong
  5. * Date: 2019/11/29
  6. * Time: 19:26
  7. */
  8. namespace app\common\services\wechat;
  9. use app\common\exceptions\AppException;
  10. use app\common\helpers\Url;
  11. use app\common\models\Member;
  12. use app\common\models\OrderPay;
  13. use app\common\models\PayType;
  14. use app\common\services\Pay;
  15. use app\common\services\wechat\lib\WxPayApi;
  16. use app\common\services\wechat\lib\WxPayConfig;
  17. use app\common\services\wechat\lib\WxPayException;
  18. use app\common\services\wechat\lib\WxPayJsApiPay;
  19. use app\common\services\wechat\lib\WxPayRefund;
  20. use app\common\services\wechat\lib\WxPayUnifiedOrder;
  21. class WechatJsapiPayService extends Pay
  22. {
  23. private static $attach_type = 'account';
  24. /**
  25. * 订单支付
  26. * @param array $data
  27. * @param int $payType
  28. * @return array|bool|mixed
  29. * @throws AppException
  30. */
  31. public function doPay($data = [], $payType = 48)
  32. {
  33. $client_type = null;
  34. $op = '微信订单支付订单号:' . $data['order_no'];
  35. $pay_type_name = PayType::get_pay_type_name($payType);
  36. $pay_order_model = $this->log(1, $pay_type_name, $data['amount'], $op, $data['order_no'], Pay::ORDER_STATUS_NON, \YunShop::app()->getMemberId());
  37. if (empty(\YunShop::app()->getMemberId())) {
  38. throw new AppException('无法获取用户ID');
  39. }
  40. $client_type = \YunShop::request()->type ?: $payType;
  41. $is_h5 = request()->type == 5 ? 1 : 0;
  42. $config = new WxPayConfig();
  43. $openid = Member::getOpenIdForType(\YunShop::app()->getMemberId(), $client_type);
  44. $data['trade_type'] = 'JSAPI';
  45. if ($is_h5){
  46. $data['trade_type'] = 'MWEB';
  47. }
  48. /* 支付请求对象 */
  49. $wxPay = new WxPayUnifiedOrder();
  50. //设置商品或支付单简要描述
  51. $wxPay->SetBody(mb_substr($data['subject'], 0, 120));
  52. //设置商家数据包,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
  53. $wxPay->SetAttach(\YunShop::app()->uniacid . ':' . self::$attach_type .':'.(new WxPayConfig())->GetProfitSharing().':'.request()->store_id);
  54. //设置商户系统内部的订单号
  55. $wxPay->SetOut_trade_no($data['order_no']);
  56. //设置订单总金额
  57. $wxPay->SetTotal_fee($data['amount'] * 100);
  58. $wxPay->SetDevice_info('yun_shop');
  59. if ($config->getIndependent()) {
  60. $wxPay->SetSubOpenid($openid);
  61. } else {
  62. $wxPay->SetOpenid($openid);
  63. }
  64. // $wxPay->SetAttach((new WxPayConfig())->GetProfitSharing());
  65. $wxPay->SetTrade_type($data['trade_type']);
  66. $wxPay->SetNotify_url(Url::shopSchemeUrl('payment/wechat/jsapiNotifyUrl.php'));
  67. //请求数据日志
  68. self::payRequestDataLog($data['order_no'], $pay_order_model->type, $pay_order_model->third_type, json_encode($data));
  69. $result = WxPayApi::unifiedOrder($config, $wxPay);
  70. if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS'){
  71. $pay_order_model->status = Pay::ORDER_STATUS_WAITPAY;
  72. $pay_order_model->trade_no = '';
  73. $pay_order_model->save();
  74. } elseif ($result['return_code'] == 'SUCCESS') {
  75. throw new AppException($result['err_code_des']);
  76. } else {
  77. throw new AppException($result['return_msg']);
  78. }
  79. if ($is_h5){
  80. $config = $result;
  81. }else{
  82. $config = $this->GetJsApiParameters($result, $config);
  83. }
  84. // $url = 'https://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
  85. // $nonce = WxPayApi::getNonceStr();
  86. // $ticket = $this->ticket();
  87. // $timestamp = time();
  88. // $signPackage = [
  89. // 'appId' => $result['appid'],
  90. // 'nonceStr' => $nonce,
  91. // 'timestamp' => $timestamp,
  92. // 'url' => $url,
  93. // 'signature' => sha1("jsapi_ticket={$ticket}&noncestr={$nonce}&timestamp={$timestamp}&url={$url}"),
  94. // 'debug' => false,
  95. // 'beta' => false,
  96. // 'jsApiList' => ['chooseWXPay']
  97. // ];
  98. return [
  99. 'config'=>$config,
  100. // 'js'=>json_encode($signPackage)
  101. ];
  102. }
  103. /**
  104. * 退款
  105. *
  106. * @param $out_trade_no 订单号
  107. * @param $totalmoney 订单总金额
  108. * @param $refundmoney 退款金额
  109. * @return mixed
  110. */
  111. public function doRefund($out_trade_no, $totalmoney, $refundmoney)
  112. {
  113. $totalmoney = intval(bcmul($totalmoney, 100, 0));
  114. $refundmoney = intval(bcmul($refundmoney, 100, 0));
  115. $this->getSubMchId($out_trade_no);
  116. $wxPayApi = new WxPayApi;
  117. $config = new WxPayConfig;
  118. $wxPayRefund = new WxPayRefund;
  119. $wxPayRefund->SetOut_trade_no($out_trade_no);
  120. $wxPayRefund->SetTotal_fee($totalmoney);
  121. $wxPayRefund->SetRefund_fee($refundmoney);
  122. $outRefundNo = createNo('RN', 20, true); //生产唯一的订单号;
  123. $wxPayRefund->SetOut_refund_no($outRefundNo);
  124. if (!$config->GetSubMerchantId()) {
  125. throw new AppException('请先配置门店子商户参数');
  126. }
  127. $wxPayRefund->SetSubMchId($config->GetSubMerchantId());
  128. $payTypeId = OrderPay::get_paysn_by_pay_type_id($out_trade_no);
  129. $payTypeName = PayType::get_pay_type_name($payTypeId);
  130. $op = '微信退款 订单号:' . $out_trade_no . '退款单号:' . $outRefundNo . '退款总金额:' . $totalmoney;
  131. $payOrderModel = $this->refundlog(Pay::PAY_TYPE_REFUND, $payTypeName, $refundmoney, $op, $out_trade_no, Pay::ORDER_STATUS_NON, 0);
  132. $result = $wxPayApi::refund($config, $wxPayRefund);
  133. \Log::debug('微信服务商退款记录', $result);
  134. if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
  135. $payOrderModel->status = Pay::ORDER_STATUS_COMPLETE;
  136. $payOrderModel->trade_no = $result['transaction_id'];
  137. $payOrderModel->save();
  138. return true;
  139. } elseif ($result['return_code'] == 'SUCCESS') {
  140. throw new AppException($result['err_code_des']);
  141. } else {
  142. throw new AppException($result['return_msg']);
  143. }
  144. }
  145. /**
  146. * 提现
  147. *
  148. * @param $member_id 提现者用户ID
  149. * @param $out_trade_no 提现批次单号
  150. * @param $money 提现金额
  151. * @param $desc 提现说明
  152. * @param $type 只针对微信 1-企业支付(钱包) 2-红包
  153. * @return mixed
  154. */
  155. function doWithdraw($member_id, $out_trade_no, $money, $desc, $type)
  156. {
  157. // TODO: Implement doWithdraw() method.
  158. }
  159. /**
  160. * 构造签名
  161. *
  162. * @return mixed
  163. */
  164. function buildRequestSign()
  165. {
  166. // TODO: Implement buildRequestSign() method.
  167. }
  168. /**
  169. *
  170. * 获取jsapi支付的参数
  171. * @param array $UnifiedOrderResult 统一支付接口返回的数据
  172. * @param WxPayConfig $config 设置
  173. * @throws WxPayException
  174. *
  175. * @return array 数据,可直接填入js函数作为参数
  176. */
  177. public function GetJsApiParameters($UnifiedOrderResult, $config)
  178. {
  179. if(!array_key_exists("appid", $UnifiedOrderResult)
  180. || !array_key_exists("prepay_id", $UnifiedOrderResult)
  181. || $UnifiedOrderResult['prepay_id'] == "")
  182. {
  183. throw new WxPayException("参数错误");
  184. }
  185. $jsapi = new WxPayJsApiPay();
  186. if (\YunShop::request()->type == 2 && !$config->set['is_independent'] && $config->set['sub_mini_appid']) {
  187. $jsapi->SetAppid($UnifiedOrderResult["sub_appid"]);
  188. } else {
  189. $jsapi->SetAppid($UnifiedOrderResult["appid"]);
  190. }
  191. $timeStamp = time();
  192. $jsapi->SetTimeStamp("$timeStamp");
  193. $jsapi->SetSignType("MD5");
  194. $jsapi->SetNonceStr(WxPayApi::getNonceStr());
  195. $jsapi->SetPackage("prepay_id=" . $UnifiedOrderResult['prepay_id']);
  196. $config = new WxPayConfig();
  197. $jsapi->SetPaySign($jsapi->MakeSign($config));
  198. $parameters = $jsapi->GetValues();
  199. return $parameters;
  200. }
  201. // 获取门店子商户号
  202. private function getSubMchId($outTradeNo)
  203. {
  204. if (app('plugins')->isEnabled('store-cashier')) {
  205. $orderPay = OrderPay::where('pay_sn', $outTradeNo)->first();
  206. $storeId = \Yunshop\StoreCashier\common\models\StoreOrder::where('order_id', $orderPay->orders->first()->id)->value('store_id');
  207. if ($storeId) {
  208. request()->offsetSet('store_id', $storeId);
  209. }
  210. }
  211. if (!isset($storeId)) {
  212. throw new AppException('请确认订单是否属于门店订单');
  213. }
  214. }
  215. }