有这样的场景,用户购买商品。但是退款的时候要去除运费。
或者,用户购买多个商家的商品,统一支付。但是用户只退其中一个商家的商品。 又或者,用户一个订单购买多个商品。只退其中一个商品。微信退款接口可以满足这些需求,前提是总的退款金额不能超过总的支付金额。
支付的时候,不需要证书。 但是退款的时候,需要提供证书。下面看看实际的情况。
从上图可以看出,我支付2分钱,可以一次性退。
也可以,一次退1分,分两次退还。
而且,用银行卡就退到银行卡,用信用卡就退到信用卡。
这些支付,退款相关的信息,可以在管理后台中统一管理。或者写到配置文件中。
退款需要证书,需要到微信商户平台进行下载。(开放平台【app】,公众平台【微信,pc,小程序】)分别有一个商户号。
退款的时候,需要提供支付的订单号或者微信提供的transaction_id。每一个退款都有自己的退款号(确保不要相同)。
一笔退款失败后重新提交,请不要更换退款单号,请使用原商户退款单号。
private function refundWx($data) { Clog::setLog($data); vendor('Wxpay.WxPay'); $input = new WxPayRefund(); $input->SetOut_trade_no($data['out_trade_no']); $input->SetAppid($data['app_id']); $input->SetMch_id($data['mch_id']); $input->SetOut_refund_no($data['out_trade_no'] . 'R'); $input->SetTotal_fee($data['price']); $input->SetRefund_fee($data['price']); $input->SetOp_user_id($data['mch_id']); $input->key = $data['key']; $path = C('WX_KEY_PATH'); switch ($data['type']) { case 1: $path = $path . 'open/'; break; case 2: $path = $path . 'public/'; break; } $response = WxPayApi::refund($input, $path); if ($response['return_code'] == 'SUCCESS' && $response['result_code'] == 'SUCCESS') { return true; } else { return false; }}
/** * * 申请退款,WxPayRefund中out_trade_no、transaction_id至少填一个且 * out_refund_no、total_fee、refund_fee、op_user_id为必填参数 * appid、mchid、spbill_create_ip、nonce_str不需要填入 * @param WxPayRefund $inputObj * @param int $timeOut * @throws WxPayException * @return 成功时返回,其他抛异常 */ public static function refund($inputObj, $path, $timeOut = 6) { $url = "https://api.mch.weixin.qq.com/secapi/pay/refund"; //检测必填参数 if(!$inputObj->IsOut_trade_noSet() && !$inputObj->IsTransaction_idSet()) { throw new WxPayException("退款申请接口中,out_trade_no、transaction_id至少填一个!"); }else if(!$inputObj->IsOut_refund_noSet()){ throw new WxPayException("退款申请接口中,缺少必填参数out_refund_no!"); }else if(!$inputObj->IsTotal_feeSet()){ throw new WxPayException("退款申请接口中,缺少必填参数total_fee!"); }else if(!$inputObj->IsRefund_feeSet()){ throw new WxPayException("退款申请接口中,缺少必填参数refund_fee!"); }else if(!$inputObj->IsOp_user_idSet()){ throw new WxPayException("退款申请接口中,缺少必填参数op_user_id!"); } $inputObj->SetNonce_str(self::getNonceStr());//随机字符串 $inputObj->SetSign();//签名 $xml = $inputObj->ToXml(); $startTimeStamp = self::getMillisecond();//请求开始时间 $response = self::postXmlCurl($xml, $url, true, $timeOut, $path); $result = WxPayResults::Init($response); self::reportCostTime($url, $startTimeStamp, $result);//上报请求花费时间 return $result;}
/** * 以post方式提交xml到对应的接口url * * @param string $xml 需要post的xml数据 * @param string $url url * @param bool $useCert 是否需要证书,默认不需要 * @param int $second url执行超时时间,默认30s * @throws WxPayException */ private static function postXmlCurl($xml, $url, $useCert = false, $second = 30, $certPath = '') { $ch = curl_init(); //设置超时 curl_setopt($ch, CURLOPT_TIMEOUT, $second); //如果有配置代理这里就设置代理 if(WxPayConfig::CURL_PROXY_HOST != "0.0.0.0" && WxPayConfig::CURL_PROXY_PORT != 0){ curl_setopt($ch,CURLOPT_PROXY, WxPayConfig::CURL_PROXY_HOST); curl_setopt($ch,CURLOPT_PROXYPORT, WxPayConfig::CURL_PROXY_PORT); } curl_setopt($ch,CURLOPT_URL, $url); curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE); curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验 //设置header curl_setopt($ch, CURLOPT_HEADER, FALSE); //要求结果为字符串且输出到屏幕上 curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); if($useCert == true){ //设置证书 //使用证书:cert 与 key 分别属于两个.pem文件 curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM'); curl_setopt($ch,CURLOPT_SSLCERT, $certPath . 'apiclient_cert.pem'); curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM'); curl_setopt($ch,CURLOPT_SSLKEY, $certPath . 'apiclient_key.pem'); } //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"); }}
具体的参考:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4