开发者社区> Tinywan.> 正文

ThinkPHP 3.2 支付宝即时到账接口开发

简介: 前言:   一、支付流程 构造请求参数 向支付宝网关发送请求 生成支付宝页面 支付宝交易结果 二、构建支付类  1.官方即时到账文档地址: https://doc.open.
+关注继续查看

前言:

 

一、支付流程

  1. 构造请求参数
  2. 向支付宝网关发送请求
  3. 生成支付宝页面
  4. 支付宝交易结果

二、构建支付类

 1.官方即时到账文档地址:

https://doc.open.alipay.com/doc2/detail?treeId=62&articleId=103740&docType=1

 2.支付类:

<?php
/**
 * Created by PhpStorm.
 * User: Tinywan
 * Date: 2016/9/16
 * Time: 10:44
 */
namespace Common\Model;

use Think\Model;

class PayModel
{
    public $alipay_gateway_new = 'https://mapi.alipay.com/gateway.do?';
    public $https_verify_url = 'https://mapi.alipay.com/gateway.do?service=notify_verify&';
    public $http_verify_url = 'http://notify.alipay.com/trade/notify_query.do?';
    private $keys = 'k2zu8i7h9enbkafsvtfrgdcuy1n273qn';
    private $partner = '2088802807619823';
    private $cacert = 'http://www.19981.com/cert/cacert.pem';

    public function alipay($data)
    {
        $param = array(
            'service' => 'create_direct_pay_by_user',
            'partner' => $this->partner,
            '_input_charset' => 'utf-8',
            'notify_url' => 'http://wechatu.xd107.com/pay/notify/notify_url',  //异步通知
            //'return_url' => 'http://wechatu.xd107.com/pay/notify/return_url', //同步通知
            'out_trade_no' => $data['out_trade_no'],
            'subject' => $data['subject'],
            'payment_type' => '1',
            'total_fee' => $data['total_fee'],
            'seller_email' => 'mzhsoft@126.com',
        );
        //构造请求参数
        $res = $this->buildRequestPara($param);
        $form = $this->buildRequestForm($res, 'get', '提交');
        return $form;
        //echo($form);
    }

    /**
     * 针对notify_url验证消息是否是支付宝发出的合法消息
     * @return 验证结果
     */
    public function verifyNotify()
    {
        if (empty($_POST)) {//判断POST来的数组是否为空
            return false;
        } else {
            //生成签名结果
            $isSign = $this->getSignVeryfy($_POST, $_POST["sign"]);
            //获取支付宝远程服务器ATN结果(验证是否是支付宝发来的消息)
            $responseTxt = 'false';
            if (!empty($_POST["notify_id"])) {
                $responseTxt = $this->getResponse($_POST["notify_id"]);
            }

            //写日志记录
            //if ($isSign) {
            //    $isSignStr = 'true';
            //}
            //else {
            //    $isSignStr = 'false';
            //}
            //$log_text = "responseTxt=".$responseTxt."\n notify_url_log:isSign=".$isSignStr.",";
            //$log_text = $log_text.createLinkString($_POST);
            //logResult($log_text);

            //验证
            //$responsetTxt的结果不是true,与服务器设置问题、合作身份者ID、notify_id一分钟失效有关
            //isSign的结果不是true,与安全校验码、请求时的参数格式(如:带自定义参数等)、编码格式有关
            if (preg_match("/true$/i", $responseTxt) && $isSign) {
                return true;
            } else {
                return false;
            }
        }
    }

    /**
     * 针对return_url验证消息是否是支付宝发出的合法消息
     * @return 验证结果
     */
    public function verifyReturn()
    {

        if (empty($_GET)) {//判断POST来的数组是否为空
            return false;
        } else {
            //生成签名结果
            $isSign = $this->getSignVeryfy($_GET, $_GET["sign"]);
            //获取支付宝远程服务器ATN结果(验证是否是支付宝发来的消息)
            $responseTxt = 'false';
            if (!empty($_GET["notify_id"])) {
                $responseTxt = $this->getResponse($_GET["notify_id"]);
            }

            //写日志记录
            //if ($isSign) {
            //    $isSignStr = 'true';
            //}
            //else {
            //    $isSignStr = 'false';
            //}
            //$log_text = "responseTxt=".$responseTxt."\n return_url_log:isSign=".$isSignStr.",";
            //$log_text = $log_text.createLinkString($_GET);
            //logResult($log_text);

            //验证
            //$responsetTxt的结果不是true,与服务器设置问题、合作身份者ID、notify_id一分钟失效有关
            //isSign的结果不是true,与安全校验码、请求时的参数格式(如:带自定义参数等)、编码格式有关
            if (preg_match("/true$/i", $responseTxt) && $isSign) {
                return true;
            } else {
                return false;
            }
        }
    }

