项目接入银联支付的过程, 在此记录下,希望能帮助开发盆友平坑。
银联SKD链接:https://open.unionpay.com/ajweb/product/newProDetail?proId=1&cataId
本地保存的sdk
首先下载sdk包导入以后,创建控制器YunpayController.class.php,
直接上代码:
<?php
namespace Appapi\Controller;
use Common\Controller\HomebaseController;
class YunpayController extends HomebaseController {
public function Establish(){
$data = [
'uid' => I('uid') ?: 'null',
'changeid' => I('changeid') ?: 'null',
'coin' => I('coin') ?: 'null',
'money' => I('money') ?: 'null',
];
die('<h3>'.var_export($data).'<br>请核对打印参数结果,若与提交一致则说明对接成功');
}
/* 提交订单 */
public function index()
{
header ( 'Content-type:text/html;charset=utf-8' );
Vendor('Yunpay.acp_service');
$frontUrl = "http://".I("server.HTTP_HOST")."/5.php"; //前台通知地址
$backUrl = "http://".I("server.HTTP_HOST")."/index.php?g=Appapi&m=yunpay&a=notify_yl"; //后台通知地址
$params = array(
//以下信息非特殊情况不需要改动
'version' => \com\unionpay\acp\sdk\SDKConfig::getSDKConfig()->version, //版本号
'encoding' => 'utf-8', //编码方式
'txnType' => '01', //交易类型
'txnSubType' => '01', //交易子类
'bizType' => '000201', //业务类型
'frontUrl' => $frontUrl, //前台通知地址
'backUrl' => $backUrl, //后台通知地址
'signMethod' => \com\unionpay\acp\sdk\SDKConfig::getSDKConfig()->signMethod, //签名方法
'channelType' => '08', //渠道类型,07-PC,08-手机
'accessType' => '0', //接入类型
'currencyCode' => '156',
'merId' => \com\unionpay\acp\sdk\SDKConfig::getSDKConfig()->merid, //商户号
'txnTime' => date('YmdHis'),
'payTimeout' => date('YmdHis', strtotime('+60 minutes')) //订单发送时间(超过超时时间调查询接口应答origRespCode不是A6或者00的就可以判断为失败)
);
$uid=I("uid");
$token=I("token");
$changeid=I("changeid");
$coin=I("coin");
$money=I("money");
if(!$uid || !$token){
$rs['code']=1001;
$rs['msg']='身份未验证:uid or token on null!';
$this->ajaxReturn($rs);
}
if(!$changeid || (!is_numeric($changeid))){
$rs['code']=1002;
$rs['msg']='商品ID错误:changeid on null or changeid not numeric!';
$this->ajaxReturn($rs);
}
if(!$coin || !$money){
$rs['code']=1003;
$rs['msg']='商品信息错误:coin or money on null!';
$this->ajaxReturn($rs);
}
if(!$true_token = M('users')->where("id={$uid} and user_type='2'")->order("id DESC")->getField("token")) {
$rs['code']=1004;
$rs['msg']='身份验证失败:uid not null!';
$this->ajaxReturn($rs);
}
if($true_token != $token){
$rs['code']=1005;
$rs['msg']='身份验证失败:token not null!'.$true_token;
$this->ajaxReturn($rs);
}
if( !$uid || !$token || checkToken($uid,$token)==700 ){
$rs['code']=1005;
$rs['msg']='身份验证失败:token not null!';
$this->ajaxReturn($rs);
}
if(!$change_info = M("charge_rules")->where("id='{$changeid}'")->find()) {
$rs['code']=1006;
$rs['msg']='商品信息错误:changeid not null!';
$this->ajaxReturn($rs);
}
if ($change_info['coin'] != $coin || $change_info['money'] != $money) {
$rs['code']=1007;
$rs['msg']='商品信息错误:coin or money untrue!';
$this->ajaxReturn($rs);
}
$orderid=$uid.'D'.date('YmdHis').rand(100,999);
$orderinfo=array(
"uid"=>$uid,
"touid"=>$uid,
"money"=>$money,
"coin"=>$coin,
"orderno"=>$orderid,
"coin_give"=>$change_info['give'],
"type"=>4,
"status"=>0,
"addtime"=>time()
);
M("users_charge")->create($orderinfo);
if(M("users_charge")->add($orderinfo)) {
//加入商户参数
// $params['txnAmt'] = $money*100;
// $params['orderId'] = $orderid;
//
// \com\unionpay\acp\sdk\AcpService::sign ( $params );
// $uri = \com\unionpay\acp\sdk\SDKConfig::getSDKConfig()->frontTransUrl;
// $html_form = \com\unionpay\acp\sdk\AcpService::createAutoFormHtml( $params, $uri );
// echo $html_form;
// exit;
$rs['code']=0;
$rs['msg']='订单创建成功';
$rs['orderId']=$orderid;
$this->ajaxReturn($rs);
}else{
$rs['code']=1008;
$rs['msg']='订单创建失败!:orderinfo untrue!';
$this->ajaxReturn($rs);
}
}
/* 异步处理 */
public function notify_yl()
{
Vendor('Yunpay.acp_service');
$logger = \com\unionpay\acp\sdk\LogUtil::getLogger();
$logger->LogInfo("receive back notify: " . \com\unionpay\acp\sdk\createLinkString ( $_POST, false, true ));
if (isset ( $_POST ['signature'] )) {
echo \com\unionpay\acp\sdk\AcpService::validate ( $_POST ) ? '验签成功' : '验签失败';
$respCode = I('post.respCode');
$orderId = I('post.orderId'); // 商户订单号
$total_amount = I('post.settleAmt'); //订单金额
$trade_no = I('post.queryId'); // queryId 银联唯一标识一笔交易
//判断respCode=00、A6后,对涉及资金类的交易,请再发起查询接口查询,确定交易成功后更新数据库。
if( $respCode=='00' ){
$this->unionpay($orderId,$total_amount,$trade_no);
}else{
$res = $this->confirmpay($orderId,'1');
if( $res == 'Successful' ){
$this->unionpay($orderId,$total_amount,$trade_no);
} else {
echo '交易失败';
}
}
} else {
echo '签名为空';
}
}
/* 订单处理 */
public function unionpay($orderId,$total_amount,$trade_no)
{
$per = M("users_charge")
->where(['orderno'=>$orderId])
->order("id desc")
->find(); //查找该订单
if( $per['status']=='1' ){
echo '已充值';
return;
}
$data = array(
'ambient'=>0, //支付环境 0沙盒,1生产
'trade_no'=>$trade_no, //银联唯一标识
'status'=>'1' //交易状态
);
M("users_charge")
->where(['orderno'=>$orderId])
->order("id desc")
->save($data); // 更新订单
/* 更新会员虚拟币 */
$coin=$per['coin']+$per['coin_give'];
M("users")
->where(['id'=>$per['touid']])
->setInc("coin",$coin);
/* ...后续继续补充业务需求 */
echo 'Success';
return;
}
/* 重新验证订单是否交易成功 */
public function confirmpay($orderId,$L)
{
header ( 'Content-type:text/html;charset=utf-8' );
Vendor('Yunpay.acp_service');
$params = array(
//以下信息非特殊情况不需要改动
'version' => \com\unionpay\acp\sdk\SDKConfig::getSDKConfig()->version, //版本号
'encoding' => 'utf-8', //编码方式
'signMethod' => \com\unionpay\acp\sdk\SDKConfig::getSDKConfig()->signMethod, //签名方法
'txnType' => '00', //交易类型
'txnSubType' => '00', //交易子类
'bizType' => '000000', //业务类型
'accessType' => '0', //接入类型
'channelType' => '07', //渠道类型
'merId' => \com\unionpay\acp\sdk\SDKConfig::getSDKConfig()->merid, //商户号
);
$time = M("users_charge")->where('orderno='.$orderId)->order("id desc")->find()['addtime'];
$params['orderId'] = $orderId; //交易的订单号
$params['txnTime'] = date('YmdHis',$time); //订单发送时间
\com\unionpay\acp\sdk\AcpService::sign ( $params ); // 签名
$url = \com\unionpay\acp\sdk\SDKConfig::getSDKConfig()->singleQueryUrl;
$result_arr = \com\unionpay\acp\sdk\AcpService::post ( $params, $url);
if(count($result_arr)<=0) { //没收到200应答的情况
return 'No200';
}
if (!\com\unionpay\acp\sdk\AcpService::validate ($result_arr) ){
return "应答报文验签失败";
}
if ($result_arr["respCode"] == "00"){
if ($result_arr["origRespCode"] == "00"){
//交易成功
//TODO
return "Successful";
} else if ($result_arr["origRespCode"] == "03"
|| $result_arr["origRespCode"] == "04"
|| $result_arr["origRespCode"] == "05"){
//后续需发起交易状态查询交易确定交易状态
//TODO
return "交易处理中,请稍微查询";
} else {
//其他应答码做以失败处理
//TODO
return "交易失败:" . $result_arr["origRespMsg"];
}
} else if ($result_arr["respCode"] == "03"
|| $result_arr["respCode"] == "04"
|| $result_arr["respCode"] == "05" ){
//后续需发起交易状态查询交易确定交易状态
//TODO
return "处理超时,请稍微查询";
} else {
//其他应答码做以失败处理
//TODO
return "失败:" . $result_arr["respMsg"];
}
}
}
补充:
这里第一个方法xxx 中的respCode等于00 就是支付成功 ,如果没有需要根据你生成的订单号在次查询在结果。这里客服说这种失败不好模拟,就不说了,但是这操作方法还是
建议写下,以防万一 。
最后说明下几个参数 queryId 银联唯一标识,需要保存, 还有银联支付是按 '分' 做单位的 所以支付跳转前 假如是1元,你得乘以100,它才可以识别为1元,要不然就是0.01元
然后异步到你的时候,如果你是元的单位在除于100,如果是分就不用了。
搞定收工!
无论从事什么行业,只要做好两件事就够了,一个是你的专业、一个是你的人品,专业决定了你的存在,人品决定了你的人脉,剩下的就是坚持,用善良專業和真诚赢取更多的信任。