config = $config; } /** * 支付接口 * @param array $order * [ * 'title' => '订单标题' * 'order_no' => '交易流水号' * 'amount' => 金额 * 'mall_id' => 商城ID * 'notify_url' => '回调地址', * 'type' => '汇聚支付方式' * 'open_id' => '微信openid' * ] * @return array|mixed * @throws Exception */ public function pay($order) { $request = $this->getRequest($order); \Yii::error(' joinpay request params => '.json_encode($request)); $result = RequestUtil::http_post($this->url, $request); \Yii::error(' joinpay response data => ' . $result);\Yii::getLogger()->flush(true); $result = json_decode($result, true); if(isset($order['type'])&&$order['type']==JoinPayTypeEnum::WEIXIN_CJXCX){ if ($result['ra_Code'] == 100 ) { return $result; } else { return ['return_code'=>'FAIL','return_msg'=>$result['rb_CodeMsg']]; } } if ($result['ra_Code'] == 100 && !empty($result['rc_Result'])) { if (StringHelper::isJson($result['rc_Result'])) { $return = json_decode($result['rc_Result'], true); if (!isset($return['timestamp'])) { $return['timestamp'] = $return['timeStamp'] ?? ''; } return $return; } return ['result' => $result['rc_Result'], 'rd_Pic' => $result['rd_Pic']]; } else { return ['return_code'=>'FAIL','return_msg'=>$result['rb_CodeMsg']]; } } private function getRequest($order) { $request = [ 'p0_Version' => '1.0', //版本号 'p1_MerchantNo' => $this->config['merchant_no'], //商户编号 'p2_OrderNo' => $order['order_no'], 'p3_Amount' => sprintf("%.2f", $order['amount']), 'p4_Cur' => 1, //人民币 'p5_ProductName' => str_replace('+', '', trim($order['title'])), 'p6_ProductDesc' => str_replace('+', '', trim($order['title'])), 'p7_Mp' => json_encode(['mall_id' => $order['mall_id']]),// 公用回传参数 'p8_ReturnUrl' => '', //商户页面通知地址 'p9_NotifyUrl' => $order['notify_url'], // 服务器异步通知地址 'q1_FrpCode' => $order['type'], //支付宝扫码,扫码方式 'q2_MerchantBankCode' => '', //银行商户编码 'q3_SubMerchantNo' => '', //子商户号 'q4_IsShowPic' => '', //是否展示图片 'q5_OpenId' => '', //微信 Openid,微信支付必填 'q6_AuthCode' => '', //付款码数字 'q7_AppId' => '', //APPID 'q8_TerminalNo' => '', //终端号 'q9_TransactionModel' => '', //微信 H5 模式:空,MODEL1,MODEL2 // 'qa_TradeMerchantNo' => $this->config['trade_merchant_no'], //报备商户号 ]; if (in_array($order['type'], ['WEIXIN_GZH', 'WEIXIN_XCX', 'WEIXIN_APP3',JoinPayTypeEnum::WEIXIN_CJXCX])) { //微信环境获取对应的openid $request['q5_OpenId'] = $order['open_id'] ?? ''; $appid = ''; $trade_merchant_no = ''; switch ($order['type']) { case 'WEIXIN_GZH': $appid = $this->config['wechat_gzh_appid'] ?? ''; $trade_merchant_no = $this->config['wechat_gzh_trade_merchant_no'] ?? ''; break; case 'WEIXIN_XCX': $appid = $this->config['wechat_xcx_appid'] ?? ''; $trade_merchant_no = $this->config['wechat_xcx_trade_merchant_no'] ?? ''; break; case JoinPayTypeEnum::WEIXIN_CJXCX: $appid = ''; $request['q5_OpenId'] = ''; $trade_merchant_no = $this->config['wechat_xcx_trade_merchant_no'] ?? ''; break; case 'WEIXIN_APP3': $appid = $this->config['wechat_xcx_appid'] ?? ''; $trade_merchant_no = $this->config['wechat_app3_trade_merchant_no'] ?? ''; break; case 'WEIXIN_H5': $appid = $this->config['wechat_gzh_appid'] ?? ''; $trade_merchant_no = $this->config['wechat_gzh_trade_merchant_no'] ?? ''; break; } } elseif (in_array($order['type'], ['ALIPAY_H5'])) { $appid = ''; $trade_merchant_no = $this->config['alipay_trade_merchant_no'] ?? ''; } elseif (in_array($order['type'], [JoinPayTypeEnum::UNIONPAY_APP, JoinPayTypeEnum::UNIONPAY_H5])) { $appid = ''; $trade_merchant_no = $this->config['unionpay_trade_merchant_no'] ?? ''; } $request['q7_AppId'] = $appid; $request['qa_TradeMerchantNo'] = $trade_merchant_no; // dd($this->config, $request); $secret = $this->config['merchant_secret']; $request['hmac'] = urlencode(RequestUtil::hmacRequest($request, $secret)); return $request; } /** * 查询订单支付状态 * @params string $trade_no * @return array * @throws */ public function query($trade_no) { $request = [ 'p0_Version' => 2.5, 'p1_MerchantNo' => $this->config['merchant_no'], 'p2_OrderNo' => $trade_no, ]; $secret = $this->config['merchant_secret']; $request['hmac'] = urlencode(RequestUtil::hmacRequest($request, $secret)); $result = RequestUtil::http_post($this->order_query_url, $request); return json_decode($result, true); } /** * 退款 * @param array $info * [ * 'refund_order_no' => $refund_order_no, * 'out_refund_no' => $out_trade_no, * 'refund_amount' => 1, * 'refund_reason' => 1 * ] * * @return bool * @throws */ public function refund($info) { try { //异步回调地址 // $url = Url::toApi(['notify/joinpay-refund'], TRUE); $request = [ 'p1_MerchantNo' => $this->config['merchant_no'], //商户编号 'p2_OrderNo' => $info['out_refund_no'], 'p3_RefundOrderNo' => $info['refund_order_no'], 'p4_RefundAmount' => $info['refund_amount'], 'p5_RefundReason' => $info['refund_reason'], 'p6_NotifyUrl' => \Yii::getAlias('@apiUrl').'/notify/joinpay-refund', 'q1_version' => '2.1' ]; //商户密钥: $secret = $this->config['merchant_secret']; $request['hmac'] = urlencode(RequestUtil::hmacRequest($request, $secret)); $result = RequestUtil::http_post($this->refund_url, $request); \Yii::error('joinpay refund res:' . $result); $result = json_decode($result, true); if ($result['ra_Status'] != 100 || $result['rb_Code'] != 100) { //失败 throw new Exception($result['rc_CodeMsg']); } return true; } catch (Exception $e) { $this->setError($e->getMessage()); Yii::error('joinpay refund fail,msg:' . $e->getMessage()); return false; } } /** * 验证回调签名 * @param $params * @return bool */ private function checkHmac($params) { //验证签名 $hmac = $params['hmac']; unset($params['hmac']); unset($params['r']); $hmacVal = urlencode(RequestUtil::hmacRequest($params, $this->config['merchant_secret'])); if ($hmacVal !== $hmac) { \Yii::error('签名不合法'); return false; } return true; } /** * 异步回调 * @return array * @throws */ public function notify() { $params = array_merge(Yii::$app->request->post(), Yii::$app->request->get()); $params = array_map('urldecode', $params); //验证签名 if (!$this->checkHmac($params)) { throw new Exception('签名不合法'); } if (intval($params['r6_Status']) !== 100) { //100:支付成功,101:支付失败 throw new Exception('支付状态有误'); } if ($params['r1_MerchantNo'] !== $this->config['merchant_no']) { //商户编号不一致 throw new Exception('商户编号不一致'); } $trade = PaymentTrade::findOne(['out_trade_no' => $params['r2_OrderNo']]); if (!$trade) { throw new Exception('订单不存在'); } $pay_fee = (doubleval($params['r3_Amount']) * 100) . ''; //订单号查询对应订单 $order_amount = (doubleval($trade->pay_fee) * 100) . ''; if (intval($pay_fee) !== intval($order_amount)) { throw new Exception('支付金额与订单金额不一致'); } //支付回调验证成功 $trade_type = $params['rc_BankCode'] ?? ''; return [ 'out_trade_no' => $trade->out_trade_no, 'trade_no' => $params['r7_TrxNo'], 'pay_fee' => $params['r3_Amount'], 'trade_type' => $trade_type, ]; } /** * 退款回调处理 * @param $res * @return bool * @throws */ public function refund_notify($res) { //验证签名 if (!$this->checkHmac($res)) { return false; } //r2_OrderNo : 退款流水号 //r3_RefundOrderNo 退款订单号 return true; } }