PHP 实现支付宝 “(单笔转账)转账到支付宝账号”

目录

前言

一、服务层

二、控制层

1.控制层

2.接口返回参数实例


前言

单笔转账接口 | 网页&移动应用

准备:

1:应用的证书(证书申请和配置可以参考我另一篇分享“支付宝 ‘应用证书配置’ ”)

2:配置证书后注意公共参数中 私钥值 是csr生成后的私钥

3:需要接口能力已开通


一、服务层

<?php


class AlipayMethod
{

    // 私钥值
    const RSAPRIVATEKEY  = "配置证书后私钥是在CSR生成中的私钥";
    // 网关地址
    const SERVERURL      = "https://openapi.alipay.com/gateway.do";
    // 开放平台上创建的应用的ID                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
    const APPID          = "app_id";
    // 字符串编码,推荐:utf-8                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
    const CHARSET        = "UTF-8";
    // 签名算法类型,推荐:RSA2                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
    const SIGNTYPE       = "RSA2"; 
    // 报文格式,推荐:json                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
    const FORMAT         = "json";
    // 敏感信息对称加密算法密钥                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
    const ENCRYPYKEY     = "";
    // 调用的接口版本,固定为:1.0                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
    const VERSION        = "1.0"; 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
    // 接口方法
    const METHOD_TRANSFER    = "alipay.fund.trans.uni.transfer";  // 转账到支付宝账号
    const METHOD_OAUTH_TOKEN = "alipay.system.oauth.token";       // 换取授权访问令牌
    const TOKE_NGRANT_TYPE   = "authorization_code";              // 访问令牌授权方式
    // 证书
    const ALIPAY_ROOT_CERT       = "你的路径";                     // 支付宝根证书路径                         
    const ALIPAY_CERT_PUBLIC_KEY = "你的路径";                     // 应用公钥证书路径


    /**
     * (单笔转账接口)转账到支付宝账号
     * @param  sOid             订单号
     * @param  ALIPAY_USER_ID   支付宝用户UID
     * @param  nMoney           转款金额
     * @return array
     */
    public function onPaymentByAlipay($alipayUserId, $nMoney)
    {
        // 转账金额:保留两位小数
        $transAmount = number_format($nMoney, 2);
        // $transAmount = number_format("1", 2);
        // 请求参数
        $biz_content = [
             // 商家侧唯一订单号,由商家自定义
            "out_biz_no"   => "测试订单号".time(),   
            // 订单总金额,单位为元,不支持千位分隔符,精确到小数点后两位             
            "trans_amount" => $transAmount,
            // 固定为 TRANS_ACCOUNT_NO_PWD          
            "product_code" => "TRANS_ACCOUNT_NO_PWD",
            // 固定为 DIRECT_TRANSFER
            "biz_scene"    => "DIRECT_TRANSFER",
            // 转账业务的标题:收款方会收到     
            "order_title"  => "测试支付",
            // 收款方信息              
            "payee_info"   => [  
                // 参与方的标识 ID:ALIPAY_USER_ID(UID) ALIPAY_LOGON_ID(支付宝登录号)                    
                "identity"      => $alipayUserId,
                "identity_type" => "ALIPAY_USER_ID",   
                // "name"          => "真实姓名",          //  identity_type = ALIPAY_LOGON_ID name必填
            ]
        ];

        // 请求参数        
        $params = [
            "app_id"              => self::APPID,
            "method"              => self::METHOD_TRANSFER,
            "format"              => self::FORMAT,
            "charset"             => self::CHARSET,
            "sign_type"           => self::SIGNTYPE,
            "timestamp"           => date('Y-m-d H:i:s'),
            "version"             => self::VERSION,
            "biz_content"         => json_encode($biz_content, 256),
            "alipay_root_cert_sn" => $this->getRootCertSN(dirname(__FILE__) . self::ALIPAY_ROOT_CERT),     // 支付宝根证书路径
            "app_cert_sn"         => $this->getCertSN(dirname(__FILE__) . self::ALIPAY_CERT_PUBLIC_KEY),   // 应用公钥证书路径
        ];
        // 签名        
        $params["sign"]    = $this->generateSign($params, $params['sign_type']);
        // 发起请求
        $result = $this->curlPost('https://openapi.alipay.com/gateway.do', $params);
        // 结果数组化
        $result = json_decode($result, true);

        return $result;
    }