    /**
     * 获取返回时的签名验证结果
     * @param $para_temp 通知返回来的参数数组
     * @param $sign 返回的签名结果
     * @return 签名验证结果
     */
    function getSignVeryfy($para_temp, $sign)
    {
        //除去待签名参数数组中的空值和签名参数
        $para_filter = $this->paraFilter($para_temp);

        //对待签名参数数组排序
        $para_sort = $this->argSort($para_filter);

        //把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
        $prestr = $this->createLinkstring($para_sort);

        $isSgin = false;
        switch (strtoupper(trim('MD5'))) {
            case "MD5" :
                $isSgin = $this->md5Verify($prestr, $sign, $this->keys);
                break;
            default :
                $isSgin = false;
        }

        return $isSgin;
    }

    /**
     * 获取远程服务器ATN结果,验证返回URL
     * @param $notify_id 通知校验ID
     * @return 服务器ATN结果
     * 验证结果集:
     * invalid命令参数不对 出现这个错误,请检测返回处理中partner和key是否为空
     * true 返回正确信息
     * false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
     */
    function getResponse($notify_id)
    {
        $transport = strtolower(trim('http'));
        $partner = trim($this->partner);
        $veryfy_url = '';
        if ($transport == 'https') {
            $veryfy_url = $this->https_verify_url;
        } else {
            $veryfy_url = $this->http_verify_url;
        }
        $veryfy_url = $veryfy_url . "partner=" . $partner . "&notify_id=" . $notify_id;
        $responseTxt = $this->getHttpResponseGET($veryfy_url, $this->cacert);

        return $responseTxt;
    }

    /**
     * 建立请求,以表单HTML形式构造(默认)
     * @param $para_temp 请求参数数组
     * @param $method 提交方式。两个值可选:post、get
     * @param $button_name 确认按钮显示文字
     * @return 提交表单HTML文本
     */
    function buildRequestForm($para_temp, $method, $button_name)
    {
        //待请求参数数组
        $para = $this->buildRequestPara($para_temp);

        $sHtml = "<form id='alipaysubmit' name='alipaysubmit' action='" . $this->alipay_gateway_new . "_input_charset=" . trim(strtolower('utf-8')) . "' method='" . $method . "'>";
        while (list ($key, $val) = each($para)) {
            $sHtml .= "<input type='hidden' name='" . $key . "' value='" . $val . "'/>";
        }

        //submit按钮控件请不要含有name属性
        $sHtml = $sHtml . "<input type='submit'  value='" . $button_name . "' style='display:none;'></form>";

        $sHtml = $sHtml . "<script>document.forms['alipaysubmit'].submit();</script>";

        return $sHtml;
    }

    /**
     * 生成要请求给支付宝的参数数组
     * @param $para_temp 请求前的参数数组
     * @return 要请求的参数数组
     */
    function buildRequestPara($para_temp)
    {
        //除去待签名参数数组中的空值和签名参数
        $para_filter = $this->paraFilter($para_temp);

        //对待签名参数数组排序
        $para_sort = $this->argSort($para_filter);

        //生成签名结果
        $mysign = $this->buildRequestMysign($para_sort);

        //签名结果与签名方式加入请求提交参数组中
        $para_sort['sign'] = $mysign;
        $para_sort['sign_type'] = strtoupper(trim('MD5'));

        return $para_sort;
    }

    /**
     * 除去数组中的空值和签名参数
     * @param $para 签名参数组
     * return 去掉空值与签名参数后的新签名参数组
     */
    function paraFilter($para)
    {
        $para_filter = array();
        while (list ($key, $val) = each($para)) {
            if ($key == "sign" || $key == "sign_type" || $val == "") continue;
            else    $para_filter[$key] = $para[$key];
        }
        return $para_filter;
    }

    /**
     * 对数组排序
     * @param $para 排序前的数组
     * return 排序后的数组
     */
    function argSort($para)
    {
        ksort($para);
        reset($para);
        return $para;
    }


    /**
     * 生成签名结果
     * @param $para_sort 已排序要签名的数组
     * return 签名结果字符串
     */
    function buildRequestMysign($para_sort)
    {
        //把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
        $prestr = $this->createLinkstring($para_sort);

        $mysign = "";
        switch (strtoupper(trim('MD5'))) {
            case "MD5" :
                $mysign = $this->md5Sign($prestr, $this->keys);
                break;
            default :
                $mysign = "";
        }

        return $mysign;
    }

