【收藏】银联在线支付商户UPMP接口的使用和说明

【转载】http://www.dodobook.net/php/1110

相关的文档可以参阅。http://202.101.25.178:8080/sim/docs/

 

这个接口文档还是比较完备的,相信很多技术人员稍加摸索,就能实现了。

目录结构为

Upmp
----upmp_config.php
----upmp_core.php
----UpmpService.php
----notify_url.php

直接上代码:upmp_config.php

<?php
 
/**
 * 类名:配置类
 * 功能:配置类
 * 类属性:公共类
 * 版本:1.0
 * 日期:2012-10-11
 * 作者:中国银联UPMP团队
 * 版权:中国银联
 * 说明:以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己的需要,按照技术文档编写,并非一定要使用该代码。该代码仅供参考。
 * */
 
class upmp_config
{
     
    static $timezone                = "Asia/Shanghai"; //时区
     
    static $version                 = "2.1.4"; // 版本号(测试的时候用2.1.4版本)
    static $charset                 = "UTF-8"; // 字符编码
    static $sign_method             = "MD5"; // 签名方法,目前仅支持MD5
     
    static $mer_id                  = "8800000000*****"; // 商户号
    static $security_key            = "1nHFngmByQgZ5N71OrfO7yS*******"; // 商户密钥
    static $mer_back_end_url        = "http://pay.dodobook.net/api/upmp/successByUpmp"; // 后台通知地址
    static $mer_front_end_url       = "http://pay.dodobook.net/api/upmp/successByUpmp"; // 前台通知地址
 
    static $upmp_trade_url          = "http://202.101.25.178:8080/gateway/merchant/trade";
    static $upmp_query_url          = "http://202.101.25.178:8080/gateway/merchant/query";  
     
    const VERIFY_HTTPS_CERT         = false;
     
    const RESPONSE_CODE_SUCCESS     = "00"; // 成功应答码
    const SIGNATURE                 = "signature"; // 签名
    const SIGN_METHOD               = "signMethod"; // 签名方法
    const RESPONSE_CODE             = "respCode"; // 应答码
    const RESPONSE_MSG              = "respMsg"; // 应答信息
     
    const QSTRING_SPLIT             = "&"; // &
    const QSTRING_EQUAL             = "="; // =
     
}
 
?>
View Code

upmp_core.php

<?php
/**
 * 类名:交易服务类
 * 功能:接口公用函数类
 * 版本:1.0
 * 日期:2012-10-11
 * 作者:中国银联UPMP团队
 * 版权:中国银联
 * 说明:以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己的需要,按照技术文档编写,并非一定要使用该代码。该代码仅供参考。
 * */
 
require_once("upmp_config.php");
 
/**
 * 除去请求要素中的空值和签名参数
 * @param para 请求要素
 * @return 去掉空值与签名参数后的请求要素
 */
function paraFilter($para) {
    $result = array ();
    while ( list ( $key, $value ) = each ( $para ) ) {
        if ($key == upmp_config::SIGNATURE || $key == upmp_config::SIGN_METHOD || $value == "") {
            continue;
        } else {
            $result [$key] = $para [$key];
        }
    }
    return $result;
}
 
/**
 * 生成签名
 * @param req 需要签名的要素
 * @return 签名结果字符串
 */
function buildSignature($req) {
    $prestr = createLinkstring($req, true, false);
    $prestr = $prestr.upmp_config::QSTRING_SPLIT.md5(upmp_config::$security_key);
    return md5($prestr);
}
 
/**
 * 把请求要素按照“参数=参数值”的模式用“&”字符拼接成字符串
 * @param para 请求要素
 * @param sort 是否需要根据key值作升序排列
 * @param encode 是否需要URL编码
 * @return 拼接成的字符串
 */
function createLinkString($para, $sort, $encode) {
    $linkString  = "";
    if ($sort){
        $para = argSort($para);
    }
    while (list ($key, $value) = each ($para)) {
        if ($encode){
            $value = urlencode($value);
        }
        $linkString.=$key.upmp_config::QSTRING_EQUAL.$value.upmp_config::QSTRING_SPLIT;
    }
    //去掉最后一个&字符
    $linkString = substr($linkString,0,count($linkString)-2);
     
    return $linkString;
}
 
/**
 * 对数组排序
 * @param $para 排序前的数组
 * return 排序后的数组
 */
