appid = $appid; $this->mch_id = $mch_id; $this->partnerKey = $partnerKey; } /** * 统一下单接口 */ public function unifiedorder($orderNo, $total_fee, $ip, $source, $shopName, $openid = '', $attach = '') { $url = $this->apiUrl.'unifiedorder'; switch ($source){ case 'byteDanceH5': $trade_type = 'MWEB'; break; case 'H5': $trade_type = 'MWEB'; break; case 'APP': $trade_type = 'APP'; break; default: $trade_type = 'JSAPI'; break; } $params = [ 'appid' => $this->appid, // 小程序ID/微信开放平台审核通过的应用APPID 'mch_id' => $this->mch_id, // 商户号 'nonce_str' => md5(md5(time().'qianniao.vip')), // 32位随机字符串 'out_trade_no' => $orderNo, // 商户订单号 'total_fee' => yuanToFen($total_fee), // 订单总金额,单位分 'spbill_create_ip' => $ip, // 终端ip 'trade_type' => $trade_type, // 交易类型 'notify_url' => $this->notifyUrl, // 通知地址 'body' => $shopName.'-'.'线上商城', 'attach' => $attach, // 附加数据 ]; if($trade_type == 'JSAPI'){ $params['openid'] = $openid; } $params['sign'] = self::getSign($params); $post_xml = arrayToWeiXinXml($params); $curl_content = request($url, $post_xml); $result = $this->commonResult($curl_content); if(!$result->isSuccess()){ return ResultWrapper::fail($result->getData(), $result->getErrorCode()); } $response_content = $result->getData(); //获取统一下单返回的id $prepay_id = $response_content['prepay_id']; // H5支付返回的支付跳转链接 $mweb_url = isset($response_content['mweb_url']) ? $response_content['mweb_url'] : ''; switch ($source){ case 'byteDanceH5': $payParams = $mweb_url; // 字节跳动小程序调用微信H5支付获取支付链接 break; case 'H5': $payParams = self::payment($prepay_id); break; case 'APP': $payParams = self::payment($prepay_id); break; default: $payParams = self::paymentToMinprogram($prepay_id); break; } return ResultWrapper::success($payParams); } /** * 小程序调起支付用到的参数 * 官方接口地址: https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=3 */ public function paymentToMinprogram($prepay_id) { unset($params); $params = [ 'appId' => $this->appid, // 小程序ID 'timeStamp' => (string)time(), //时间戳 'nonceStr' => md5(md5(time().'qianniao.vip')), //32位随机字符串 'package' => 'prepay_id='.$prepay_id, // 数据包 'signType' => 'MD5', // 签名方式 ]; $params['paySign'] = self::getSign($params); return $params; } /** * 调起支付用到的参数 * 官方接口地址: https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_12 * @param $prepay_id */ public function payment($prepay_id) { unset($parm); $parm = array( 'appid' => $this->appid, 'partnerid' => $this->mch_id, 'timestamp' => (string)time(), //时间戳 'noncestr' => md5(md5(time().'qianniao.vip')), //32位随机字符串 'prepayid' => $prepay_id, //统一下单接口返回的prepay_id 参数值 'package' => 'Sign=WXPay', ); $parm['sign'] = self::getSign($parm); return $parm; } /** * 付款码支付 * 官方文档地址: https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_10&index=1 */ public function micropay($orderNo, $total_fee, $ip, $shopName, $auth_code) { $url = $this->apiUrl.'micropay'; $params = [ 'appid' => $this->appid, // 小程序ID/微信开放平台审核通过的应用APPID 'mch_id' => $this->mch_id, // 商户号 'nonce_str' => md5(md5(time().'qianniao.vip')), // 32位随机字符串 'body' => $shopName.'-'.'线上商城', 'out_trade_no' => $orderNo, // 商户订单号 'total_fee' => yuanToFen($total_fee), // 订单总金额,单位分 'spbill_create_ip' => $ip, // 终端ip 'auth_code' => $auth_code, ]; $params['sign'] = self::getSign($params); $post_xml = arrayToWeiXinXml($params); $curl_content = request($url, $post_xml); if($curl_content['httpcode'] != '200'){ return ResultWrapper::fail($curl_content['errorMsg'], ErrorCode::$apiNotResult); } $response_content = (array)simplexml_load_string($curl_content['content'], 'SimpleXMLElement', LIBXML_NOCDATA); if($response_content['return_code'] != 'SUCCESS'){ return ResultWrapper::fail($response_content['return_msg'], ErrorCode::$weixinPayError); } if($response_content['result_code'] != 'SUCCESS' && $response_content['err_code'] != 'USERPAYING' ){ return ResultWrapper::fail($response_content['err_code_des'], ErrorCode::$weixinPayError); } // 20s内查询支付状态,如果有明确错误直接返回错误,其他情况继续查询。如果成功直接返回成功 for($i=1; $i<=10; $i++) { $url = $this->apiUrl.'orderquery'; $params = [ 'appid' => $this->appid, // 小程序ID/微信开放平台审核通过的应用APPID 'mch_id' => $this->mch_id, // 商户号 'nonce_str' => md5(md5(time().'qianniao.vip')), // 32位随机字符串 'out_trade_no' => $orderNo, // 商户订单号 ]; $params['sign'] = self::getSign($params); $post_xml = arrayToWeiXinXml($params); $curl_content = request($url, $post_xml); if($curl_content['httpcode'] != '200'){ return ResultWrapper::fail($curl_content['errorMsg'], ErrorCode::$apiNotResult); } $response_content = (array)simplexml_load_string($curl_content['content'], 'SimpleXMLElement', LIBXML_NOCDATA); if($response_content['return_code'] != 'SUCCESS'){ return ResultWrapper::fail($response_content['return_msg'], ErrorCode::$weixinPayError); } if($response_content['result_code'] != 'SUCCESS' && $response_content['err_code'] == 'ORDERNOTEXIST'){ return ResultWrapper::fail($response_content['err_code_des'], ErrorCode::$weixinPayError); } if($response_content['return_code'] == 'SUCCESS' && $response_content['result_code'] == 'SUCCESS' && $response_content['trade_state'] == 'SUCCESS'){ return ResultWrapper::success($response_content['trade_state']); } sleep(2); } // 20s内没有支付则撤销订单 for($i=1; $i<=10; $i++){ $url = $this->baseUrl.'secapi/pay/reverse'; $params = [ 'appid' => $this->appid, // 小程序ID/微信开放平台审核通过的应用APPID 'mch_id' => $this->mch_id, // 商户号 'nonce_str' => md5(md5(time().'qianniao.vip')), // 32位随机字符串 'out_trade_no' => $orderNo, // 商户订单号 ]; $params['sign'] = self::getSign($params); $post_xml = arrayToWeiXinXml($params); $curl_content = request($url, $post_xml, 10, false, [], true); if($curl_content['httpcode'] != '200'){ return ResultWrapper::fail($curl_content['errorMsg'], ErrorCode::$apiNotResult); } $response_content = (array)simplexml_load_string($curl_content['content'], 'SimpleXMLElement', LIBXML_NOCDATA); if($response_content['return_code'] != 'SUCCESS'){ return ResultWrapper::fail($response_content['return_msg'], ErrorCode::$weixinPayError); } if($response_content['result_code'] != 'SUCCESS' && $response_content['recall'] == 'N'){ return ResultWrapper::fail($response_content['err_code_des'], ErrorCode::$weixinPayError); } if($response_content['return_code'] == 'SUCCESS' && $response_content['result_code'] == 'SUCCESS' && $response_content['recall'] == 'N'){ return ResultWrapper::success($response_content['result_code']); } } } /** * H5查询支付状态接口 */ public function orderquery($orderNo) { $url = $this->apiUrl.'orderquery'; $params = [ 'appid' => $this->appid, // 小程序ID/微信开放平台审核通过的应用APPID 'mch_id' => $this->mch_id, // 商户号 'nonce_str' => md5(md5(time().'qianniao.vip')), // 32位随机字符串 'out_trade_no' => $orderNo, // 商户订单号 ]; $params['sign'] = self::getSign($params); $post_xml = arrayToWeiXinXml($params); $curl_content = request($url, $post_xml); $result = $this->commonResult($curl_content); if(!$result->isSuccess()){ return ResultWrapper::fail($result->getData(), $result->getErrorCode()); } $response_content = $result->getData(); $trade_state = $response_content['trade_state']; return ResultWrapper::success($trade_state); } /** * 申请退款API * 官方文档地址:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_4 */ public function transfers($outerTradeNo, $out_refund_no, $refundMoney, $orderMoney, $sslData) { $url = $this->baseUrl.'secapi/pay/refund'; $params = [ 'appid' => $this->appid, 'mch_id' => $this->mch_id, 'nonce_str' => md5(md5(time().'qianniao.vip')), // 32位随机字符串 'transaction_id' => $outerTradeNo, // 微信交易号 'out_refund_no' => $out_refund_no, // 商户退款单号 'refund_fee' => yuanToFen($refundMoney), // 退款金额 'total_fee' => yuanToFen($orderMoney), // 订单总金额 ]; $params['sign'] = self::getSign($params); $post_xml = arrayToWeiXinXml($params); $curl_content = request($url, $post_xml, 10, false, [], true, $sslData); $result = $this->commonResult($curl_content); if(!$result->isSuccess()){ return ResultWrapper::fail($result->getData(), $result->getErrorCode()); } $response_content = $result->getData(); return ResultWrapper::success($response_content['refund_id']); } /** * 公共处理返回结果方法 */ public function commonResult($curl_content) { if($curl_content['httpcode'] == '200'){ $response_content = (array)simplexml_load_string($curl_content['content'], 'SimpleXMLElement', LIBXML_NOCDATA); if($response_content['return_code'] == 'SUCCESS'){ if($response_content['result_code'] == 'SUCCESS'){ return ResultWrapper::success($response_content); }else{ return ResultWrapper::fail($response_content['err_code_des'], ErrorCode::$weixinPayError); } }else{ return ResultWrapper::fail($response_content['return_msg'], ErrorCode::$weixinPayError); } }else{ return ResultWrapper::fail($curl_content['errorMsg'], ErrorCode::$apiNotResult); } } /** * 生成签名方法 * 官方接口地址: https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_3 * 官方签名测试地址: https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=20_1 * @param $parm * @return string */ public function getSign($parm) { //非空参数值的参数按照参数名ASCII码从小到大排序(字典序) if(ksort($parm)){ $stringA = ''; //使用URL键值对的格式拼接成字符串stringA foreach($parm as $key => $value){ if($value == 0 || !empty($value)){ $stringA .=$key.'='.$value.'&'; } } }else{ echo "对参数排序出错"; exit(); } //在stringA最后拼接上key=商户支付密钥 $stringSignTemp = $stringA.'key='.$this->partnerKey; //对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写 $signValue = strtoupper(md5($stringSignTemp)); return $signValue; } }