fastadmin 微信支付(V2)订单实现退款处理,支付金额原路返回
fastadmin 微信支付(V2)订单实现退款处理,支付金额原路返回
·
记录:fastadmin 微信支付(V2)订单实现退款处理,支付金额原路返回
一、在微信商户平台下载证书文件,放在addons\epay\certs 文件夹下,并在epay插件中配置证书位置信息

二、在 addons\epay\library\Service 中增加申请退款代码
/**
* 提交退款订单
* @param array|float $amount 订单金额
* @param array|float $refund_money 退款金额
* @param string $orderid 订单号
* @param string $refund_sn 退款订单号
* @param string $type 支付类型,可选alipay或wechat
* @param string $remark 退款原因
* @param string $notifyurl 通知回调URL
* @param string $returnurl 跳转返回URL
* @param string $method 支付方式
* @return Response|RedirectResponse|Collection
* @throws Exception
*/
public static function submitRefund($amount=null,$refund_money,$orderid,$refund_sn,$type,$remark = null,$notifyurl = null,$returnurl = null,$method = 'app'){
if (!is_array($amount)) {
$params = [
'amount' => $amount,
'refund_money' => $refund_money,
'orderid' => $orderid,
'refund_sn' => $refund_sn,
'type' => $type,
'remark' => $remark,
'notifyurl' => $notifyurl,
'returnurl' => $returnurl,
'method' => $method,
];
} else {
$params = $amount;
}
$amount = isset($params['amount']) ? $params['amount'] : 0;
$refund_money = isset($params['refund_money']) ? $params['refund_money'] : 0;
$orderid = isset($params['orderid']) ? $params['orderid'] : '';
$refund_sn = isset($params['refund_sn']) ? $params['refund_sn'] : '';
$type = isset($params['type']) && in_array($params['type'], ['alipay', 'wechat']) ? $params['type'] : 'wechat';
$remark = isset($params['remark']) ? $params['remark'] : '';
$request = request();
$notifyurl = isset($params['notifyurl']) ? $params['notifyurl'] : $request->root(true) . '/addons/epay/index/' . $type . 'notify';
$returnurl = isset($params['returnurl']) ? $params['returnurl'] : '';
$config = Service::getConfig($type);
$config['notify_url'] = $notifyurl;
$config['return_url'] = $returnurl;
$result = null;
//退款参数
$order_data = [
'out_trade_no' => $orderid//原订单号
];
if ($type == 'wechat') {
//创建支付对象
$pay = Pay::wechat($config);
$total_fee = $amount * 100;
$refund_fee = $refund_money * 100;
$order_data = array_merge($order_data, [
'out_refund_no' => $refund_sn,//退款订单号
'total_fee' => $total_fee,//支付金额
'refund_fee' => $refund_fee,//退款金额
'refund_desc' => $remark,//退款原因
'type' => $method //支付方式
]);
} else {
$pay = Pay::alipay($config);
$order_data = array_merge($order_data, [
'out_request_no' => $refund_sn,//退款订单号
'refund_amount' => $refund_money,
]);
}
$result = $pay->refund($order_data);
//使用重写的Response类、RedirectResponse、Collection类
if ($result instanceof \Symfony\Component\HttpFoundation\RedirectResponse) {
$result = RedirectResponse::create($result->getTargetUrl());
} elseif ($result instanceof \Symfony\Component\HttpFoundation\Response) {
$result = Response::create($result->getContent());
} elseif ($result instanceof \Yansongda\Supports\Collection) {
$result = Collection::make($result->all());
}
return $result;
}
三、调用退款接口
Db::startTrans();
...
// 调用退款接口
// 异步接收微信支付退款结果通知的回调地址,通知URL必须为外网可访问的url,不允许带参数,公网域名必须为https
$notifyurl = cdnurl('/api/paynotify/refundwxsign'.$refundRow['sign'], true);
try {
$response = \addons\epay\library\Service::submitRefund($orderRow['total_fee'], $refund_money, $refundRow['trade_no'], $refundRow['refund_order_no'], $pay_type='wechat', $refund_reason='用户退款', $notifyurl, $returnurl='', $method='miniapp');
// SUCCESS退款申请接收成功 FAIL 提交业务失败
if($response['return_code'] == 'SUCCESS' && $response['result_code'] == 'SUCCESS'){
$updataData = [
'refund_id' => $response['refund_id'],
'tuikuan_status' => '20', //真实退款状态:0=无,10=未退款,20=退款中,30=退款成功,40=退款关闭,50=退款异常
'tuikuan_time' => time(),
];
}else{
$errorMsg = $response['err_code_des'] ?? $response['return_msg'];
Db::rollback();
throw new Exception('退款申请失败:'.$errorMsg);
}
} catch (\Throwable $th) {
Db::rollback();
throw new Exception('退款申请失败,'.$th);
}
...
// 更新退款状态
$result = $refundRow->allowField(true)->save($updataData);
if($result === false){
Db::rollback();
throw new Exception("抱歉,更新取货状态失败");
}
Db::commit();
回调处理
protected $key = '';
protected $model;
public function refundwxsign10()
{
return $this->refundwx('10');
}
// 微信退款异步回调
protected function refundwx($sign)
{
//写记事本
$year = date('Y');
$month = date('m');
$day = date('d');
$route = dirname(dirname(dirname(__FILE__)));
$path = "$route/notify/$year/$month";
// 获取xml
$xml = file_get_contents('php://input', 'r');
if (!file_exists($path)) {
mkdir($path, 0777, true);
$textlog = '01:' . date('Y-m-d H:i:s') . ':微信售后退款' . $xml;
file_put_contents("$path/$day.txt", "$textlog\r\n", FILE_APPEND);
} else {
$textlog = '01:' . date('Y-m-d H:i:s') . ':微信售后退款' . $xml;
file_put_contents("$path/$day.txt", "$textlog\r\n", FILE_APPEND);
}
// 转成php数组
$dataArr = $this->toArray($xml);
$textlog = '02:' . date('Y-m-d H:i:s') . ':微信售后退款' . json_encode($dataArr);
file_put_contents("$path/$day.txt", "$textlog\r\n", FILE_APPEND);
// 1. 验证通信状态
if ($dataArr['return_code'] != 'SUCCESS') {
$textlog = '03:' . date('Y-m-d H:i:s') . ':微信售后退款-通信失败: ' . $dataArr['return_msg'];
file_put_contents("$path/$day.txt", "$textlog\r\n", FILE_APPEND);
return $this->replyXml('FAIL', '通信失败');
}
// 2.从数组中获取微信的加密数据
$req_info = $dataArr['req_info'];
$getDecryptData = $this->decrypt($req_info); //对加密数据进行解密
$data = $this->toArray($getDecryptData); //将解密的数据由xml数据转化为数据格式
$textlog = '04:' . date('Y-m-d H:i:s') . ':微信售后退款' . json_encode($data);
file_put_contents("$path/$day.txt", "$textlog\r\n", FILE_APPEND);
// 3.更新退款状态
$refundStatusMap = [
'SUCCESS' => 30, // 退款成功
'REFUNDCLOSE' => 40, // 退款关闭
'CHANGE' => 50 // 退款异常
];
$tuikuan_status = $refundStatusMap[$data['refund_status']];
$order_no = $data['out_trade_no']; // 商户订单号
$refund_no = $data['out_refund_no']; // 商户退款单号
$refund_id = $data['refund_id']; // 微信退款单号
$transaction_id = $data['transaction_id']; //微信支付订单号
$refund_fee = $data['refund_fee'] / 100;
$success_time = strtotime($data['success_time']);
if($sign == '10'){
$this->model = new Litestoreorderrefund();
}elseif($sign == '20'){
$this->model = new Litestorewmorderrefund();
}elseif($sign == '30'){
$this->model = new Litestorejzorderrefund();
}
$order = $this->model->where(['refund_order_no'=>$refund_no])->find();
if (!$order) {
return $this->replyXml('FAIL', '未查询到订单');
}
if($order['tuikuan_status'] == '30'){
return $this->replyXml('SUCCESS', 'OK');
}
// 保存退款状态
$order->save([
'refund_fee'=>$refund_fee,
'tuikuan_status'=>$tuikuan_status,
'tuikuan_time' => $success_time
]);
return $this->replyXml('SUCCESS', 'OK');
}
/**
* 将xml转为array
* @param string $xml xml字符串
* @return array 转换得到的数组
*/
private function toArray($xml)
{
//禁止引用外部xml实体
libxml_disable_entity_loader(true);
$result = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $result;
}
/**
* 对微信返回的加密回调数据进行解密并将其由xml格式转换为数组格式
* @param $req_info :微信会掉返回的加密数据
* @return false|string
*/
public function decrypt($req_info){
$APIv2 = $this->key;
$disposeAPI = strtolower(MD5($APIv2));
$disposeReq = base64_decode($req_info);
$decrypt = openssl_decrypt($disposeReq,'AES-256-ECB',$disposeAPI,OPENSSL_RAW_DATA);
file_put_contents('./returnData.xml',$decrypt."\n\r",FILE_APPEND);
return $decrypt;
}
/**
* 返回XML格式响应
*/
private function replyXml($code, $msg) {
$xml = "<xml>
<return_code><![CDATA[{$code}]]></return_code>
<return_msg><![CDATA[{$msg}]]></return_msg>
</xml>";
exit($xml);
}
更多推荐



所有评论(0)