store_id ?? get_store_id(), Wechat::WECHAT_KIND_PAY, WechatConfig::TYPE_CONFIG_APP); } else if ($is_official) { parent::init($order->store_id ?? get_store_id(), Wechat::WECHAT_KIND_PAY, WechatConfig::TYPE_CONFIG_MP); } else if ($is_h5) { parent::init($order->store_id ?? get_store_id(), Wechat::WECHAT_KIND_PAY, WechatConfig::TYPE_CONFIG_H5); } else { parent::init($order->store_id ?? get_store_id(), Wechat::WECHAT_KIND_PAY); } } catch (\Exception $e) { return [ 'code' => 1, 'msg' => $e->getMessage() ]; } if (!$order || !in_array($type, OrderNo::$validOrderType) || ($type != OrderNo::ORDER_UNION && !$goods_names)) { return [ 'code' => 1, 'msg' => '订单信息或订单类型错误' ]; } if ($is_official) { $order->order_origin = Order::ORDER_SOURCE_MP; $order->save(); } if (empty($open_id)) { if ($is_app) { $open_id = get_user()->wechat_app_open_id; } else if ($is_official) { $open_id = get_user()->wechat_platform_open_id; if (empty($open_id)) { return [ 'code' => 1, 'msg' => '支付失败 需前往个人中心 - 点击头像 - 一键同步用户信息 获取信息', ]; } } else { $open_id = get_user()->wechat_open_id; if($type == OrderNo::ORDER_UNION){ if (intval($order[0]->is_platform) === 1) { $open_id = get_saas_user()->platform_open_id; } }else{ if (intval($order->is_platform) === 1) { $open_id = get_saas_user()->platform_open_id; } } } } if (!$open_id) { $open_id = get_saas_user()->platform_open_id; } if ($type == OrderNo::ORDER_UNION) { $config = [ 'body' => count($order) . '笔订单合并支付', 'out_trade_no' => OrderNo::getOrderNo(OrderNo::ORDER_UNION), 'total_fee' => $balance_price > 0 ? round(floatval($total_pay_price - $balance_price) * 100) : $total_pay_price * 100, 'notify_url' => pay_notify_url(self::$notify_url, $order->store_id), 'openid' => $open_id, ]; if ($is_h5) { $config['trade_type'] = 'MWEB'; } elseif ($is_app) { $config['trade_type'] = 'APP'; unset($config['openid']); } else { $config['trade_type'] = 'JSAPI'; } $order_union = new OrderUnion(); $order_union->store_id = get_store_id(); $order_union->user_id = get_user()->id; $order_union->order_no = $config['out_trade_no']; $order_union->price = $total_pay_price; $order_union->is_pay = 0; $order_union->created_at = time(); $order_union->is_delete = 0; $order_id_list = []; foreach ($order as $value) { $order_id_list[] = $value->id; } $order_union->order_id_list = json_encode($order_id_list); if (!$order_union->save()) { foreach ($order_union->errors as $error) { return [ 'code' => 1, 'msg' => $error ]; } } } else { $config = [ 'body' => $goods_names, 'out_trade_no' => $order->order_no, 'total_fee' => $balance_price > 0 ? round(floatval($order->pay_price - $balance_price) * 100) : round($order->pay_price * 100), 'notify_url' => pay_notify_url(self::$notify_url, $order->store_id), 'openid' => $open_id, ]; if ($is_h5) { $config['trade_type'] = 'MWEB'; } elseif ($is_app) { $config['trade_type'] = 'APP'; unset($config['openid']); } else { $config['trade_type'] = 'JSAPI'; } } try { $res = self::$wechat_pay->order->unify($config); } catch (GuzzleException $e) { return [ 'code' => 1, 'msg' => $e->getMessage() ]; } catch (\Exception $e) { return [ 'code' => 1, 'msg' => $e->getMessage() ]; } if (!$res) { return [ 'code' => 1, 'msg' => '支付失败', ]; } if ($res['return_code'] != 'SUCCESS') { return [ 'code' => 1, 'msg' => '支付失败,' . (isset($res['return_msg']) ? $res['return_msg'] : ''), 'res' => $res, ]; } if ($res['result_code'] != 'SUCCESS') { if ($res['err_code'] == 'INVALID_REQUEST') { //商户订单号重复 $order->order_no = OrderNo::getOrderNo($type); $order->save(); if ($type != OrderNo::ORDER_UNION) { if ($is_app) { return self::orderUnify($order, $type, $goods_names, 0, true, $balance_price); } else if ($is_h5) { return self::orderUnify($order, $type, $goods_names, 0, false, $balance_price, true); } else if ($is_official) { return self::orderUnify($order, $type, $goods_names, 0, false, $balance_price, false, true); } else { return self::orderUnify($order, $type, $goods_names, 0, false, $balance_price); } } if ($is_app) { return self::orderUnify($order, $type, '', $total_pay_price, true, $balance_price); } else if ($is_h5) { return self::orderUnify($order, $type, '', $total_pay_price, false, $balance_price, true); } else if ($is_official) { return self::orderUnify($order, $type, '', $total_pay_price, false, $balance_price, false, true); } else { return self::orderUnify($order, $type, '', $total_pay_price, false, $balance_price); } } else { return [ 'code' => 1, 'msg' => '支付失败,' . (isset($res['err_code_des']) ? $res['err_code_des'] : ''), 'res' => $res, ]; } } if ($is_app) { $pay_data = self::$wechat_pay->jssdk->appConfig($res['prepay_id']); } else { $pay_data = self::$wechat_pay->jssdk->bridgeConfig($res['prepay_id'], false); } $return = [ 'code' => 0, 'msg' => 'success', 'res' => $res, 'data' => $pay_data ]; if ($type == OrderNo::ORDER_UNION) { foreach ($order as $value) { $value->order_union_id = $order_union->id; $value->save(); } $return['order_no'] = $config['out_trade_no']; $return['body'] = $config['body']; } \Yii::error($return); return $return; } /** * 退款 * @param Object $order * @param string $orderRefundNo * @param string $type * @param integer $refundFee * @param $refund_account * @return array */ public static function orderRefund($order, $type, $refundFee, $orderRefundNo, $refund_account = null) { try { if ($order->order_origin == Order::ORDER_SOURCE_APP) { parent::init($order->store_id, Wechat::WECHAT_KIND_PAY, WechatConfig::TYPE_CONFIG_APP); } else if ($order->order_origin == Order::ORDER_SOURCE_MP) { parent::init($order->store_id, Wechat::WECHAT_KIND_PAY, WechatConfig::TYPE_CONFIG_MP); } else if ($order->order_origin == Order::ORDER_SOURCE_WEB) { parent::init($order->store_id, Wechat::WECHAT_KIND_PAY, WechatConfig::TYPE_CONFIG_H5); } else { parent::init($order->store_id, Wechat::WECHAT_KIND_PAY); } } catch (\Exception $e) { return [ 'code' => 1, 'msg' => $e->getMessage() ]; } if ($type != OrderNo::ORDER_UNION) { if ($order->is_combine_pay == 1 && $order->combine_money > 0) { $payPrice = round($order->pay_price - $order->combine_money, 2); } else { $payPrice = $order->pay_price; } } else { // 联合订单支付的总额 $payPrice = $order->price; } if ($order->is_combine_pay == 1 && $order->combine_money > 0) { $refundFee = round($refundFee - $order->combine_money, 2); } $data = [ 'out_trade_no' => $order->order_no, 'out_refund_no' => $orderRefundNo, 'total_fee' => intval(bcmul(number_format($payPrice, 2, '.', ''), 100, 2)), 'refund_fee' => intval(bcmul(number_format($refundFee, 2, '.', ''), 100, 2)), ]; if (isset($order->order_union_id) && $order->order_union_id != 0) { // 多商户合并订单退款 $orderUnion = OrderUnion::findOne($order->order_union_id); if (!$orderUnion) { return [ 'code' => 1, 'msg' => '订单取消失败,合并支付订单不存在。', ]; } $data['out_trade_no'] = $orderUnion->order_no; $data['total_fee'] = $orderUnion->price * 100; } $config = []; if ($refund_account) { $config['refund_account'] = $refund_account; } try { $res = self::$wechat_pay->refund->byOutTradeNumber($data['out_trade_no'], $orderRefundNo, $data['total_fee'], $data['refund_fee'], $config); } catch (\Exception $e) { return [ 'code' => 1, 'msg' => $e->getMessage() ]; } if (!$res) { return [ 'code' => 1, 'msg' => '订单取消失败,退款失败,服务端配置出错', ]; } if ($res['return_code'] != 'SUCCESS') { return [ 'code' => 1, 'msg' => '订单取消失败,退款失败,' . $res['return_msg'], 'res' => $res, ]; } if (isset($res['err_code']) && $res['err_code'] == 'NOTENOUGH' && !$refund_account) { // 交易未结算资金不足,请使用可用余额退款 return self::orderRefund($order, $type, $refundFee, $orderRefundNo, 'REFUND_SOURCE_RECHARGE_FUNDS'); } if ($res['result_code'] != 'SUCCESS') { $refundQuery = self::$wechat_pay->order->queryByOutTradeNumber($order->order_no); if ($refundQuery['return_code'] != 'SUCCESS') { return [ 'code' => 1, 'msg' => '订单取消失败,退款失败,' . ($refundQuery['return_msg'] ?: $res['err_code_des']), 'res' => $refundQuery, 'res0' => $res, ]; } if ($refundQuery['result_code'] == 'FAIL') { return [ 'code' => 1, 'msg' => '订单取消失败,退款失败,' . $res['err_code_des'], 'res' => $res, ]; } if ($refundQuery['result_code'] != 'SUCCESS') { return [ 'code' => 1, 'msg' => '订单取消失败,退款失败,' . ($refundQuery['err_code_des'] ?: $res['err_code_des']), 'res' => $refundQuery, 'res0' => $res, ]; } if ($refundQuery['refund_status_0'] != 'SUCCESS') { return [ 'code' => 1, 'msg' => '订单取消失败,退款失败,' . ($refundQuery['err_code_des'] ?: $res['err_code_des']), 'res' => $refundQuery, 'res0' => $res, ]; } } // 联合支付,退余额 if ($order->is_combine_pay == 1 && $order->combine_money > 0) { AccountLog::saveLog($order->user_id, $order->combine_money, AccountLog::TYPE_BALANCE, AccountLog::LOG_TYPE_INCOME, AccountLog::TYPE_PLATFORM_REFUND_ORDER, $order->id, "商城订单退款,订单号:{$order->order_no}"); } return [ 'code' => 0, 'msg' => 'success', 'data' => true ]; } /** * 转账 * @param string $trade_no 商户订单号 * @param integer $amount * @param integer $user_name * @param string $desc * @param string $openid * @return array * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException * @throws \GuzzleHttp\Exception\GuzzleException */ public static function transfer($trade_no, $amount, $user_name, $openid, $desc = null) { if (!$trade_no || $amount <= 0 || empty($amount) || empty($openid)) { return [ 'code' => 1, 'msg' => '缺少必要参数' ]; } try { parent::init(get_store_id(), Wechat::WECHAT_KIND_PAY); } catch (\Exception $e) { return [ 'code' => 1, 'msg' => $e->getMessage() ]; } $data = [ 'partner_trade_no' => $trade_no, 'openid' => $openid, 'check_name' => 'FORCE_CHECK', // 强校验用户姓名 NO_CHECK:不校验用户姓名 're_user_name' => $user_name, 'amount' => $amount * 100, 'desc' => $desc ? $desc : '转账' ]; $res = self::$wechat_pay->transfer->toBalance($data); if ($res['return_code'] != 'SUCCESS') { return [ 'code' => 1, 'msg' => '转账失败:,' . $res['return_msg'], 'res' => $res, ]; } if ($res['result_code'] != 'SUCCESS') { return [ 'code' => 1, 'msg' => '转账失败:' . $res['err_code_des'], 'res' => $res, ]; } return [ 'code' => 0, 'msg' => 'success', 'data' => true ]; } /** * 查询订单 * @param $trade_no * @return array */ public static function find($trade_no) { if (!$trade_no) { return [ 'code' => 1, 'msg' => '缺少必要参数' ]; } try { parent::init(get_store_id(), Wechat::WECHAT_KIND_PAY); } catch (\Exception $e) { return [ 'code' => 1, 'msg' => $e->getMessage() ]; } try { $res = self::$wechat_pay->order->queryByOutTradeNumber($trade_no); if ($res['trade_state'] == 'SUCCESS') { return [ 'code' => 0, 'msg' => 'success', 'res' => $res, ]; } } catch (Exception $e) { \Yii::warning(['WECHAT QUERY ORDER <==========>', $trade_no, $e->getMessage()]); } return [ 'code' => 1, 'msg' => 'fail', 'data' => false ]; } // 微信扫码付款 public static function micropay($order, $type, $goods_names = '赤炎鹰-超市', $total_pay_price = 0,$balance_price = 0,$auth_code){ parent::init(get_store_id(), Wechat::WECHAT_KIND_PAY); $data = [ 'appid' => self::$wechat_config->app_id, 'mch_id' => self::$wechat_config->mch_id, 'nonce_str' => self::makeNonceStr(12), 'body' => $goods_names, 'out_trade_no' => $order->order_no, //'total_fee' => (int)$order->pay_price * 100, 'total_fee' => $balance_price > 0 ? round(floatval($total_pay_price - $balance_price) * 100) : $total_pay_price * 100, 'spbill_create_ip' => $_SERVER['REMOTE_ADDR'], 'auth_code' => $auth_code, 'sign_type' => 'MD5' ]; $data['sign'] = self::makeSign($data); // 请求微信的支付 $data = DataTransform::arrayToXml($data); $result = cloud_post('https://api.mch.weixin.qq.com/pay/micropay',$data); return DataTransform::xmlToArray($result); } // 查询扫码付款状态 public static function orderQuery($order){ parent::init(get_store_id(), Wechat::WECHAT_KIND_PAY); $data = [ 'appid' => self::$wechat_config->app_id, 'mch_id' => self::$wechat_config->mch_id, 'out_trade_no' => $order->order_no, 'nonce_str' => self::makeNonceStr(12), 'sign_type' => 'MD5' ]; $data['sign'] = self::makeSign($data); // 请求微信的支付 $data = DataTransform::arrayToXml($data); $result = cloud_post('https://api.mch.weixin.qq.com/pay/orderquery',$data); return DataTransform::xmlToArray($result); } /** * 生成签名 * @param $data * @return string * User: hankaige * DATE TIME: 2022/12/8 13:46 */ public static function makeSign($data){ // 去空 foreach($data as $key=>$value){ if(empty($value)){ unset($data[$key]); } } // 按ASSII排序 ksort($data); // 转字符串 $str = ''; foreach($data as $m=>$n){ $str .= $m . '=' . $n . '&'; } $str .= 'key='.self::$wechat_config->pay_key; // 进行 return strtoupper(MD5($str)); } /** * 获取随机字符串 * @param $len * @return string * User: hankaige * DATE TIME: 2022/12/8 13:46 */ public static function makeNonceStr($len = 6): string { $chars = array( "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ); $charsLen = count($chars) - 1; shuffle($chars); // 将数组打乱 $output = ""; for ($i = 0; $i < $len; $i++) { $output .= $chars[mt_rand(0, $charsLen)]; } return $output; } }