_key = $key; } public static function instance($key) { if (self::$self == null) { self::$self = new self($key); } return self::$self; } /** * 请求参数校验的key的顺序 */ const MAC_KEY_REQUEST_SORT = [ 'accountId', 'orderId', 'commodity', 'amount', 'responseUrl', 'subject', 'returnPage', 'openId', 'version', 'limitPay', ]; /** * 响应返回值校验的key的顺序 */ const MAC_KEY_RESPONSE_SORT = [ 'result_code', 'result_msg', 'appId', 'timeStamp', 'nonceStr', 'mPackage', 'signType', 'paySign', 'orderId', 'transId', ]; /** * 支付回调参数校验的key的顺序 */ const MAC_KEY_CALLBACK_SORT = [ 'accountId', 'orderId', 'amount', 'result_code', 'result_msg', 'channel_refund_trade_no', ]; /** * 构造银生宝下单mac参数 * @param $params * @return string */ public function makeMacParams(array $params) { $aResult = []; foreach (self::MAC_KEY_REQUEST_SORT as $key) { if (!empty($params[$key])) { $aResult[] = sprintf("%s=%s", $key, $params[$key]); } } $aResult[] = 'key=' . $this->_key; $strMac = implode('&', $aResult); LogService::info('strMac' . $strMac); $md5StrMac = md5($strMac); $md5StrMac = strtoupper($md5StrMac); return $md5StrMac; } /** * 构造银生宝回调mac参数 * @param array $params * @return string */ public function verifyCallbackMacParams(array $params) { if (empty($params['mac'])) { LogService::error('mac参数不存在,银生宝下单返回值校验失败!'); return false; } $aResult = []; foreach (self::MAC_KEY_CALLBACK_SORT as $key) { if (!empty($params[$key])) { $aResult[] = sprintf("%s=%s", $key, $params[$key]); } else { $aResult[] = $key . '='; } } $aResult[] = 'key=' . $this->_key; $strMac = implode('&', $aResult); $md5StrMac = md5($strMac); $md5StrMac = strtoupper($md5StrMac); if ($md5StrMac != $params['mac']) { LogService::error('mac值有误,银生宝回调返回值校验失败!'); return false; } else { return true; } } /** * 校验银生宝接口返回值mac * @param array $data * @return bool */ private function verifyResponse(array $data) { if (empty($data['mac'])) { LogService::error('mac参数不存在,银生宝下单返回值校验失败!'); return false; } $aResult = []; foreach (self::MAC_KEY_RESPONSE_SORT as $key) { if (!empty($data[$key])) { $aResult[] = sprintf("%s=%s", $key, $data[$key]); } else { $aResult[] = $key . '='; } } $aResult[] = 'key=' . $this->_key; $strMac = implode('&', $aResult); $md5StrMac = md5($strMac); $md5StrMac = strtoupper($md5StrMac); if ($data['mac'] != $md5StrMac) { LogService::error('mac值有误,银生宝下单返回值校验失败!'); return false; } else { return true; } } public static function makeJsJsonParam($params) { $result = json_encode([ 'appId' => $params['appId'], 'timeStamp' => $params['timeStamp'], 'nonceStr' => $params['nonceStr'], 'package' => $params['mPackage'], 'signType' => $params['signType'], 'paySign' => $params['paySign'], ]); return $result; } /** * 银生宝调用API接口 * @param $data 发送到API的数据 * @param $payInfo 支付信息 * @param $goodsInfo 商品信息 * @param $orderInfo 订单信息 * @param int $loopIndex 循环调用API的次数 * @return mixed|\Psr\Http\Message\ResponseInterface * @throws Exception */ public function payUnsApi($data, $payInfo, $goodsInfo, $orderInfo, $loopIndex = 1) { $payUrl = ApiConstants::UNS_PAY_URL; $unsApiStartTime = microtime(true); try { $client = new Http([ 'connect_timeout' => 10, 'timeout' => 30, 'http_errors' => true, //抛出异常 true是 false否 'verify' => false, //不验证ssl证书 ]); $result = $client->post($payUrl, ['form_params' => $data]); $unsApiEndTime = microtime(true); $apiRunTime = round($unsApiEndTime - $unsApiStartTime, 3); if ($result->getStatusCode() != '200') { LogService::error(sprintf('unspay_create_order_fail!wxpay_id:%s,wxpay_name:%s,mch_id:%s,channel_id:%s,user_id:%s,money:%s,good_id:%s,out_trade_no:%s,api_run_time:%s s,errmessage:%s', $payInfo['id'], $payInfo['name'], $payInfo['quartet_merchant_id'], $orderInfo['admin_id'], $orderInfo['user_id'], $goodsInfo['money'], $orderInfo['goods_id'], $orderInfo['out_trade_no'], $apiRunTime, json_encode(var_export($result, true), JSON_UNESCAPED_UNICODE))); LogService::error('银生宝平台订单创建失败!订单内容: ' . json_encode(var_export($data, true), JSON_UNESCAPED_UNICODE) . " 银生宝平台返回内容: " . json_encode(var_export($result, true), JSON_UNESCAPED_UNICODE)); throw new Exception('订单创建失败,请重新下单!'); } else { $result = json_decode($result->getBody()->getContents(), true); $verifyResponseResult = $this->verifyResponse($result); if ($result['result_code'] != '0000' || !$verifyResponseResult) { LogService::error(sprintf('unspay_create_order_fail!wxpay_id:%s,wxpay_name:%s,mch_id:%s,channel_id:%s,user_id:%s,money:%s,good_id:%s,out_trade_no:%s,api_run_time:%s s,errmessage:%s', $payInfo['id'], $payInfo['name'], $payInfo['quartet_merchant_id'], $orderInfo['admin_id'], $orderInfo['user_id'], $goodsInfo['money'], $orderInfo['goods_id'], $orderInfo['out_trade_no'], $apiRunTime, $result['result_msg'])); LogService::error('银生宝平台订单创建失败!订单内容:' . json_encode(var_export($data, true)) . " 银生宝平台返回错误码: " . $result['result_code']); throw new Exception('订单创建失败,请重新下单!'); } } LogService::info(sprintf('unspay_create_order_success!wxpay_id:%s,wxpay_name:%s,mch_id:%s,channel_id:%s,user_id:%s,money:%s,good_id:%s,out_trade_no:%s,api_run_time:%s s', $payInfo['id'], $payInfo['name'], $payInfo['quartet_merchant_id'], $orderInfo['admin_id'], $orderInfo['user_id'], $goodsInfo['money'], $orderInfo['goods_id'], $orderInfo['out_trade_no'], $apiRunTime)); return $result; } catch (GuzzleException $exception) { $unsApiEndTime = microtime(true); $apiRunTime = round($unsApiEndTime - $unsApiStartTime, 3); LogService::error(sprintf('unspay_create_order_fail!wxpay_id:%s,wxpay_name:%s,mch_id:%s,channel_id:%s,user_id:%s,money:%s,good_id:%s,out_trade_no:%s,api_run_time:%s s,errmessage:%s', $payInfo['id'], $payInfo['name'], $payInfo['quartet_merchant_id'], $orderInfo['admin_id'], $orderInfo['user_id'], $goodsInfo['money'], $orderInfo['goods_id'], $orderInfo['out_trade_no'], $apiRunTime, $exception->getMessage())); LogService::error('银生宝平台订单创建失败!订单内容: ' . json_encode(var_export($data, true), JSON_UNESCAPED_UNICODE)); LogService::error($exception->getMessage()); LogService::error($exception->getTraceAsString()); if ($loopIndex <= ApiConstants::LOOP_EXEC_API_COUNT) { $loopIndex++; return $this->payUnsApi($data, $payInfo, $goodsInfo, $orderInfo, $loopIndex); } throw new Exception('订单创建失败,请重新下单!'); } } }