    /**
     * 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
     * @param $para 需要拼接的数组
     * return 拼接完成以后的字符串
     */
    function createLinkstring($para)
    {
        $arg = "";
        while (list ($key, $val) = each($para)) {
            $arg .= $key . "=" . $val . "&";
        }
        //去掉最后一个&字符
        $arg = substr($arg, 0, count($arg) - 2);

        //如果存在转义字符,那么去掉转义
        if (get_magic_quotes_gpc()) {
            $arg = stripslashes($arg);
        }

        return $arg;
    }

    /**
     * 签名字符串
     * @param $prestr 需要签名的字符串
     * @param $key 私钥
     * return 签名结果
     */
    function md5Sign($prestr, $key)
    {
        $prestr = $prestr . $key;
        return md5($prestr);
    }

    /**
     * 验证签名
     * @param $prestr 需要签名的字符串
     * @param $sign 签名结果
     * @param $key 私钥
     * return 签名结果
     */
    function md5Verify($prestr, $sign, $key)
    {
        $prestr = $prestr . $key;
        $mysgin = md5($prestr);

        if ($mysgin == $sign) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 远程获取数据,GET模式
     * 注意:
     * 1.使用Crul需要修改服务器中php.ini文件的设置,找到php_curl.dll去掉前面的";"就行了
     * 2.文件夹中cacert.pem是SSL证书请保证其路径有效,目前默认路径是:getcwd().'\\cacert.pem'
     * @param $url 指定URL完整路径地址
     * @param $cacert_url 指定当前工作目录绝对路径
     * return 远程输出的数据
     */
    function getHttpResponseGET($url, $cacert_url)
    {
        $curl = curl_init($url);
        curl_setopt($curl, CURLOPT_HEADER, 0); // 过滤HTTP头
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);// 显示输出结果
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);//SSL证书认证
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);//严格认证
        curl_setopt($curl, CURLOPT_CAINFO, $cacert_url);//证书地址
        $responseText = curl_exec($curl);
        //var_dump( curl_error($curl) );//如果执行curl过程中出现异常,可打开此开关,以便查看异常内容
        curl_close($curl);

        return $responseText;
    }

}

三、订单支付

(1)订单显示和异步处理代码:

<?php
/**
 * Created by PhpStorm.
 * User: Tinywan
 * Date: 2016/9/16
 * Time: 10:44
 */
namespace Pay\Controller;

use Think\Controller;

class IndexController extends Controller
{
    public function index()
    {
        $map['id'] = 7;
        $info = M('alipay')->where($map)->find();
        $this->assign('info', $info);
        $this->display();
        //echo($form);
    }

    public function payTo()
    {
        $data['subject'] = $_POST['subject'];
        $data['out_trade_no'] = $_POST['out_trade_no'];
        $data['total_fee'] = $_POST['total_fee'];
        $res = D('Common/Pay')->alipay($data);
        $this->ajaxReturn($res);
    }
    

}

