is_platform) ? 0 : 1; try { parent::init(get_store_id(), $is_platform, 0, $from_type); } 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' => '订单信息或订单类型错误' ]; } $store_wechat = WechatConfig::findOne(['store_id' => get_store_id(), 'type' => $from_type]); $mch_id = $store_wechat->mch_id; if (!$mch_id) { $merchant_info = \app\models\MerchantInfo::findOne(['is_use' => 1, 'bind_store_id' => get_store_id(), 'status' => 3, 'is_delete' => 0]); if ($merchant_info->sub_mch_id) { $mch_id = $merchant_info->sub_mch_id; } } if ($is_official) { $order->order_origin = Order::ORDER_SOURCE_MP; $order->save(); } if (!$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; } else { $open_id = get_user()->wechat_open_id; } } if ($type == OrderNo::ORDER_UNION) { $config = [ 'body' => count($order) . '笔订单合并支付', 'out_trade_no' => OrderNo::getOrderNo(OrderNo::ORDER_UNION), 'total_fee' => $balance_price > 0 ? floatval($total_pay_price - $balance_price) * 100 : $total_pay_price * 100, 'notify_url' => pay_notify_url(self::$notify_url), 'sub_appid' => $store_wechat->app_id, 'sub_mch_id' => $mch_id, 'sub_openid' => $open_id, ]; if (is_platform()) { $config['sub_appid'] = Option::get('platform_appid', 0, 'saas')['value']; $config['sub_openid'] = SaasUser::findOne(['mobile' => get_user()->binding])->platform_open_id; } $self_mini = Option::get('self_mini', get_store_id(), 'store', 0)['value']; if (\Yii::$app->prod_is_dandianpu() && !$self_mini) { if ($from_type == WechatConfig::TYPE_CONFIG_MINI) { $config['sub_appid'] = Option::get('one_store_wechat_appid', 0, 'saas', '')['value']; } if ($from_type == WechatConfig::TYPE_CONFIG_MP) { $config['sub_appid'] = Option::get('one_store_wechat_official_appid', 0, 'saas', '')['value']; } if ($from_type == WechatConfig::TYPE_CONFIG_H5) { unset($config['sub_appid']); } } if ($is_h5) { $config['trade_type'] = 'MWEB'; } 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 { $pay_price = $order->pay_price; // if ($type != OrderNo::ORDER_RECHARGE) { // if (isset($order->is_delivery) && $order->is_delivery == 1) { // $deliveryInfo = DeliveryInfo::find()->where(['order_no' => $order->order_no])->one(); // if ($deliveryInfo) { // $pay_price += $deliveryInfo->fee; // } // } // } $config = [ 'body' => $goods_names, 'out_trade_no' => $order->order_no, 'total_fee' => $balance_price > 0 ? floatval($pay_price - $balance_price) * 100 : $pay_price * 100, 'notify_url' => pay_notify_url(self::$notify_url), 'sub_appid' => $store_wechat->app_id, 'sub_mch_id' => $mch_id, 'sub_openid' => $open_id, ]; if (is_platform()) { $config['sub_appid'] = Option::get('platform_appid', 0, 'saas')['value']; $config['sub_openid'] = SaasUser::findOne(['mobile' => get_user()->binding])->platform_open_id; } $self_mini = Option::get('self_mini', get_store_id(), 'store', 0)['value']; if (\Yii::$app->prod_is_dandianpu() && !$self_mini) { if ($from_type == WechatConfig::TYPE_CONFIG_MINI) { $config['sub_appid'] = Option::get('one_store_wechat_appid', 0, 'saas', '')['value']; $config['sub_openid'] = get_user()->wechat_open_id; } if ($from_type == WechatConfig::TYPE_CONFIG_MP) { $config['sub_appid'] = Option::get('one_store_wechat_official_appid', 0, 'saas', '')['value']; $config['sub_openid'] = get_user()->wechat_platform_open_id; } if ($from_type == WechatConfig::TYPE_CONFIG_H5) { unset($config['sub_appid']); } } if ($is_h5) { $config['trade_type'] = 'MWEB'; } else { $config['trade_type'] = 'JSAPI'; } if ($is_app) { $config['trade_type'] = 'APP'; } } // 是否走分账 if (is_profit_sharing()) { $config['profit_sharing'] = 'Y'; } \Yii::error($config); try { $res = self::$wechat_pay->order->unify($config); \Yii::error($res); } 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 (isset($res['err_code']) && $res['err_code'] == 'INVALID_REQUEST') { return [ 'code' => 1, 'msg' => '支付失败,' . $res['err_code_des'] ?? ($res['return_msg'] ?? ''), 'res' => $res, ]; } 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) { self::$wechat_pay->config->app_id = $store_wechat->app_id; self::$wechat_pay->config->mch_id = $store_wechat->mch_id; $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']; } if ($is_h5) { $return['res']['mweb_url'] = $return['res']['mweb_url'] . '&redirect_url=' . \Yii::$app->request->hostInfo . '/h5/#/order/order/order'; } \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, $order->is_platform, 0, WechatConfig::TYPE_CONFIG_APP); } else if ($order->order_origin == Order::ORDER_SOURCE_MP) { parent::init($order->store_id, $order->is_platform, 0, WechatConfig::TYPE_CONFIG_MP); } else if ($order->order_origin == Order::ORDER_SOURCE_WEB) { parent::init($order->store_id, $order->is_platform, 0, WechatConfig::TYPE_CONFIG_H5); } else { parent::init($order->store_id, $order->is_platform); } } 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($payPrice, 100)), 'refund_fee' => intval(bcmul($refundFee, 100)), ]; 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 [ 'code' => 1, 'msg' => '订单取消失败,退款失败,' . $res['err_code_des'], 'res' => $res, ]; 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' => $refundQuery, ]; } 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' => $refundQuery, ]; } if ($refundQuery['refund_status_0'] != 'SUCCESS') { return [ 'code' => 1, 'msg' => '订单取消失败,退款失败,' . $refundQuery['err_code_des'], 'res' => $refundQuery, ]; } } // 联合支付,退余额 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(); } 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 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 saasTransfer($trade_no, $amount, $user_name, $openid, $desc = null) { if (!$trade_no || $amount <= 0 || empty($amount) || empty($openid)) { return [ 'code' => 1, 'msg' => '缺少必要参数' ]; } try { parent::initSaas(WechatProfit::WECHAT_KIND_PAY); } catch (\Exception $e) { return [ 'code' => 1, 'msg' => $e->getMessage() ]; } $data = [ 'partner_trade_no' => $trade_no, 'openid' => $openid, 'check_name' => 'NO_CHECK', // 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(); } 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){ // 服务商参数 $wechat_config = Option::getSaasWechat(); // 特约商户参数 $store_wechat = WechatConfig::findOne(['store_id' => $order->store_id, 'type' => 1]); $data = [ 'appid' => $wechat_config['sp_appid'], 'mch_id' => $wechat_config['sp_mch_id'], 'sub_mch_id' => $store_wechat->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, ]; // 是否走分账 if (is_profit_sharing()) { $data['profit_sharing'] = 'Y'; } $data['sign'] = self::makeSign($data,$wechat_config['sp_key']); // 请求微信的支付 $data = DataTransform::arrayToXml($data); $result = cloud_post('https://api.mch.weixin.qq.com/pay/micropay',$data); $result = DataTransform::xmlToArray($result); return $result; } // 查询扫码付款状态 public static function orderQuery($order){ // 服务商参数 $wechat_config = Option::getSaasWechat(); // 特约商户参数 $store_wechat = WechatConfig::findOne(['store_id' => $order->store_id, 'type' => 1]); $data = [ 'appid' => $wechat_config['sp_appid'], 'mch_id' => $wechat_config['sp_mch_id'], 'sub_mch_id' => $store_wechat->mch_id, 'out_trade_no' => $order->order_no, 'nonce_str' => self::makeNonceStr(12), ]; $data['sign'] = self::makeSign($data,$wechat_config['sp_key']); // 请求微信的支付 $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,$pay_key){ // 去空 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='.$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; } }