    /**
     * 换取授权访问令牌
     * @param authCode  授权code 
     * @return array
     */
    public function getAccessToken($authCode)
    {
        //公共参数
        $params = array(
            "app_id"              => self::APPID,
            "method"              => self::METHOD_OAUTH_TOKEN,
            "format"              => self::FORMAT,
            "charset"             => self::CHARSET,
            "sign_type"           => self::SIGNTYPE,
            "timestamp"           => date("Y-m-d H:i:s"),
            "version"             => self::VERSION,
            "grant_type"          => self::TOKE_NGRANT_TYPE,
            "code"                => $authCode,
            "alipay_root_cert_sn" => $this->getRootCertSN(dirname(__FILE__) . self::ALIPAY_ROOT_CERT),     // 支付宝根证书路径
            "app_cert_sn"         => $this->getCertSN(dirname(__FILE__) . self::ALIPAY_CERT_PUBLIC_KEY),   // 应用公钥证书路径
        );
        // 签名       
        $params["sign"] = $this->generateSign($params, $params['sign_type']);
        // 发起请求
        $result = $this->curlPost("https://openapi.alipay.com/gateway.do?charset=" . self::CHARSET, $params);
        // 结果数组化
        $result = json_decode($result, true);

        return $result;
    }



    /**-------------------------------我是分割线---------------------------------- */


    /**
     * 加签
     * @param $params
     * @param string $signType
     * @return mixed
     */
    public function generateSign($params, $signType)
    {
        return $this->sign($this->getSignContent($params), $signType);
    }

    protected function sign($data, $signType)
    {
        $priKey = self::RSAPRIVATEKEY;
        $res = "-----BEGIN RSA PRIVATE KEY-----\n" .
            wordwrap($priKey, 64, "\n", true) .
            "\n-----END RSA PRIVATE KEY-----";
        ($res) or die('您使用的私钥格式错误,请检查RSA私钥配置');
        if ($signType == "RSA2") {
            openssl_sign($data, $sign, $res, OPENSSL_ALGO_SHA256);
            // OPENSSL_ALGO_SHA256是php5.4.8以上版本才支持
            // openssl_sign($data, $sign, $res, version_compare(PHP_VERSION, '5.4.0', '<') ? SHA256 : OPENSSL_ALGO_SHA256);
        } else {
            openssl_sign($data, $sign, $res);
        }
        $sign = base64_encode($sign);
        return $sign;
    }

    /**
     * 校验$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;
    }

    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, self::CHARSET);
                if ($i == 0) {
                    $stringToBeSigned .= "$k" . "=" . "$v";
                } else {
                    $stringToBeSigned .= "&" . "$k" . "=" . "$v";
                }
                $i++;
            }
        }
        unset($k, $v);
        return $stringToBeSigned;
    }

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


    /**
     * 发送curl请求
     * @param $url
     * @param null $postFields
     * @return bool|string
     * @throws Exception
     */
    protected function curlPost($url, $postFields = null)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_FAILONERROR, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
        $postBodyString = "";
        $encodeArray = array();
        $postMultipart = false;

        if (is_array($postFields) && 0 < count($postFields)) {
            foreach ($postFields as $k => $v) {
                if ("@" != substr($v, 0, 1)) //判断是不是文件上传
                {
                    $postBodyString .= "$k=" . urlencode($this->characet($v, self::CHARSET)) . "&";
                    $encodeArray[$k] = $this->characet($v, self::CHARSET);
                } else //文件上传用multipart/form-data,否则用www-form-urlencoded
                {
                    $postMultipart = true;
                    $encodeArray[$k] = new \CURLFile(substr($v, 1));
                }
            }
            unset($k, $v);
            curl_setopt($ch, CURLOPT_POST, true);
            if ($postMultipart) {
                curl_setopt($ch, CURLOPT_POSTFIELDS, $encodeArray);
            } else {
                curl_setopt($ch, CURLOPT_POSTFIELDS, substr($postBodyString, 0, -1));
            }
        }

