/*首次写csdn 博客 , 如有不足 , 还望包含 . 微信和支付宝单文件实现接口的文章较多 , 也就不往上写了 . 因为网上银联接口php方面的信息比较少, 只是需要调用银联退款接口 . 用银联下载的sdk怎么调 都报签名错误 . 后来自己基于银联 sdk 封装了一个单文件 实现 银联 查询 和 退款的接口 ,如果有需要的也可以自行添加其他接口 , 都比较简单. 其中log类可以自定义,也可以写个方法代替 , 看个人喜好就行 . 这个单文件写在这供大家参考下 , 也作为自己的备忘.*/
话不多说 ,直接上代码 .
<?php
header('Content-Type:text/html;charset=utf-8 ');
//银联目录 ( 只用证书和日志类 可自定义)
defined('UNIONPAY_PATH') or define('UNIONPAY_PATH',$_SERVER ['DOCUMENT_ROOT']."你的证书目录");
//日志 目录
defined('SDK_LOG_FILE_PATH') or define("SDK_LOG_FILE_PATH",UNIONPAY_PATH . '你的日志类目录');
//日志级别
defined('SDK_LOG_LEVEL') or define("SDK_LOG_LEVEL",'INFO');
/*使用方法*/
$Unipay = new Unipay();
$input_arr['out_trade_no'] = '第三方订单号';
$input_arr['out_refund_no'] = '退单单号' ;
$input_arr['total_fee'] = "1" ; //总金额
$input_arr['refund_fee'] = "1" ; //要退的金额
$input_arr['post_time'] = "20181101102212" ; //下单时间
var_dump($Unipay->refund($input_arr));
/*银联支付类*/
class Unipay{
private $mch_id = '****************'; //银联商户 id
private $cert_path = '/unionpay_pc/key/**************.pfx'; //签名证书路径
private $cert_pwd = '******'; //签名证书密码
private $cert_id; //证书id
private $backUrl; //回调地址
private $log; //日志类
private $signature; //签名
public function __construct($mch_id='',$cert_path='',$cert_pwd=''){
include_once UNIONPAY_PATH . '/unionpay_pc/func/log.class.php';
$this->log = new PhpLog ( SDK_LOG_FILE_PATH, "PRC", SDK_LOG_LEVEL );
if($mch_id!='') $this->mch_id = $mch_id;
if($cert_path!='') {$this->cert_path = $cert_path;}else{$this->cert_path = UNIONPAY_PATH.$this->cert_path;}
if($cert_pwd!='') $this->cert_pwd = $cert_pwd;
$this->cert_id = $this->getSignCertId ();
$this->backUrl = '你的回调地址';
}
/**
* 获取证书id
*/
function getSignCertId(){
$pkcs12certdata = file_get_contents ( $this->cert_path );
openssl_pkcs12_read ( $pkcs12certdata, $certs, $this->cert_pwd );
$x509data = $certs ['cert'];
openssl_x509_read ( $x509data );
$certdata = openssl_x509_parse ( $x509data );
$cert_id = $certdata ['serialNumber'];
return $cert_id;
}
/**
* 申请退款
* @param type $input_arr
* @return type
*/
function refund($orders,$type=''){
//请求参数
$query = $this->unionPayQuery('', $orders);
if ($query['respCode'] != "00") {
return $query;
}
$params = array(
//以下信息非特殊情况不需要改动
'version' => '5.0.0', //版本号
'encoding' => 'utf-8', //编码方式
'certId' => $this->cert_id, //证书id
'signMethod' => '01', //签名方法
'txnType' => '04', //交易类型
'txnSubType' => '00', //交易子类
'bizType' => '000301', //业务类型
'accessType' => '0', //接入类型
'channelType' => '07', //渠道类型
'backUrl' => $this->backUrl, //后台通知地址
//TODO 以下信息需要填写
'merId' => $this->mch_id, //商户代码,请改成自己的商户号
'orderId' => $orders['out_refund_no'], //商户订单号,8-32位数字字母,不能含“-”或“_”,可以自行定制规则,重新产生-此处为在退款订单前拼接 T
'origQryId' => $query["queryId"], //原消费的queryId,可以从查询接口或者通知接口中获取
'txnTime' => date('YmdHis', time()), //订单发送时间,格式为YYYYMMDDhhmmss,重新产生,不同于原消费
'txnAmt' => $orders['refund_fee'], //交易金额,退货总金额需要小于等于原消费
);
$this->sign ( $params ); // 签名
$params['signature'] = $this->signature;
$url = 'https://gateway.95516.com/gateway/api/backTransReq.do';
$result_arr = $this->to_array($this->curlPost ( $params, $url));
$this->log->LogInfo ( json_encode($result_arr) );
return $result_arr;
}
/**
* 获取preid
* @param type $request
* @param type $orders
*/
function unionPayQuery($request, $orders){
$params = array(
//以下信息非特殊情况不需要改动
'version' => '5.0.0', //版本号
'certId' => $this->cert_id, //证书id
'encoding' => 'utf-8', //编码方式
'signMethod' => '01', //签名方法
'txnType' => '00', //交易类型
'txnSubType' => '00', //交易子类
'bizType' => '000000', //业务类型
'accessType' => '0', //接入类型
'backUrl' => $this->backUrl, //后台通知地址
'channelType' => '07', //渠道类型
//TODO 以下信息需要填写
'orderId' => $orders['out_trade_no'], //请修改被查询的交易的订单号,8-32位数字字母,不能含“-”或“_”
'merId' => $this->mch_id, //商户代码,请改自己的测试商户号
'txnTime' => date('YmdHis', $orders['post_time']), //请修改被查询的交易的订单发送时间,格式为YYYYMMDDhhmmss
);
$sign = $this->sign ( $params ); // 签名
$params['signature'] = $this->signature;
$url = 'https://gateway.95516.com/gateway/api/queryTrans.do';
$result_arr = $this->to_array($this->curlPost ( $params, $url));
$this->log->LogInfo ( json_encode($result_arr) );
return $result_arr;
}
/**
* 签名
*
* @param String $params_str
*/
function sign($params) {
$this->log->LogInfo ( '=====签名报文开始======' );
if(isset($params['transTempUrl'])){
unset($params['transTempUrl']);
}
// 转换成key=val&串
$params_str = $this->coverParamsToString ( $params );
$this->log->LogInfo ( "签名key=val&...串 >" . $params_str );
$params_sha1x16 = sha1 ( $params_str, FALSE );
$this->log->LogInfo ( "摘要sha1x16 >" . $params_sha1x16 );
// 签名证书路径
$cert_path = $this->cert_path;
$private_key = $this->getPrivateKey ( $cert_path );
// 签名
$sign_falg = openssl_sign ( $params_sha1x16, $signature, $private_key, OPENSSL_ALGO_SHA1 );
if ($sign_falg) {
$signature_base64 = base64_encode ( $signature );
$this->log->LogInfo ( "签名串为 >" . $signature_base64 );
return $params ['signature'] = $this->signature = $signature_base64;
} else {
$this->log->LogInfo ( ">>>>>签名失败<<<<<<<" );
}
$this->log->LogInfo ( '=====签名报文结束======' );
return false;
}
/**
* 数组 排序后转化为字体串
*
* @param array $params
* @return string
*/
function coverParamsToString($params) {
$sign_str = '';
// 排序
ksort ( $params );
foreach ( $params as $key => $val ) {
if ($key == 'signature') {
continue;
}
$sign_str .= sprintf ( "%s=%s&", $key, $val );
// $sign_str .= $key . '=' . $val . '&';
}
return substr ( $sign_str, 0, strlen ( $sign_str ) - 1 );
}
/**
* 返回(签名)证书私钥 -
*
* @return unknown
*/
function getPrivateKey($cert_path) {
$pkcs12 = file_get_contents ( $cert_path );
openssl_pkcs12_read ( $pkcs12, $certs, $this->cert_pwd );
return $certs ['pkey'];
}
/**
* 结果转数组
* @param type $str
* @return type
*/
function to_array($str){
$result_arr_1 = explode('&',$str);
$array = array();
foreach($result_arr_1 as $k=>$v){
$one = explode('=',$v);
$array[$one[0]] = $one[1];
}
return $array;
}
/**
* curl方法
* @param type $postData
* @param type $url
* @param type $options
* @return type
*/
function curlPost($postData = '',$url = '', $options = array())
{
if (is_array($postData)) {
$postData = http_build_query($postData);
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数
if (!empty($options)) {
curl_setopt_array($ch, $options);
}
//https请求 不验证证书和host
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$data = curl_exec($ch);
curl_close($ch);
return $data;
}
function dump($var, $echo = true, $label = null, $strict = true) {
$label = ($label === null) ? '' : rtrim($label) . ' ';
if (!$strict) {
if (ini_get('html_errors')) {
$output = print_r($var, true);
$output = '<pre>' . $label . htmlspecialchars($output, ENT_QUOTES) . '</pre>';
} else {
$output = $label . print_r($var, true);
}
} else {
ob_start();
var_dump($var);
$output = ob_get_clean();
if (!extension_loaded('xdebug')) {
$output = preg_replace('/\]\=\>\n(\s+)/m', '] => ', $output);
$output = '<pre>' . $label . htmlspecialchars($output, ENT_QUOTES) . '</pre>';
}
}
if ($echo) {
echo($output);
return null;
} else
return $output;
}
function show_err($str){
exit(json_encode(array('staus'=>0,'infotxt'=>$str,'icon'=>'error','time'=>2)));
}
}