PHP 实现微信支付 “请求单次分账”

  • Post author:
  • Post category:php



目录


一、小总结


二、服务层


一、小总结


分账

1:调用分账的订单必须是请求下单中以下参数等于  Y 的订单并且在下单接口中该参数也等于 Y 才能使用分账(可以参考一下

PHP JSAPI调起微信支付API_一本曾经的博客-CSDN博客

"profit_sharing" => "Y"   // 是否是分账订单

2:特别需要注意的是

如果是支付成功后立马调用分账的话需要 sleep(60) :等待60秒(这个大家在测试时可以缩短,比如30秒试一下看能成功调用不)

如果支付成功后没有等待直接调用会失败:提示订单正在处理中

二、服务层

// 分账
public function profitsharing()
	{
        // 分账金额                                                                                                                                                             
		$nMoney      = 1;

		// 添加分账接收方
		$url = 'https://api.mch.weixin.qq.com/pay/profitsharingaddreceiver';
        // 接收方信息
		$receiver = [
             // 分账接收方类型
			"type"          => "PERSONAL_OPENID",
            // 分账接收方账号 (个人:openid)               
			"account"       => "个人:openid", 
            // 关系  
			"relation_type" => 'SUPPLIER',                       
		];
        // 公共参数
		$oValues = [
			"mch_id"    => "商户号",
			"appid"     => "公众账号ID",
			"nonce_str" => $this->getNonceStr(),  // 随机字符串
			"receiver"  => json_encode($receiver),// 接收方信息
		];
        // 产生签名
		ksort($oValues);
		$oValues["sign"] = $this->MakeSign($oValues); 		
        // 参数转ml
		$postXML = $this->ToXml($oValues);

		// 请求
		$timeOut  = 6;
		$response = self::postXmlCurl($postXML, $url, true, $timeOut);
        // 结果转数组
		$ret      = $this->FromXml($response);
		if ($ret["return_code"] == "SUCCESS" && $ret["result_code"] == "SUCCESS") {

            // 添加分账接收方成功

			// 请求单次分账
			$receivers   = [
				"type"        => "PERSONAL_OPENID",    // 分账接收方类型       
				"account"     => "个人:openid",        // 分账接收方账号 
				"amount"      => $nMoney * 100,        // 分账金额 分
				"description" => "分到个人"             // 分账描述                  
			];

			$aData          = [
				"mch_id"         => "商户号",
			    "appid"          => "公众账号ID",
				"nonce_str"      => $this->getNonceStr(),       // 随机字符串
				"sign_type"      => "跟下单类型保持一致",         // 签名类型              
				"transaction_id" => $transaction_id,            // 微信订单号:根据你的业务场景来获取
				"out_order_no"   => "自定义",                    // 商户分账单号
				"receivers"      => json_encode($receivers, 256)// 分账接收方列表 json格式 中文不转码   
			];
            // 签名
			ksort($aData);  													  
			$aData['sign']  = $this->MakeSign($aData);                            
            // 请求单次分账
			$url     = 'https://api.mch.weixin.qq.com/secapi/pay/profitsharing';  
			// 参数转ml
		    $postXML = $this->ToXml($oValues);

			// 结果
			$timeOut  = 6;
			$response = self::postXmlCurl($postXML, $url, true, $timeOut);
            // 结果转数组
			$ret      = $this->FromXml($response);
			// 保存分账订单状态或者别的操作

			if ($ret["return_code"] == "SUCCESS" && $ret["result_code"] == "SUCCESS")                       
            {
                dump("分账成功")

				// 保存分账订单状态或者别的操作

			} else {
                dump("分账失败")
			}
		} else {
			dump("添加分账接收方失败")
		}
	}




	// 查询分账结果
	public function profitsharingquery()
	{
		$url = 'https://api.mch.weixin.qq.com/pay/profitsharingquery';
		$aWxpayParam = $this->get_wxpay_param();
		$aData          = [
			"mch_id"         => "商户号",
			"transaction_id" => $transaction_id,      // 微信订单号:根据你的业务场景来获取     
		    "out_order_no"   => "请求分账中自定义的单号",// 商户分账单号
			"nonce_str"      => $this->getNonceStr(), // 随机字符串
			"sign_type"      => "跟下单类型保持一致",   // 签名类型
		];
        // 签名
		ksort($aData);  													  
		$aData['sign']  = $this->MakeSign($aData);          
		$postXML = $this->ToXml($aData);

		// 结果
		$timeOut  = 6;
		$response = self::postXmlCurl($postXML, $url, true, $timeOut);
		$ret      = $this->FromXml($response);
		return json($ret);
	}
<-------------------------------------------------------------------------->

	/**
	 * 
	 * 产生随机字符串,不长于32位
	 * @param int $length
	 * @return 产生的随机字符串
	 */
	public static function getNonceStr($length = 32)
	{
		$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
		$str = "";
		for ($i = 0; $i < $length; $i++) {
			$str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
		}

		return $str;
	}

	/**
	 * 输出xml字符
	 **/
	public function ToXml($values)
	{
		if (!is_array($values) || count($values) <= 0) {
			return "数组数据异常!";
		}

		$xml = "<xml>";

		foreach ($values as $key => $val) {
			if (is_numeric($val)) {
				$xml .= "<" . $key . ">" . $val . "</" . $key . ">";
			} else {
				$xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
			}
		}
		$xml .= "</xml>";

		return $xml;
	}

	/**
	 * 将xml转为array
	 * @param string $xml
	 * @throws WxPayException
	 */
	public function FromXml($xml)
	{
		if (!$xml) {
			return "xml数据异常!";
		}
		//将XML转为array
		//禁止引用外部xml实体
		libxml_disable_entity_loader(true);
		$res = json_decode(json_encode(simplexml_load_string($xml,             'SimpleXMLElement', LIBXML_NOCDATA)), true);
		return $res;
	}

	/**
	 * 生成签名
	 * @param bool $needSignType  是否需要补signtype
	 * @return 签名,本函数不覆盖sign成员变量,如要设置签名需要调用SetSign方法赋值
	 */
	public function MakeSign($values, $needSignType = true)
	{
		if ($needSignType) {
			// $this->SetSignType($config->GetSignType());
			$sSignType = self::SIGNTYPE;
		}

		$sKey = $this->get_wxpay_param()['key'];

		// 签名步骤一:按字典序排序参数
		ksort($values);
		$string = $this->ToUrlParams($values);

		// 签名步骤二:在string后加入KEY
		$string = $string . "&key=" . $sKey;

		// 签名步骤三:MD5加密或者HMAC-SHA256
		if ($sSignType == "MD5") {
			$string = md5($string);
		} else if ($sSignType == "HMAC-SHA256") {
			$string = hash_hmac("sha256", $string, $sKey);
		} else {
			return "签名类型不支持!";
		}

		// 签名步骤四:所有字符转为大写
		$result = strtoupper($string);

		return $result;
	}

	/**
	 * 以post方式提交xml到对应的接口url
	 * 
	 * @param WxPayConfigInterface $config  配置对象
	 * @param string 	$xml  		需要post的xml数据
	 * @param string 	$url  		url
	 * @param bool 		$useCert 	是否需要证书,默认不需要
	 * @param int 		$second   	url执行超时时间,默认30s
	 */
	private function postXmlCurl($xml, $url, $useCert = false, $second = 30)
	{
 
		$ch 			= curl_init();
		$curlVersion 	= curl_version();
		$ua 			= "WXPaySDK/" . self::VERSION . " (" . PHP_OS . ") PHP/" . PHP_VERSION . " CURL/" . $curlVersion['version'] . " " . $aWxpayParam['mchid'];
 
		//设置超时
		curl_setopt($ch, CURLOPT_TIMEOUT, $second);
 
		$proxyHost = "0.0.0.0";
		$proxyPort = 0;
 
		// 如果有配置代理这里就设置代理
		if ($proxyHost != "0.0.0.0" && $proxyPort != 0) {
			curl_setopt($ch, CURLOPT_PROXY, $proxyHost);
			curl_setopt($ch, CURLOPT_PROXYPORT, $proxyPort);
		}
 
		curl_setopt($ch, CURLOPT_URL, $url);
		// curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);
		// curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); //严格校验
		curl_setopt($ch, CURLOPT_USERAGENT, $ua);
		// 设置header
		curl_setopt($ch, CURLOPT_HEADER, FALSE);
		// 要求结果为字符串且输出到屏幕上
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
 
		if ($useCert == true) {
			// 设置证书
			// 使用证书:cert 与 key 分别属于两个.pem文件
			// 证书文件请放入服务器的非web目录下
			$sslCertPath 	= 'sslCertPath';        // 证书路径
			$sslKeyPath 	= 'sslKeyPath';         // 证书路径
			curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
			curl_setopt($ch, CURLOPT_SSLCERT, $sslCertPath);
			curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
			curl_setopt($ch, CURLOPT_SSLKEY, $sslKeyPath);
		}
 
		// post提交方式
		curl_setopt($ch, CURLOPT_POST, TRUE);
		curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
 
		// 运行curl
		$data = curl_exec($ch);
 
		// 返回结果
		if ($data) {
			curl_close($ch);
			return $data;
		} else {
			$error = curl_errno($ch);
			curl_close($ch);
			throw new WxPayException("curl出错,错误码:$error");
		}
	}



版权声明:本文为arlene12345原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。