        if (!$postMultipart) {
            $headers = array('content-type: application/x-www-form-urlencoded;charset=' . self::CHARSET);
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        }

        $reponse = curl_exec($ch);
        if (curl_errno($ch)) {
            throw new \Exception(curl_error($ch), 0);
        } else {
            $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            if (200 !== $httpStatusCode) {
                throw new \Exception($reponse, $httpStatusCode);
            }
        }
        curl_close($ch);
        return $reponse;
    }

    /**
     * 从证书中提取序列号
     * @param $cert
     * @return string
     */
    public function getCertSN($certPath)
    {
        $cert = file_get_contents($certPath);
        $ssl = openssl_x509_parse($cert);
        $SN = md5($this->array2string(array_reverse($ssl['issuer'])) . $ssl['serialNumber']);
        return $SN;
    }

    /**
     * 提取根证书序列号
     * @param $cert  根证书
     * @return string|null
     */
    public function getRootCertSN($certPath)
    {
        $cert = file_get_contents($certPath);
        $this->alipayRootCertContent = $cert;
        $array = explode("-----END CERTIFICATE-----", $cert);
        $SN = null;
        for ($i = 0; $i < count($array) - 1; $i++) {
            $ssl[$i] = openssl_x509_parse($array[$i] . "-----END CERTIFICATE-----");
            if (strpos($ssl[$i]['serialNumber'], '0x') === 0) {
                $ssl[$i]['serialNumber'] = $this->hex2dec($ssl[$i]['serialNumberHex']);
            }
            if ($ssl[$i]['signatureTypeLN'] == "sha1WithRSAEncryption" || $ssl[$i]['signatureTypeLN'] == "sha256WithRSAEncryption") {
                if ($SN == null) {
                    $SN = md5($this->array2string(array_reverse($ssl[$i]['issuer'])) . $ssl[$i]['serialNumber']);
                } else {

                    $SN = $SN . "_" . md5($this->array2string(array_reverse($ssl[$i]['issuer'])) . $ssl[$i]['serialNumber']);
                }
            }
        }
        return $SN;
    }

    /**
     * 0x转高精度数字
     * @param $hex
     * @return int|string
     */
    function hex2dec($hex)
    {
        $dec = 0;
        $len = strlen($hex);
        for ($i = 1; $i <= $len; $i++) {
            $dec = bcadd($dec, bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i))));
        }
        return $dec;
    }

    protected function array2string($array)
    {
        $string = [];
        if ($array && is_array($array)) {
            foreach ($array as $key => $value) {
                $string[] = $key . '=' . $value;
            }
        }
        return implode(',', $string);
    }
}

二、控制层

1.控制层

    /**
     * 换取授权访问令牌
     * @param authCode  授权code  
     * @return array
     */
    public function getAccessToken($authCode,$nMoney)
    {
        $cAlipayMethod = new AlipayMethod();
        // 换取授权访问令牌(获取返回中code对应的user_id)
        $result        = $cAlipayMethod->getAccessToken($authCode);
        if (!isset($result["alipay_system_oauth_token_response"])) {
            // 换取授权访问令牌失败
            return json(["code" => 0, "data" => $result]);
        } else {
            // 获取用户唯一标识
            $alipayUserId = $result["alipay_system_oauth_token_response"]["user_id"];
            // 请求转账
            $result       = $cAlipayMethod->onPaymentByAlipay($alipayUserId, $nMoney);
         if ($result["alipay_fund_trans_uni_transfer_response"]["code"] == "10000") {

                dump("转账成功")
                // 业务逻辑

            } else {

                dump("转账失败")
                // 业务逻辑
            }
        }
    }