function argSort($para) {
    ksort($para);
    reset($para);
    return $para;
}
 
/*
 * curl_call
*
* @url:  string, curl url to call, may have query string like ?a=b
* @content: array(key => value), data for post
*
* return param:
*   mixed:
*     false: error happened
*     string: curl return data
*
*/
function post($url, $content = null)
{
    if (function_exists("curl_init")) {
        $curl = curl_init();
 
        if (is_array($content)) {
            $data = http_build_query($content);
        }
 
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $content);
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_HEADER, false);
        curl_setopt($curl, CURLOPT_TIMEOUT, 60); //seconds
         
        // https verify
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, upmp_config::VERIFY_HTTPS_CERT);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, upmp_config::VERIFY_HTTPS_CERT);
 
        $ret_data = curl_exec($curl);
 
        if (curl_errno($curl)) {
            printf("curl call error(%s): %s\n", curl_errno($curl), curl_error($curl));
            curl_close($curl);
            return false;
        }
        else {
            curl_close($curl);
            return $ret_data;
        }
    } else {
        throw new Exception("[PHP] curl module is required");
    }
}
 
?>
View Code

UpmpService.php

<?php
/**
 * 类名:接口处理核心类
 * 功能:组转报文请求,发送报文,解析应答报文
 * 版本:1.0
 * 日期:2012-10-11
 * 作者:中国银联UPMP团队
 * 版权:中国银联
 * 说明:以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己的需要,按照技术文档编写,并非一定要使用该代码。该代码仅供参考。
 */
 
require_once("upmp_core.php");
 
if (function_exists("date_default_timezone_set")) {
    date_default_timezone_set(upmp_config::$timezone);
}
 
class UpmpService {
     
    /**
     * 交易接口处理
     * @param req 请求要素
     * @param resp 应答要素
     * @return 是否成功
     */
    static function trade($req, &$resp) {
        $nvp = self::buildReq($req);
    //    file_put_contents('charge_req.txt', $nvp,FILE_APPEND); //写文件记录,用于提交请求的报文
        $respString = post(upmp_config::$upmp_trade_url, $nvp);
    //    file_put_contents('aa.txt', $respString);         //写文件记录用于报文信息
        return self::verifyResponse($respString, $resp);
    }
     
    /**
     * 交易查询处理
     * @param req 请求要素
     * @param resp 应答要素
     * @return 是否成功
     */
    static function query($req, &$resp) {
        $nvp = self::buildReq($req);
   //     file_put_contents('charge_search.txt', $nvp,FILE_APPEND);  //写文件记录,用于提交请求的报文 
        $respString = post(upmp_config::$upmp_query_url, $nvp);
    //    file_put_contents('bb.txt', $respString);           //写文件记录用于报文信息
        return self::verifyResponse($respString, $resp);
    }
     
    /**
     * 拼接请求字符串
     * @param req 请求要素
     * @return 请求字符串
     */
    static function buildReq($req) {
        //除去待签名参数数组中的空值和签名参数
        $filteredReq = paraFilter($req);
        // 生成签名结果
        $signature = buildSignature($filteredReq);
         
        // 签名结果与签名方式加入请求
        $filteredReq[upmp_config::SIGNATURE] = $signature;
        $filteredReq[upmp_config::SIGN_METHOD] = upmp_config::$sign_method;
         
        return createLinkstring($filteredReq, false, true);
    }
     
    /**
     * 拼接保留域
     * @param req 请求要素
     * @return 保留域
     */
    static function buildReserved($req) {
        $prestr = "{".createLinkstring($req, true, true)."}";
        return $prestr;
    }
     
    /**
     * 应答解析
     * @param respString 应答报文
     * @param resp 应答要素
     * @return 应答是否成功
     */
    static function verifyResponse($respString, &$resp) {
        if  ($respString != ""){
            parse_str($respString, $para);
             
            $signIsValid = self::verifySignature($para);
 
            $resp = $para;
            if ($signIsValid) {
                return true;
            }else {
                return false;
            }
        }
    }
     
    /**
     * 异步通知消息验证
     * @param para 异步通知消息
     * @return 验证结果
     */
    static function verifySignature($para) {
        $respSignature = $para[upmp_config::SIGNATURE];
        // 除去数组中的空值和签名参数
        $filteredReq = paraFilter($para);
        $signature = buildSignature($filteredReq);
        if ("" != $respSignature && $respSignature==$signature) {
            return true;
        }else {
            return false;
        }
    }
     
}
?>
View Code