(2)订单显示和异步请求代码:

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>购物车</title>
<script src="http://apps.bdimg.com/libs/jquery/1.6.4/jquery.min.js" type="text/javascript"></script>
</head> 
<body>
<style type="text/css">
*{margin:0px;padding:0px;border:0px; font-size:12px;color:#333; font-family:微软雅黑;}
ul li{ list-style:none}
a{ text-decoration:none;}
a:hover{ color:#e46432;}
body{margin:auto;;overflow-x:hidden;}


/*****购物车*********/
.gwc{ width:950px;overflow:hidden;}
.gwc_tb1{ width:100%; border-top:5px solid #48b9e5; background:#d0e7fa; height:38px; margin-top:20px; overflow:hidden;}
.tb1_td1{ width:35px; text-align:center;}
.tb1_td3{ width:290px; text-align:center;}
.tb1_td4{ width:260px; text-align:center;}
.tb1_td5{ width:115px; text-align:center;}
.tb1_td6{ width:135px; text-align:center;}
.tb1_td7{ text-align:center;}


.gwc_tb2{ width:100%; margin-top:20px; background:#eef6ff; border:1px solid #e5e5e5; padding-top:20px; padding-bottom:20px;}
.tb2_td1{ width:60px; text-align:center;}
.tb2_td2{ width:100px; text-align:center;}
.tb2_td2 img{ width:96px; height:96px; border:2px solid #c9c6c7;}
.tb2_td3{ width:170px; padding-left:12px; padding-right:18px;}
.tb2_td3 a{ font-size:14px; line-height:22px;}

.gwc_tb3{ width:100%; border:1px solid #d2d2d2; background:#e7e7e7; height:46px; margin-top:20px; }
.gwc_tb3 tr td{font-size:14px;}
.tb3_td2{ width:100px;text-align:center;}
.tb3_td2 span{ color:#ff5500;font-size:14px; font-weight:bold; padding-left:5px; padding-right:5px; }
.tb3_td3{ width:220px;text-align:center;}
.tb3_td3 span{ font-size:18px; font-weight:bold;}
.tb3_td4{ width:110px;text-align:center;}
.jz2{ width:100px; height:46px; line-height:46px; text-align:center; font-size:18px; color:#fff; background:#ee0000; display:block; float:right;}
#jz1{font-size:18px;}
</style>
<div class="gwc" style=" margin:auto;">
    <table cellpadding="0" cellspacing="0" class="gwc_tb1">
        <tr>
            <td class="tb1_td3">商品</td>
            <td class="tb1_td4">商品信息</td>
            <td class="tb1_td5">数量</td>
            <td class="tb1_td6">单价</td>
            <td class="tb1_td7">操作</td>
        </tr>
    </table>

    <table cellpadding="0" cellspacing="0" class="gwc_tb2">
        <tr>
            <td class="tb2_td2"></td>
            <td class="tb2_td3"><a href="#">苹果手机</a></td>
            <td class="tb1_td4">{$info.subject}</td>
            <td class="tb1_td5">
                <input id="min1" name=""  style=" width:20px; height:18px;border:1px solid #ccc;" type="button" value="-" />
                <input id="text_box1" name="" type="text" value="1" style=" width:30px; text-align:center; border:1px solid #ccc;" />
                <input id="add1" name="" style=" width:20px; height:18px;border:1px solid #ccc;" type="button" value="+" />
            </td>
            <td class="tb1_td6"><label id="total1" class="tot" style="color:#ff5500;font-size:14px; font-weight:bold;">{$info.total_fee}</label></td>
            <td class="tb1_td7"><a href="#">删除</a></td>
        </tr>
    </table>
    <table cellpadding="0" cellspacing="0" class="gwc_tb3">
        <tr>
            <td id="subject" style="display:none">{$info.subject}</td>
            <td id="out_trade_no"  style="display:none">{$info.out_trade_no}</td>
            <td class="tb3_td3">合计(不含运费):<span></span><span id="total_fee">{$info.total_fee}</span></td>
            <td class="tb3_td4"><a href="#" style=" display:block;" onclick="payto()"  class="jz2" id="jz2">结算</a></td>
        </tr>
    </table>

</div>
<div id="form" style="display:none"></div>
<script type='text/javascript'>
    var $URL = "__ROOT__/Pay/Index/";
    function payto(){
    $.ajax({
         type: "post",
         url: $URL+'payTo',
         data: {subject:$("#subject").html(), out_trade_no:$("#out_trade_no").html(),total_fee:$("#total_fee").html()},
         dataType: "json",
         success: function(data){
                     $('#form').html(data);
                  }
    });
}
</script>
</body>
</html>

四、同步通知和异步通知(已经做了是否支付宝发送过来的信息)

(1)notify_url()方法:【异步方法】

       说明:应该是每3秒异步请求或说刷新一次。里面主要做:比如更改订单状态(如果验证成功,几把本地订单设置为已支付,包括发送短信等操作)

(2)return_url()方法:【同步方法】

       说明:支付成功提示后跳转回去的页面,就是finish页面。建议这里只做提示用,因为如果想在这里做业务逻辑的话做法不当,因为如果这个页面跳转是有个时间段的,需要几秒钟,如果支付成功后客户在还没有跳转前把页面关闭了,那么对应的业务逻辑就没法实现。业务逻辑应该都是放在notify_url()方法中实现比较好的(*^__^*) 嘻嘻……。

<?php
/**
 * Created by PhpStorm.
 * User: Tinywan
 * Date: 2016/9/16
 * Time: 10:44
 */
namespace Pay\Controller;
use Think\Controller;
class NotifyController extends Controller {
    //同步通知
    public function return_url(){
        $verify = D('Common/Pay')->verifyReturn();
        if($verify){
            $out_trade_no = $_GET['out_trade_no'];
            $trade_no = $_GET['trade_no'];
            $trade_status = $_GET['trade_status'];
            if($trade_status == 'TRADE_FINISHED' || $trade_status == 'TRADE_SUCCESS') {
                $map['out_trade_no'] = $out_trade_no;
                $data['trade_no'] = $trade_no;
                $res = M('alipay')->where($map)->save($data);
                if($res !== false){
                    echo 'success,return_url'.$res;
                }
            }
        }

    }

    //异步通知
    public function notify_url(){
        $verify = D('Common/Pay')->verifyNotify();
        if($verify){
            $out_trade_no = $_POST['out_trade_no'];
            $trade_no = $_POST['trade_no'];
            $trade_status = $_POST['trade_status'];
            if($trade_status == 'TRADE_FINISHED' || $trade_status == 'TRADE_SUCCESS') {
                $map['out_trade_no'] = $out_trade_no;
                $data['trade_no'] = $trade_no;
                $res = M('alipay')->where($map)->save($data);
                if($res !== false){
                    echo 'success notify_url--$res';
                }
            }
        }
    }
}

五、验证是否是支付宝发来的通知

 $verify = D('Common/Pay')->verifyNotify();   // 返回值:true 正确,false:错误
 /**
     * 针对return_url验证消息是否是支付宝发出的合法消息
     * @return 验证结果
     */
    public function verifyReturn()
    {

        if (empty($_GET)) {//判断POST来的数组是否为空
            return false;
        } else {
            //生成签名结果
            $isSign = $this->getSignVeryfy($_GET, $_GET["sign"]);
            //获取支付宝远程服务器ATN结果(验证是否是支付宝发来的消息)
            $responseTxt = 'false';
            if (!empty($_GET["notify_id"])) {
                $responseTxt = $this->getResponse($_GET["notify_id"]);
            }

            //写日志记录
            //if ($isSign) {
            //    $isSignStr = 'true';
            //}
            //else {
            //    $isSignStr = 'false';
            //}
            //$log_text = "responseTxt=".$responseTxt."\n return_url_log:isSign=".$isSignStr.",";
            //$log_text = $log_text.createLinkString($_GET);
            //logResult($log_text);

            //验证
            //$responsetTxt的结果不是true,与服务器设置问题、合作身份者ID、notify_id一分钟失效有关
            //isSign的结果不是true,与安全校验码、请求时的参数格式(如:带自定义参数等)、编码格式有关
            if (preg_match("/true$/i", $responseTxt) && $isSign) {
                return true;
            } else {
                return false;
            }
        }
    }

六、支付宝有密批量退款

处理类:

<?php
namespace Common\Model;
use Think\Model;
class RefundModel{
    public $alipay_gateway_new = 'https://mapi.alipay.com/gateway.do?';
    private $keys = 'k2zu8i7h9enbkafsvtfrgdcuy1n273qn';
    private $partner  = '2088802807619823';
    private $cacert = './cert/cacert.pem';
    private $private_key_path = './cert/rsa_private_key.pem';

    //退款请求
    function refund($data){
        //构造要请求的参数数组,无需改动
        $parameter = array(
                "service" => 'refund_fastpay_by_platform_pwd',
                "partner" => $this->partner,
                "seller_email"    => 'mzhsoft@126.com',
                "refund_date"    => date('Y-m-d H:i:s',time()),
                "batch_no"    => $data['batch_no'],
                "batch_num"    => $data['batch_num'],
                "detail_data"    => $data['detail_data'],
                "_input_charset"    => trim(strtolower('utf-8'))
                
        );
        $res = $this->buildRequestForm($parameter, 'get', '确认');
        return $res ;
    }
    /**
     * 生成签名结果
     * @param $para_sort 已排序要签名的数组
     * return 签名结果字符串
     */
    function buildRequestMysign($para_sort) {
        //把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
        $prestr = $this->createLinkstring($para_sort);
        
        $mysign = "";
        switch (strtoupper(trim('RSA'))) {
            case "RSA" :
                $mysign = $this->rsaSign($prestr, $this->private_key_path);
                break;
            default :
                $mysign = "";
        }
        
        return $mysign;
    }

    /**
     * 生成要请求给支付宝的参数数组
     * @param $para_temp 请求前的参数数组
     * @return 要请求的参数数组
     */
    function buildRequestPara($para_temp) {
        //除去待签名参数数组中的空值和签名参数
        $para_filter = $this->paraFilter($para_temp);

        //对待签名参数数组排序
        $para_sort = $this->argSort($para_filter);

        //生成签名结果
        $mysign = $this->buildRequestMysign($para_sort);
        
        //签名结果与签名方式加入请求提交参数组中
        $para_sort['sign'] = $mysign;
        $para_sort['sign_type'] = strtoupper(trim('RSA'));
        
        return $para_sort;
    }

    /**
     * 生成要请求给支付宝的参数数组
     * @param $para_temp 请求前的参数数组
     * @return 要请求的参数数组字符串
     */
    function buildRequestParaToString($para_temp) {
        //待请求参数数组
        $para = $this->buildRequestPara($para_temp);
        
        //把参数组中所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串,并对字符串做urlencode编码
        $request_data = $this->createLinkstringUrlencode($para);
        
        return $request_data;
    }
    
    /**
     * 建立请求,以表单HTML形式构造(默认)
     * @param $para_temp 请求参数数组
     * @param $method 提交方式。两个值可选:post、get
     * @param $button_name 确认按钮显示文字
     * @return 提交表单HTML文本
     */
    function buildRequestForm($para_temp, $method, $button_name) {
        //待请求参数数组
        $para = $this->buildRequestPara($para_temp);
        
        $sHtml = "<form id='alipaysubmit' name='alipaysubmit' action='".$this->alipay_gateway_new."_input_charset=".trim(strtolower('utf-8'))."' method='".$method."'>";
        while (list ($key, $val) = each ($para)) {
            $sHtml.= "<input type='hidden' name='".$key."' value='".$val."'/>";
        }

        //submit按钮控件请不要含有name属性
        $sHtml = $sHtml."<input type='submit' value='".$button_name."'></form>";
        
        $sHtml = $sHtml."<script>document.forms['alipaysubmit'].submit();</script>";
        
        return $sHtml;
    }
    
    /**
     * 建立请求,以模拟远程HTTP的POST请求方式构造并获取支付宝的处理结果
     * @param $para_temp 请求参数数组
     * @return 支付宝处理结果
     */
    function buildRequestHttp($para_temp) {
        $sResult = '';
        
        //待请求参数数组字符串
        $request_data = $this->buildRequestPara($para_temp);

        //远程获取数据
        $sResult = $this->getHttpResponsePOST($this->alipay_gateway_new, $this->cacert,$request_data,trim(strtolower('utf-8')));

        return $sResult;
    }
    
    /**
     * 建立请求,以模拟远程HTTP的POST请求方式构造并获取支付宝的处理结果,带文件上传功能
     * @param $para_temp 请求参数数组
     * @param $file_para_name 文件类型的参数名
     * @param $file_name 文件完整绝对路径
     * @return 支付宝返回处理结果
     */
    function buildRequestHttpInFile($para_temp, $file_para_name, $file_name) {
        
        //待请求参数数组
        $para = $this->buildRequestPara($para_temp);
        $para[$file_para_name] = "@".$file_name;
        
        //远程获取数据
        $sResult = $this->getHttpResponsePOST($this->alipay_gateway_new, $this->cacert,$para,trim(strtolower('utf-8')));

        return $sResult;
    }    
    
    
    /**
     * 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
     * @param $para 需要拼接的数组
     * return 拼接完成以后的字符串
     */
    function createLinkstring($para) {
        $arg  = "";
        while (list ($key, $val) = each ($para)) {
            $arg.=$key."=".$val."&";
        }
        //去掉最后一个&字符
        $arg = substr($arg,0,count($arg)-2);
        
        //如果存在转义字符,那么去掉转义
        if(get_magic_quotes_gpc()){$arg = stripslashes($arg);}
        
        return $arg;
    }
    /**
     * 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串,并对字符串做urlencode编码
     * @param $para 需要拼接的数组
     * return 拼接完成以后的字符串
     */
    function createLinkstringUrlencode($para) {
        $arg  = "";
        while (list ($key, $val) = each ($para)) {
            $arg.=$key."=".urlencode($val)."&";
        }
        //去掉最后一个&字符
        $arg = substr($arg,0,count($arg)-2);
        
        //如果存在转义字符,那么去掉转义
        if(get_magic_quotes_gpc()){$arg = stripslashes($arg);}
        
        return $arg;
    }
    /**
     * 除去数组中的空值和签名参数
     * @param $para 签名参数组
     * return 去掉空值与签名参数后的新签名参数组
     */
    function paraFilter($para) {
        $para_filter = array();
        while (list ($key, $val) = each ($para)) {
            if($key == "sign" || $key == "sign_type" || $val == "")continue;
            else    $para_filter[$key] = $para[$key];
        }
        return $para_filter;
    }
    /**
     * 对数组排序
     * @param $para 排序前的数组
     * return 排序后的数组
     */
    function argSort($para) {
        ksort($para);
        reset($para);
        return $para;
    }
    /**
     * 写日志,方便测试(看网站需求,也可以改成把记录存入数据库)
     * 注意:服务器需要开通fopen配置
     * @param $word 要写入日志里的文本内容 默认值:空值
     */
    function logResult($word='') {
        $fp = fopen("log.txt","a");
        flock($fp, LOCK_EX) ;
        fwrite($fp,"执行日期:".strftime("%Y%m%d%H%M%S",time())."\n".$word."\n");
        flock($fp, LOCK_UN);
        fclose($fp);
    }

    /**
     * 远程获取数据,POST模式
     * 注意:
     * 1.使用Crul需要修改服务器中php.ini文件的设置,找到php_curl.dll去掉前面的";"就行了
     * 2.文件夹中cacert.pem是SSL证书请保证其路径有效,目前默认路径是:getcwd().'\\cacert.pem'
     * @param $url 指定URL完整路径地址
     * @param $cacert_url 指定当前工作目录绝对路径
     * @param $para 请求的数据
     * @param $input_charset 编码格式。默认值:空值
     * return 远程输出的数据
     */
    function getHttpResponsePOST($url, $cacert_url, $para, $input_charset = '') {

        if (trim($input_charset) != '') {
            $url = $url."_input_charset=".$input_charset;
        }
        $curl = curl_init($url);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);//SSL证书认证
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);//严格认证
        curl_setopt($curl, CURLOPT_CAINFO,$cacert_url);//证书地址
        curl_setopt($curl, CURLOPT_HEADER, 0 ); // 过滤HTTP头
        curl_setopt($curl,CURLOPT_RETURNTRANSFER, 1);// 显示输出结果
        curl_setopt($curl,CURLOPT_POST,true); // post传输数据
        curl_setopt($curl,CURLOPT_POSTFIELDS,$para);// post传输数据
        $responseText = curl_exec($curl);
        //var_dump( curl_error($curl) );//如果执行curl过程中出现异常,可打开此开关,以便查看异常内容
        curl_close($curl);
        
        return $responseText;
    }

    /**
     * 远程获取数据,GET模式
     * 注意:
     * 1.使用Crul需要修改服务器中php.ini文件的设置,找到php_curl.dll去掉前面的";"就行了
     * 2.文件夹中cacert.pem是SSL证书请保证其路径有效,目前默认路径是:getcwd().'\\cacert.pem'
     * @param $url 指定URL完整路径地址
     * @param $cacert_url 指定当前工作目录绝对路径
     * return 远程输出的数据
     */
    function getHttpResponseGET($url,$cacert_url) {
        $curl = curl_init($url);
        curl_setopt($curl, CURLOPT_HEADER, 0 ); // 过滤HTTP头
        curl_setopt($curl,CURLOPT_RETURNTRANSFER, 1);// 显示输出结果
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);//SSL证书认证
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);//严格认证
        curl_setopt($curl, CURLOPT_CAINFO,$cacert_url);//证书地址
        $responseText = curl_exec($curl);
        //var_dump( curl_error($curl) );//如果执行curl过程中出现异常,可打开此开关,以便查看异常内容
        curl_close($curl);
        
        return $responseText;
    }

    /**
     * 实现多种字符编码方式
     * @param $input 需要编码的字符串
     * @param $_output_charset 输出的编码格式
     * @param $_input_charset 输入的编码格式
     * return 编码后的字符串
     */
    function charsetEncode($input,$_output_charset ,$_input_charset) {
        $output = "";
        if(!isset($_output_charset) )$_output_charset  = $_input_charset;
        if($_input_charset == $_output_charset || $input ==null ) {
            $output = $input;
        } elseif (function_exists("mb_convert_encoding")) {
            $output = mb_convert_encoding($input,$_output_charset,$_input_charset);
        } elseif(function_exists("iconv")) {
            $output = iconv($_input_charset,$_output_charset,$input);
        } else die("sorry, you have no libs support for charset change.");
        return $output;
    }
    /**
     * 实现多种字符解码方式
     * @param $input 需要解码的字符串
     * @param $_output_charset 输出的解码格式
     * @param $_input_charset 输入的解码格式
     * return 解码后的字符串
     */
    function charsetDecode($input,$_input_charset ,$_output_charset) {
        $output = "";
        if(!isset($_input_charset) )$_input_charset  = $_input_charset ;
        if($_input_charset == $_output_charset || $input ==null ) {
            $output = $input;
        } elseif (function_exists("mb_convert_encoding")) {
            $output = mb_convert_encoding($input,$_output_charset,$_input_charset);
        } elseif(function_exists("iconv")) {
            $output = iconv($_input_charset,$_output_charset,$input);
        } else die("sorry, you have no libs support for charset changes.");
        return $output;
    }    
    
    
    
    
    /**
     * RSA签名
     * @param $data 待签名数据
     * @param $private_key_path 商户私钥文件路径
     * return 签名结果
     */
    function rsaSign($data, $private_key_path) {
        $priKey = file_get_contents($private_key_path);
        $res = openssl_get_privatekey($priKey);
        openssl_sign($data, $sign, $res);
        openssl_free_key($res);
        //base64编码
        $sign = base64_encode($sign);
        return $sign;
    }

    /**
     * RSA验签
     * @param $data 待签名数据
     * @param $ali_public_key_path 支付宝的公钥文件路径
     * @param $sign 要校对的的签名结果
     * return 验证结果
     */
    function rsaVerify($data, $ali_public_key_path, $sign)  {
        $pubKey = file_get_contents($ali_public_key_path);
        $res = openssl_get_publickey($pubKey);
        $result = (bool)openssl_verify($data, base64_decode($sign), $res);
        openssl_free_key($res);    
        return $result;
    }

    /**
     * RSA解密
     * @param $content 需要解密的内容,密文
     * @param $private_key_path 商户私钥文件路径
     * return 解密后内容,明文
     */
    function rsaDecrypt($content, $private_key_path) {
        $priKey = file_get_contents($private_key_path);
        $res = openssl_get_privatekey($priKey);
        //用base64将内容还原成二进制
        $content = base64_decode($content);
        //把需要解密的内容,按128位拆开解密
        $result  = '';
        for($i = 0; $i < strlen($content)/128; $i++  ) {
            $data = substr($content, $i * 128, 128);
            openssl_private_decrypt($data, $decrypt, $res);
            $result .= $decrypt;
        }
        openssl_free_key($res);
        return $result;
    }    
    
}

具体方法:

//退款
    public function refund(){
        header("Content-type:text/html;charset=utf-8");
        $data['batch_no'] = date('YmdHis').'MZ01';
        $data['batch_num'] = 1;  
        $data['detail_data'] = '2016091621001004480242264394^0.01^协商退款01';
        $res = D('Common/Refund')->refund($data);
        //var_dump($res); 这个用在测试的时候,如果打印的是一个表单的话表示错误
        echo($res);
    }

六、RSA商户公钥与支付宝公钥(待定....)

具体详细代码和说明文档见Github:https://github.com/Tinywan/ThinkPhpStudy

 

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
文档小喇叭 | 支付宝小程序开发常见问题 FAQ
小程序开发疑难杂症解决方案大盘点!
6879 0
node+mysql+express接口开发数据库连接池
node+mysql+express接口开发数据库连接池
60 0
支付宝接口申请
申请支付宝接口是不需要花钱的,收费标准是按你成交额多的少,按比例分成的。   申请支付宝接口的步骤: 登录支付宝官网:https://www.alipay.com/ 点击右上角  支付宝首页   进入商家服务:   进入 产品商店:           选择即使到账接...
908 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
29151 0
【开发课堂】支付宝小程序视图层数据绑定和渲染的能力
1.数据绑定:使用 Mustache 语法(双大括号)将变量包起来,可以作用于各种场合。  a. 作用于内容  {{message}}  Page({ data: {message: 'Hello alipay!'}}) b.
766 0
AVEVA PDMS数据接口开发
AVEVA PDMS数据接口的开发 eryar@163.com          摘要:本文简要介绍AVEVA Plant/PDMS数据接口的开发方式。        关键字:AVEVA Plant、PDMS、DARs、二次开发、数据接口   一、概述 Overview AVEVA提供了三种二次开发的方式:DAR(Data Access Routines)、C#、PML(AVEVA Programmable Macro Language)。
2034 0
阿里大鱼短信发送接口开发
一、 API接口介绍 alibaba.aliqin.fc.sms.num.send (短信发送) 向指定手机号码发送模板短信,模板内可设置部分变量。使用前需要在阿里大于管理中心添加短信签名与短信模板。
11376 0
+关注
244
文章
1
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载