2.接口返回参数实例

alipay.system.oauth.token(换取授权访问令牌):
成功:
{
   "alipay_system_oauth_token_response": {
      "access_token": "....",
      "alipay_user_id": "....",
      "auth_start": "2022-11-19 11:05:18",
      "expires_in": ....,
      "re_expires_in": ....,
      "refresh_token": "....",
      "user_id": "...."                       // 用户标识:是我们需要的
   },
   "alipay_cert_sn": "....",
   "sign": "...."
}
错误:
{
   "error_response": {
      "code": "40002",
      "msg": "Invalid Arguments",
      "sub_code": "isv.code-invalid",
      "sub_msg": "授权码code无效"
   },
   "alipay_cert_sn": "....",
   "sign": "...."
}


alipay.fund.trans.uni.transfer(单笔转账接口)转账到支付宝账号:
成功:
{
   "alipay_fund_trans_uni_transfer_response": {
      "code": "10000",
      "msg": "Success",
      "order_id": "....",
      "out_biz_no": "....",
      "pay_fund_order_id": "....",
      "status": "SUCCESS",
      "trans_date": "2022-11-18 13:45:02"
   },
   "alipay_cert_sn": "....",
   "sign": "...."
}

异常:
{
   "alipay_fund_trans_uni_transfer_response": {
      "code": "40004",
      "msg": "Business Failed",
      "sub_code": "INVALID_PARAMETER",
      "sub_msg": "参数有误参数payee_info.identity格式非法"
   },
   "alipay_cert_sn": "....",
   "sign": "...."
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是支付宝实现单笔转账接口的代码示例(使用的是PHP语言): ```php <?php // 引入支付宝 SDK require_once 'aliyun-php-sdk-core/Config.php'; require_once 'aop/AopClient.php'; // 初始化 AopClient $aop = new AopClient(); $aop->gatewayUrl = 'https://openapi.alipay.com/gateway.do'; $aop->appId = '<YOUR_APP_ID>'; $aop->rsaPrivateKey = '<YOUR_RSA_PRIVATE_KEY>'; $aop->alipayrsaPublicKey = '<ALIPAY_RSA_PUBLIC_KEY>'; $aop->format = 'json'; $aop->charset = 'UTF-8'; $aop->signType = 'RSA2'; // 创建请求对象 $request = new AlipayFundTransToaccountTransferRequest(); $request->setBizContent('{ "out_biz_no":"<YOUR_OUT_BIZ_NO>", // 商户转账唯一订单号 "payee_type":"ALIPAY_LOGONID", // 收款方账户类型 "payee_account":"<PAYEE_ACCOUNT>", // 收款方账户 "amount":"<TRANSFER_AMOUNT>", // 转账金额,单位:元 "payer_show_name":"<PAYER_SHOW_NAME>", // 付款方姓名 "payee_real_name":"<PAYEE_REAL_NAME>", // 收款方真实姓名 "remark":"<TRANSFER_REMARK>" // 转账备注 }'); // 发起 API 请求 $result = $aop->execute($request); // 处理 API 响应 if ($result && $result->alipay_fund_trans_toaccount_transfer_response->code == '10000') { // 转账成功 echo '转账成功,支付宝转账流水号:' . $result->alipay_fund_trans_toaccount_transfer_response->order_id; } else { // 转账失败 echo '转账失败,错误信息:' . $result->alipay_fund_trans_toaccount_transfer_response->sub_msg; } ?> ``` 需要注意的是,上述代码中的 `<YOUR_APP_ID>`、`<YOUR_RSA_PRIVATE_KEY>`、`<ALIPAY_RSA_PUBLIC_KEY>`等变量需要替换成你自己的支付宝应用ID、RSA私钥、支付宝公钥等信息。同时,`<YOUR_OUT_BIZ_NO>`、`<PAYEE_ACCOUNT>`、`<TRANSFER_AMOUNT>`等参数也需要替换成你自己的值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值