client = $client ?: null; if ($options['mchType'] === 'provider') { $config = [ 'appid' => $options['provider']['appid'], 'appkey' => $options['provider']['appkey'], 'msgSrcId' => $options['provider']['msgSrcId'], 'msgSrc' => $options['provider']['msgSrc'], 'md5Secret' => $options['provider']['md5Secret'], 'mid' => $options['provider']['mid'], 'tid' => $options['provider']['tid'], ]; } else { $config = [ 'appid' => $options['normal']['appid'], 'appkey' => $options['normal']['appkey'], 'msgSrcId' => $options['normal']['msgSrcId'], 'msgSrc' => $options['normal']['msgSrc'], 'md5Secret' => $options['normal']['md5Secret'], 'mid' => $options['normal']['mid'], 'tid' => $options['normal']['tid'], ]; } $this->config = $config; return $this; } /* 统一下单API * @param string $outTradeNo 交易订单号 * @param string $totalFee 实际付款金额 * @param array $extra 附加的数据 (需要携带openid) * @return bool * @throws BaseException */ public function addReceiver(string $type, string $account, string $name, string $relation_type = "HEADQUARTER", string $custom_relation = ""): bool { return true; } /** * 统一下单API * @param string $outTradeNo 交易订单号 * @param string $totalFee 实际付款金额 * @param array $extra 附加的数据 (需要携带openid) * @return bool * @throws BaseException */ public function profitsharing(string $transaction_id, string $out_order_no, array $receivers): bool { return true; } public function profitsharingQuery(string $out_order_no, string $transaction_id): ?array{ return []; } /** * 统一下单API * @param string $outTradeNo 交易订单号 * @param string $totalFee 实际付款金额 * @param array $extra 附加的数据 (需要携带openid) * @return bool * @throws BaseException */ public function unify(string $outTradeNo, string $totalFee, array $extra = []): bool { try { $result = null; // 发起API调用 微信小程序端 if ($this->client === ClientEnum::MP_WEIXIN) { //$outTradeNo = date("YmdHis").mt_rand(1000,9999); $order_url = $this->domain . "/v1/netpay/wx/mini-pre-order"; //$order_url = $this->domain . "/v1/netpay/wx/unified-order"; $params = [ "requestTimestamp" => date("Y-m-d H:i:s"), "merOrderId" => $this->config['msgSrcId'].$outTradeNo, "mid" => $this->config['mid'], "tid" => $this->config['tid'], "subAppId" => $this->config['appid'], "subOpenId" => $extra['openid'], "tradeType" => "MINI", "notifyUrl" => $this->notifyUrl(), "orderDesc" => "商品支付", "totalAmount" => bcmul($totalFee, "100"), "divisionFlag" => false,//分账标记 //"platformAmount" => false,//平台商户分 账金额 // "subOrders" => [ // [ // "mid" => "111", // "totalAmount" => 1, // ] // ], ]; $authorization = $this->getAuthorization($params); $headers = ["Authorization:".$authorization]; $data = httpRequest($order_url, "POST", json_encode($params), $headers, false); if ($data['errCode'] != "SUCCESS") { Log::append('银联-unify', ['client' => $this->client, 'result' => $data]); return false; } $this->result = $data; return true; } } catch (\Throwable $e) { $this->throwError('银联API下单失败:' . $e->getMessage(), true, 'unify'); } return false; } /** * 交易查询 (主动查询订单支付状态) * @param string $outTradeNo 交易订单号 * @return array|null * @throws BaseException */ public function tradeQuery(string $outTradeNo): ?array { try { $tradeQuery_url = $this->domain . "/v1/netpay/wx/mini-pre-query"; $params = [ "requestTimestamp" => date("Y-m-d H:i:s"), "merOrderId" => $outTradeNo,//date("YmdHis").mt_rand(1000,9999), "mid" => $this->config['mid'], "tid" => $this->config['tid'], ]; $authorization = $this->getAuthorization($params); $headers = ["Authorization:".$authorization]; $data = httpRequest($tradeQuery_url, "POST", json_encode($params), $headers, false); // echo "
"; // print_r($data); // exit(); if ($data['errCode'] != "SUCCESS") { Log::append('huifu-tradeQuery', ['client' => $this->client, 'result' => $data]); return false; } return [ // 支付状态: true成功 false失败 'paySuccess' => isset($data['status']) && $data['status'] === 'TRADE_SUCCESS', // 第三方交易流水号 'tradeNo' => $data['targetOrderId'] ?? "" ]; return $data; } catch (\Throwable $e) { $this->throwError('银联API交易查询失败:' . $e->getMessage(), true, 'tradeQuery'); } return false; } /** * 输出错误信息 * @param string $errMessage 错误信息 * @param bool $isLog 是否记录日志 * @param string $action 当前的操作 * @throws BaseException */ private function throwError(string $errMessage, bool $isLog = false, string $action = '') { $this->error = $errMessage; $isLog && Log::append("银联-{$action}", ['errMessage' => $errMessage]); throwError($errMessage); } /** * 支付成功后的异步通知 * @return bool * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException */ public function notify(): bool { // 接收表单数据 $this->notifyParams = request()->filter([])->post(); $notifyParams = $this->notifyParams; $sign = $notifyParams['sign']; unset($notifyParams['sign']); ksort($notifyParams); $str = http_build_query($notifyParams); $verifyNotify = false; if ($sign != md5($str)) { $verifyNotify = true; } // 判断交易单状态必须是支付成功 $this->notifyResult = $verifyNotify && $this->notifyParams['status'] === 'TRADE_SUCCESS'; // 记录日志 Log::append('银联-notify', [ 'params' => $this->notifyParams, 'verifyNotify' => $verifyNotify, 'response' => $this->getNotifyResponse(), 'result' => $this->notifyResult, 'message' => '银联异步回调验证' . ($this->notifyResult ? '成功' : '失败') ]); return $this->notifyResult; } /** * 微信支付退款API * @param string $outTradeNo 第三方交易单号 * @param string $refundAmount 退款金额 * @param array $extra 附加数据 (需要携带订单付款总金额) * @return bool * @throws BaseException */ public function refund(string $outTradeNo, string $refundAmount, array $extra = []): bool { try { $refund_url = $this->domain . "/v1/netpay/refund"; $params = [ "requestTimestamp" => date("Y-m-d H:i:s"), "merOrderId" => $outTradeNo, "mid" => $this->config['mid'], "tid" => $this->config['tid'], "targetOrderId" => $extra['trade_no'], "refundAmount" => bcmul($refundAmount, "100"), "refundOrderId" => $this->config['msgSrcId'].date("YmdHis").mt_rand(1000, 9999), "refundDesc" => "订单退款", ]; $authorization = $this->getAuthorization($params); $headers = ["Authorization:".$authorization]; $data = httpRequest($refund_url, "POST", json_encode($params), $headers, false); if ($data['errCode'] != "SUCCESS") { $this->throwError($data['errMsg']); } // 请求成功 return true; } catch (\Throwable $e) { $this->throwError('银联API退款请求:' . $e->getMessage(), true, 'refund'); } return false; } /** * 商家转账到零钱API * @param string $outTradeNo 交易订单号 * @param string $totalFee 实际付款金额 * @param array $extra 附加的数据 (需要携带openid、desc) * @return bool * @throws BaseException */ public function transfers(string $outTradeNo, string $totalFee, array $extra = []): bool { if (!$this->getApp()->transfers($outTradeNo, $totalFee, $extra)) { $this->setError($this->getApp()->getError()); return false; } return true; } /** * 获取异步回调的请求参数 * @return array */ public function getNotifyParams(): array { return [ // 第三方交易流水号 'tradeNo' => $this->notifyParams['targetOrderId'] ]; } /** * 返回异步通知结果的输出内容 * @return string */ public function getNotifyResponse(): string { return $this->notifyResult ? 'SUCCESS' : 'FAILED'; } /** * 返回统一下单API的结果 (用于前端) * @return array * @throws BaseException */ public function getUnifyResult(): array { if (empty($this->result)) { $this->throwError('当前没有unify结果', true, 'getUnifyResult'); } $this->result['out_trade_no'] = $this->result['merOrderId'];//str_replace($this->config['msgSrcId'], "", $this->result['merOrderId']); return $this->result; } /** * 生成授权码 * [getAuthorization description] * @param [type] $data [description] * @return [type] [description] */ private function getAuthorization($data){ $appid = $this->config['appid']; $appkey = $this->config['appkey']; $timestamp = date("YmdHis",time()); $nonce = md5(uniqid((string)microtime(true),true)); $body = json_encode($data); //echo $body; $str = bin2hex(hash('sha256', $body, true)); $signature = base64_encode(hash_hmac('sha256', "$appid$timestamp$nonce$str", $appkey, true)); $authorization = 'OPEN-BODY-SIG AppId="'.$appid.'", Timestamp="'.$timestamp.'", Nonce="'.$nonce.'", Signature="'.$signature.'"'; return $authorization; } /** * 异步回调地址 * @return string */ private function notifyUrl(): string { // 例如:https://www.xxxx.com/alipayNotice.php return base_url() . 'huifuNotice.php'; } }