支付宝手机网站支付

对于对接第三方支付,只想说文档很重要,漏了一步,后期排错真的很费时间。好好读文档,结合自己的项目优化官方给的SDK例子。也可以拿官方的demo在自己服务器先能成功执行完整个流程后,再结合自己项目从sdk抽出自己想要的代码

在开发过程中,对接第三方为保证数据的可靠以及安全,都会对数据进行加签以及验签。
加签会涉及到你在第三方平台设置的公钥(相当你的私钥),当你把数据传给第三方平台的时候,第三方平台会验签,通过后会同步或者异步返回数据给你
验签会涉及到第三方平台公钥(相当于第三方平台加签用的私钥),当你接受到第三方平台传回的数据后进行验签来保证数据可靠,验签成功后响应对应的数据给第三方平台

signtype = 'RSA2';
        $this->charset = 'utf8';
        $this->method = 'alipay.trade.wap.pay';
    }

    /**
     * 支付宝官方-H5支付(生成支付链接或者表单)
     *这里是支付链接
     */
    public function doPayInit($payParams)
    {
        $orderId = $payParams['orderId'];
        $totalAmount = $payParams['money'];
        //请求参数
        $requestConfigs = array(
            'out_trade_no' => $orderId,
            'product_code' => 'QUICK_WAP_WAY',
            'total_amount' => $totalAmount, //单位 元
            'subject' => 'test', //订单标题
        );
        $commonConfigs = array(
            //公共参数
            'app_id' => $this->appId,
            'method' => $this->method, //接口名称
            'format' => 'JSON',
            'charset' => $this->charset,
            'sign_type' => $this->signtype,
            'timestamp' => date('Y-m-d H:i:s'),
            'version' => '1.0',
            'notify_url' => '你的异步回调地址',
            'biz_content' => json_encode($requestConfigs, JSON_UNESCAPED_UNICODE),
        );
        $commonConfigs["sign"] = $this->generateSign($commonConfigs);
        //value做urlencode
        $preString = $this->getSignContentUrlencode($commonConfigs);
        //拼接GET请求串
        $requestUrl = $this->gatewayUrl."?".$preString;
        $res = [];
        $res['pay_info'] = $requestUrl;
        return $res;
    }

    /**
     * 支付宝官方-H5支付回调地址
     */
    public function doPayCb($request)
    {
        $retResponse = $_REQUEST;
        if (empty($retResponse)) {
            throw new Exception('fail');
        }
        // 其验签步骤为: 
        // 第一步: 在通知返回参数列表中,除去sign、sign_type两个参数外,凡是通知返回回来的参数皆是待验签的参数。 
        // 第二步: 将剩下参数进行url_decode, 然后进行字典排序,组成字符串,得到待签名字符串: 
        // 第三步: 将签名参数(sign)使用base64解码为字节码串。 
        // 第四步: 使用RSA的验签方法,通过签名字符串、签名参数(经过base64解码)及支付宝公钥验证签名。 
        // 第五步:在步骤四验证签名正确后,必须再严格按照如下描述校验通知数据的正确性。 
        $result = $this->rsaCheckV1($retResponse);
        if (!in_array( $retResponse['trade_status'] , [ 'TRADE_FINISHED', 'TRADE_SUCCESS' ] )) {
            throw new Exception('fail');
        }
        //验签成功处理你自己的逻辑,更新订单状态等
        // 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号; 
        // 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额); 
        // 3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email); 
        // 4、验证app_id是否为该商户本身。 
        return 'success';//最后一定返回success,否则支付宝会继续回调
    }

    //此方法对value做urlencode
    public function getSignContentUrlencode($params) {
        ksort($params);
        $stringToBeSigned = "";
        $i = 0;
        foreach ($params as $k => $v) {
            if (false === $this->checkEmpty($v) && "@" != substr($v, 0, 1)) {

                // 转换成目标字符集
                $v = $this->characet($v, $this->charset);

                if ($i == 0) {
                    $stringToBeSigned .= "$k" . "=" . urlencode($v);
                } else {
                    $stringToBeSigned .= "&" . "$k" . "=" . urlencode($v);
                }
                $i++;
            }
        }
        unset ($k, $v);
        return $stringToBeSigned;
    }

    /** rsaCheckV1 & rsaCheckV2
     *  验证签名
     *  在使用本方法前,必须初始化AopClient且传入公钥参数。
     *  公钥是否是读取字符串还是读取文件,是根据初始化传入的值判断的。
     * */
    public function rsaCheckV1($params)
    {
        $sign = $params['sign'];
        $params['sign_type'] = null;
        $params['sign'] = null;
        return $this->verify($this->getSignContent($params), $sign);
    }

    public function verify($data, $sign)
    {
        $pubKey = $this->alipayPublicKey;
        $res = "-----BEGIN PUBLIC KEY-----\n" .
                wordwrap($pubKey, 64, "\n", true) .
                "\n-----END PUBLIC KEY-----";
        if(!$res){
            throw new Exception(Exception::ETYPE_INVALID_PAY_CALLBACK_ERROR,"支付宝RSA公钥错误。请检查公钥文件格式是否正确");
        }
        //调用openssl内置方法验签,返回bool值
        $result = FALSE;
        if ("RSA2" == $this->signtype) {
            $result = (openssl_verify($data, base64_decode($sign), $res, OPENSSL_ALGO_SHA256) === 1);
        } else {
            $result = (openssl_verify($data, base64_decode($sign), $res) === 1);
        }
        return $result;
    }

    public function getSignContent($params)
    {
        ksort($params);
        $stringToBeSigned = "";
        $i = 0;
        foreach ($params as $k => $v) {
            if (false === $this->checkEmpty($v) && "@" != substr($v, 0, 1)) {
                // 转换成目标字符集
                $v = $this->characet($v, $this->charset);
                if ($i == 0) {
                    $stringToBeSigned .= "$k" . "=" . "$v";
                } else {
                    $stringToBeSigned .= "&" . "$k" . "=" . "$v";
                }
                $i++;
            }
        }
        unset($k, $v);
        return $stringToBeSigned;
    }

    /**
     * 校验$value是否非空
     *  if not set ,return true;
     *    if is null , return true;
     * */
    protected function checkEmpty($value)
    {
        if (!isset($value))
            return true;
        if ($value === null)
            return true;
        if (trim($value) === "")
            return true;
        return false;
    }

    /**
     * 转换字符集编码
     * @param $data
     * @param $targetCharset
     * @return string
     */
    public function characet($data, $targetCharset)
    {
        if (!empty($data)) {
            $fileType = $this->charset;
            if (strcasecmp($fileType, $targetCharset) != 0) {
                $data = mb_convert_encoding($data, $targetCharset, $fileType);
            }
        }
        return $data;
    }

    public function generateSign($params)
    {
        return $this->sign($this->getSignContent($params));
    }

    //加签
    protected function sign($data)
    {
        $priKey = $this->rsaPrivateKey;
        $res = "-----BEGIN RSA PRIVATE KEY-----\n" .
                wordwrap($priKey, 64, "\n", true) .
                "\n-----END RSA PRIVATE KEY-----";
        if(!$res){
            throw new Exception(Exception::ETYPE_INVALID_PAY_CALLBACK_ERROR,"支付宝RSA私钥错误。请检查私钥文件格式是否正确");
        }
        if ("RSA2" == $this->signtype) {
            openssl_sign($data, $sign, $res, version_compare(PHP_VERSION, '5.4.0', '<') ? SHA256 : OPENSSL_ALGO_SHA256); //OPENSSL_ALGO_SHA256是php5.4.8以上版本才支持
        } else {
            openssl_sign($data, $sign, $res);
        }
        $sign = base64_encode($sign);
        return $sign;
    }
}

你可能感兴趣的:(php)