修改成分配给你的参数。至此基本上这个包已经完整了。上面还有一些写文件的操作。用于填写报文。

调用的方法和成功的回调响应的接口

//会员充值到账户余额--使用银联--生成订单(简易版本)
public function actionChargeByUpmp() {
    $request = Yii::app()->request;
    Yii::import('application.extensions.Upmp.*');        //引入Upmp
    $member_id = $request->getParam('mid');              //会员ID
    $number =  $request->getParam('number');         //会员充值的金额
    //加入对会员的安全状态各方面的检测和处理.......
 
    $transaction = Yii::app()->db->beginTransaction();        //使用事务能保证尽可能少的产生脏数据
    try{
        //先本地生成订单,生成订单ID
        $billList = new MemberBillList;
        $billList->member_id = $member_id;
        $billList->number = $number;         //充值金额
        $billList->in_out = '1';             //会员支出
        $billList->in_out_type = '2';            //会员个人余额充值
        $billList->add_time = time();
        $billList->status = '2';             //账单状态未完成
        $billList->remark = '会员银联充值';        //账单状态未完成
        $billList->isNewRecord = true;
        if(!$billList->save()){
            throw new Exception('生成账单记录失败,'.$this->getModelFirstError($billList), 11);
        }
 
        //银联的这个账单号的长度是(8-40),我在文档上只看到了不大于40,没看到最小8.(后来询问才知道)
        $orderNumber = date('Ymd').'MCHA'.$billList->id; //本地的账单ID,经过组装后用于提交给银联
     
        //去服务器上生成流水号
        $notify_url = 'http://pay.dodobook.net/api/upmp/successByChargeUpmp';   //通知的地址(成功之后每隔几分钟向该地址发送成功的通知)
        $bankRt = $this->actionCreateBill($orderNumber,$number,$notify_url); //根据提交的信息去请求并生成流水号
 
        if($bankRt['respCode'] != '00'){        //判断返回的状态
            throw new Exception('银行接口返回失败,'.$bankRt['respMsg'], 10);
        }
 
        if(!$bankRt['tn']){         //判断流水号
            throw new Exception('银行接口流水号未返回', 11);
        }
 
        //更新刚才的账单的信息,增加刚才账单和流水号等的对应(完整记录)
        $bank_order_id = $bankRt['tn'];
        $newBillList = MemberBillList::model()->findByPk($billList->id);
        $newBillList->bank_tn_id = $bank_order_id;       //银行流水号
        $newBillList->bank_order_id = $orderNumber;      //银行系统记录的账单号
 
        if(!$newBillList->save()){
            throw new Exception('更新流水号失败', 12);
        }
 
        $transaction->commit();
        $json['id'] = date("YmdHis").'_'.$billList->id;      //账单的ID
        $json['bank_order_id'] = $bank_order_id;        //银行流水号
 
    //  $this->_end_api('0','账单记录生成成功,账单状态为未完成.',$json);
    }catch(Exception $e){
        $transaction->rollback();
    //  $this->_end_api($e->getCode(),'生成账单记录失败',$e->getMessage());
    }
}
View Code
//会员充值到账户余额--使用银联--回调方法
public function actionSuccessByChargeUpmp() {
    $request = Yii::app()->request;
    Yii::import('application.extensions.Upmp.*');        //引入Upmp
 
    测试的时候把返回过来的回调的信息先写文本日志记录
    $str = '';
    foreach ($_POST as $k => $v) {
        $str .= $k .'==='.$v.'<hr>';
    }
 
    file_put_contents('charge_upmp.txt', '交易时间'.date('Y-m-d H:i:s').$str,FILE_APPEND);
    if($_POST['transStatus'] == '00'){
        echo 'success';
    }
    exit();
 
/*  //正式环境下的账单的处理
    if (UpmpService::verifySignature($_POST)){// 服务器签名验证成功
        //请在这里加上商户的业务逻辑程序代码
        //获取通知返回参数,可参考接口文档中通知参数列表(以下仅供参考)
        $transStatus = $_POST['transStatus'];// 交易状态
        if ("" != $transStatus && "00"==$transStatus){
            // 交易处理成功
            $qn = $_POST['qn'];                     //查询流水号
            $orderNumber = $_POST['orderNumber'];   //商户订单号
            $total_fee = $_POST['settleAmount'] / 100;  //清算金额
 
            $billList = MemberBillList::model()->find("bank_order_id = '{$orderNumber}'");
            $bill_list_id = $billList->id;                    //账单记录ID
            $memBillList = MemberBillList::model()->findByPk($bill_list_id);
            if($memBillList->status == '1'){             //如果已处理过
                echo 'success';
                $this->_end_api('0','Sucess','Sucess');      //处理成功
            }
             
            $transaction = Yii::app()->db->beginTransaction();
            try{
                //bill_list_id 
                //total_fee     银联支付的金额
                //更新会员账单记录表 member_bill_list
                $memBillList = MemberBillList::model()->findByPk($bill_list_id);
                $member_id = $memBillList->member_id;        //消费的会员ID
                $memBill = MemberBill::model()->find("member_id = '{$member_id}'");
                $memBillList->status = '1';                  //更新状态值为已完成
                if(!$memBillList->save()){
                    $this->_end_api('1','更改会员账单记录表状态失败',$this->getModelFirstError($memBillList));
                }
 
                //更新会员账单--.............
                 
                $transaction->commit();
                echo 'success';             //成功之后告诉银联,避免银联不断的发送信息过来,会尝试24小时,5次
                $this->_end_api('0','success','success');        //处理成功
            }catch(Exception $e){
                $transaction->rollback();
                echo "fail";
                $this->_end_api('12','银联支付回调处理失败','');
            }
        }else {
            file_put_contents('charge_upmp_err.txt', '失败:交易时间'.date('Y-m-d H:i:s').$str,FILE_APPEND);
        }
        echo "success";
    }else {// 服务器签名验证失败
        echo "fail";
        $this->_end_api('1','交易失败','');
    }
    */
}
View Code
//生成账单请求的公共方法
public function actionCreateBill($orderNumber='',$orderAmount='',$notify_url) {
    header('Content-Type:text/html;charset=utf-8');
    Yii::import('application.extensions.Upmp.*');        //引入Upmp
 
    //需要填入的部分
    $req['version']             = upmp_config::$version;    // 版本号
    $req['charset']             = upmp_config::$charset;    // 字符编码
    $req['transType']           = "01";                     // 交易类型
    $req['merId']               = upmp_config::$mer_id;     // 商户代码
    $req['backEndUrl']          = $notify_url;              // 通知URL
    $req['frontEndUrl']         = $notify_url;              // 前台通知URL(可选)
    $req['orderDescription']    = "订单描述";               // 订单描述(可选)
    $req['orderTime']           = date("YmdHis");           // 交易开始日期时间yyyyMMddHHmmss
    $req['orderTimeout']        = "";                       // 订单超时时间yyyyMMddHHmmss(可选)
    $req['orderNumber']         = $orderNumber;             //订单号(商户根据自己需要生成订单号)
    $req['orderAmount']         = intval($orderAmount * 100); // 订单金额(人民币单位为分)
    $req['orderCurrency']       = "156";                    // 交易币种(可选)--人民币
    $req['reqReserved']         = "透传信息";               // 请求方保留域(可选,用于透传商户信息)
 
    // 保留域填充方法
    $merReserved['test']        = "test";
    $req['merReserved']         = UpmpService::buildReserved($merReserved); // 商户保留域(可选)
 
    $resp = array ();
    $validResp = UpmpService::trade($req, $resp);
 
    // 商户的业务逻辑
    if ($validResp){        // 服务器应答签名验证成功
        return $resp;
    }else {                 // 服务器应答签名验证失败
        return $resp;
    }
}
View Code

备注说明:
1.可以完全按照银联提供的接口和example来使用,有时候会报错,大多数情况下是目录结构和命名规范导致。自己改进。
2.测试报告的报文,都是需要状态为成功的才能通过。即测试的订单推送等都是需要完成的。
3.用户发出的请求报文,可以通过写文件操作记录。收到的应答报文也可以写文件记录。根据他们的示例。
4.文档可能有进一步的改进,请到官网去查询相关的资料。此处只是个人的一个demo的记录。
5.欢迎大家交流和指点。

转载于:https://www.cnblogs.com/hzp0szl/p/4943002.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值