payType = $payType; $unifyParams = $this->applyPaymentData(); $this->unify($unifyParams); } /** * @return array * @throws BadRequestHttpException * @throws Exception * 生成支付参数 */ private function applyPaymentData() { $orderId = Yii::$app->request->getBodyParam('order_id');/*int 商品id*/ $paymentAmount = Yii::$app->request->getBodyParam('payment_amount');/*int 商品id*/ $notifyUrl = Yii::$app->request->getBodyParam('notify_url');/*int 商品id*/ if (empty($orderId) || empty($paymentAmount) || empty($notifyUrl)) { throw new BadRequestHttpException(Helper::REQUEST_BAD_PARAMS); } $this->tradeType = self::TRADE_TYPE_JS_API; $this->savePaymentLog($orderId, $paymentAmount, $notifyUrl); $params = [ 'body' => '订单支付', 'out_trade_no' => $orderId, 'total_fee' => round($paymentAmount * 100), 'openid' => Yii::$app->user->identity->wx_openid, ]; return $params; } /** * @param string $orderId * @param float $paymentAmount * @param string $notifyUrl * @throws Exception * 保存支付信息 */ private function savePaymentLog($orderId, $paymentAmount, $notifyUrl) { $paymentLog = PaymentLog::findOne(['order_id' => $this->order->order_sn]); if (!$paymentLog) { $paymentLog = new PaymentLog(); } $paymentLog->order_id = $orderId; $paymentLog->payment_amount = $paymentAmount; $paymentLog->notify_url = $notifyUrl; $paymentLog->type = $this->payType; $paymentLog->status = PaymentLog::PAYMENT_STATUS_WAITING; if (!$paymentLog->save()) { throw new Exception(Helper::errorMessageStr($paymentLog->errors)); } } private function getPaymentApp() { $this->initObject(); $config = [ 'app_id' => $this->appId, 'mch_id' => $this->mchId, 'key' => $this->key, 'cert_path' => $this->certPath, 'key_path' => $this->keyPath, 'notify_url' => $this->notifyUrl, 'trade_type' => $this->tradeType, 'sandbox' => true, // 设置为 false 或注释则关闭沙箱模式 ]; $this->app = Factory::payment($config); // 判断当前是否为沙箱模式: $this->app->inSandbox(); } /** * @var WxPayConfig $wxPayConfig */ private function initObject() { $path = Yii::getAlias('@backend'); $wxPayConfig = WxPayConfig::find()->one(); // $wxConfig = WxConfig::find()->one(); // switch ($this->payType) { // case self::PAY_TYPE_WEB: // $this->appId = trim($wxConfig->wx_appId); // break; // case self::PAY_TYPE_MINI_PROGRAM: // $this->appId = trim($wxConfig->mini_program_appId); // break; // } $this->mchId = $wxPayConfig->mch_id; $this->certPath = trim($path . $wxPayConfig->cert_path); $this->keyPath = trim($path . $wxPayConfig->key_path); $this->notifyUrl = Yii::$app->request->hostInfo . '/wx-payment/notify'; } /** * @param $unifyParams * @return mixed * 统一下单 */ private function unify($unifyParams) { $this->getPaymentApp(); return $this->app->order->unify($unifyParams); } /** * @return array|bool * @throws BadRequestHttpException * @throws \yii\base\InvalidConfigException * @throws \yii\httpclient\Exception * 微信支付回调 */ public function notify() { $notifyData = Json::decode(Json::encode(simplexml_load_string(Yii::$app->request->getRawBody(), 'SimpleXMLElement', LIBXML_NOCDATA))); Yii::info($notifyData, "notify"); if (!$this->checkSign($notifyData)) { throw new BadRequestHttpException(Helper::REQUEST_BAD_PARAMS); } $tra = Yii::$app->db->beginTransaction('SERIALIZABLE'); try { if ($notifyData->result_code != 'SUCCESS' || $notifyData->return_code != 'SUCCESS') { throw new BadRequestHttpException('result_code or return_code is false'); } $paymentLog = PaymentLog::findOne(['order_id' => $notifyData->out_trade_no]); $this->notifyUrl = Yii::$app->request->hostInfo . $paymentLog->notify_url; $paymentLog->mch_id = $notifyData->mch_id; $paymentLog->wx_refund_id = $notifyData->transaction_id; //交易号 $paymentLog->status = PaymentLog::PAYMENT_STATUS_SUCCESS; if (!$paymentLog->save()) { throw new Exception(Helper::errorMessageStr($paymentLog->errors)); } if (!$tra->commit()) { throw new Exception('保存数据失败'); } /*转发回调信息*/ $this->forwardNotify($notifyData, true); return ['return_code' => 'SUCCESS', 'return_msg' => 'OK'];//回传成功信息到微信服务器 } catch (Exception $e) { $tra->rollBack(); $this->forwardNotify($notifyData, false); Yii::info($e->getMessage(), 'notify'); return false; } catch (BadRequestHttpException $e) { Yii::info($e->getMessage(), 'notify'); return false; } } /** * @param $notifyData * @param $status * @return bool * @throws \yii\base\InvalidConfigException * @throws \yii\httpclient\Exception * 转发异步回调信息 */ private function forwardNotify($notifyData, $status) { $notify = [ 'notify' => [ 'status' => $status, 'notify' => $notifyData ] ]; $client = new Client(); $response = $client->createRequest() ->setMethod('POST') ->setUrl($this->notifyUrl) ->addHeaders(['content-type' => 'application/json']) ->setContent(Json::encode($notify)) ->send(); if ($response->isOk) { return true; } else { return false; } } /** * @param $data * @return bool * 支付成功回调验证签名和支付金额 */ public function checkSign($data) { $this->initObject(); $notifySign = $data['sign']; unset($data['sign']); $sign = $this->_sign($data); if ($notifySign == $sign) { return true; } else { return false; } } /** * @param $arr * @return string * 微信签名方法 */ private function _sign($arr) { $arr = array_filter($arr); ksort($arr); $arr['key'] = $this->key; $queryString = http_build_query($arr); $queryString = urldecode($queryString); return strtoupper(md5($queryString)); } }