背景

最近项目需要上线支付宝小程序,同时需要走用户的授权流程完成用户信息的存储,以前做过微信小程序的开发,本以为实现授权的过程是很简单的事情,但是再实现的过程中还是遇到了不少的坑,因此记录一下实现的过程

学到的知识

  1. 支付宝开放接口的调用模式以及实现方式
  2. 支付宝小程序授权的流程
  3. RSA 加密方式

吐槽点

  1. 支付宝小程序的入口隐藏的很深,没有微信小程序那么直接了当
  2. 支付宝小程序的开发者工具比较难用,编译时候比较卡,性能有很大的问题
  3. 每提交一次代码,支付宝小程序的体验码都要进行更换,比较繁琐,而且 localStorage 的东西不知道要如何删除

事先准备

  1. 支付宝开放平台注册一个开发者账号,并做好相应的认证等工作
  2. 创建一个小程序,并记录好相关的小程序信息,包括支付宝公钥,私钥,app 公钥等,可以借鉴支付宝官方提供的相应的公钥生成工具来生成公钥和私钥,工具的下载地址:传送门
  3. 了解下支付宝小程序的签名机制, 详细见docs.open.alipay.com/291/105974
  4. 熟悉下支付宝小程序获取用户信息的过程,详细见支付宝小程序用户授权指引

授权的步骤

授权时序图

clipboard.png

实现流程

  1. 客户端通过 my.getAuthCode 接口获取 code,传给服务端
  2. 服务端通过 code,调用获取 token 接口获取 access_token,alipay.system.oauth.token(换取授权访问令牌)
  3. 通过 token 接口调用支付宝会员查询接口获取会员信息,alipay.user.info.share(支付宝会员授权信息查询接口)
  4. 将获取的用户信息保存到数据库

AmpHelper 工具类

<?php
/**
 * Created by PhpStorm.
 * User: My
 * Date: 2018/8/16
 * Time: 17:45
 */

namespace App\Http\Helper;

use App\Http\Helper\Sys\BusinessHelper;
use Illuminate\Support\Facades\Log;

class AmpHelper
{

const API_DOMAIN = <span class="hljs-string">"https://openapi.alipay.com/gateway.do?"</span>;
const API_METHOD_GENERATE_QR = <span class="hljs-string">'alipay.open.app.qrcode.create'</span>;
const API_METHOD_AUTH_TOKEN = <span class="hljs-string">'alipay.system.oauth.token'</span>;
const API_METHOD_GET_USER_INFO = <span class="hljs-string">'alipay.user.info.share'</span>;

const SIGN_TYPE_RSA2 = <span class="hljs-string">'RSA2'</span>;
const VERSION = <span class="hljs-string">'1.0'</span>;
const FILE_CHARSET_UTF8 = <span class="hljs-string">"UTF-8"</span>;
const FILE_CHARSET_GBK = <span class="hljs-string">"GBK"</span>;
const RESPONSE_OUTER_NODE_QR = <span class="hljs-string">'alipay_open_app_qrcode_create_response'</span>;
const RESPONSE_OUTER_NODE_AUTH_TOKEN = <span class="hljs-string">'alipay_system_oauth_token_response'</span>;
const RESPONSE_OUTER_NODE_USER_INFO = <span class="hljs-string">'alipay_user_info_share_response'</span>;
const RESPONSE_OUTER_NODE_ERROR_RESPONSE = <span class="hljs-string">'error_response'</span>;

const STATUS_CODE_SUCCESS = 10000;
const STATUS_CODE_EXCEPT = 20000;


/**
 * 获取用户信息接口,根据token
 * @param <span class="hljs-variable">$code</span> 授权码
 * 通过授权码获取用户的信息
 */
public static <span class="hljs-keyword">function</span> getAmpUserInfoByAuthCode(<span class="hljs-variable">$code</span>){
    <span class="hljs-variable">$aliUserInfo</span> = [];
    <span class="hljs-variable">$tokenData</span> = AmpHelper::getAmpToken(<span class="hljs-variable">$code</span>);
    //如果token不存在,这种主要是为了处理支付宝的异常记录
    <span class="hljs-keyword">if</span>(isset(<span class="hljs-variable">$tokenData</span>[<span class="hljs-string">'code'</span>])){
        <span class="hljs-built_in">return</span> <span class="hljs-variable">$tokenData</span>;
    }
    <span class="hljs-variable">$token</span> = formatArrValue(<span class="hljs-variable">$tokenData</span>,<span class="hljs-string">'access_token'</span>);
    <span class="hljs-keyword">if</span>(<span class="hljs-variable">$token</span>){
        <span class="hljs-variable">$userBusiParam</span> = self::getAmpUserBaseParam(<span class="hljs-variable">$token</span>);
        <span class="hljs-variable">$url</span> = self::buildRequestUrl(<span class="hljs-variable">$userBusiParam</span>);
        <span class="hljs-variable">$resonse</span> = self::getResponse(<span class="hljs-variable">$url</span>,self::RESPONSE_OUTER_NODE_USER_INFO);
        <span class="hljs-keyword">if</span>(<span class="hljs-variable">$resonse</span>[<span class="hljs-string">'code'</span>] == self::STATUS_CODE_SUCCESS){
            //有效的字段列
            <span class="hljs-variable">$userInfoColumn</span> = [<span class="hljs-string">'user_id'</span>,<span class="hljs-string">'avatar'</span>,<span class="hljs-string">'province'</span>,<span class="hljs-string">'city'</span>,<span class="hljs-string">'nick_name'</span>,<span class="hljs-string">'is_student_certified'</span>,<span class="hljs-string">'user_type'</span>,<span class="hljs-string">'user_status'</span>,<span class="hljs-string">'is_certified'</span>,<span class="hljs-string">'gender'</span>];
            foreach (<span class="hljs-variable">$userInfoColumn</span> as <span class="hljs-variable">$column</span>){
                <span class="hljs-variable">$aliUserInfo</span>[<span class="hljs-variable">$column</span>] = formatArrValue(<span class="hljs-variable">$resonse</span>,<span class="hljs-variable">$column</span>,<span class="hljs-string">''</span>);
            }

        }<span class="hljs-keyword">else</span>{
            <span class="hljs-variable">$exceptColumns</span> = [<span class="hljs-string">'code'</span>,<span class="hljs-string">'msg'</span>,<span class="hljs-string">'sub_code'</span>,<span class="hljs-string">'sub_msg'</span>];
            foreach (<span class="hljs-variable">$exceptColumns</span> as <span class="hljs-variable">$column</span>){
                <span class="hljs-variable">$aliUserInfo</span>[<span class="hljs-variable">$column</span>] = formatArrValue(<span class="hljs-variable">$resonse</span>,<span class="hljs-variable">$column</span>,<span class="hljs-string">''</span>);
            }
        }
    }
    <span class="hljs-built_in">return</span> <span class="hljs-variable">$aliUserInfo</span>;
}


/**
 * 获取小程序token接口
 */
public static <span class="hljs-keyword">function</span> getAmpToken(<span class="hljs-variable">$code</span>){
    <span class="hljs-variable">$param</span> = self::getAuthBaseParam(<span class="hljs-variable">$code</span>);
    <span class="hljs-variable">$url</span> = self::buildRequestUrl(<span class="hljs-variable">$param</span>);
    <span class="hljs-variable">$response</span> = self::getResponse(<span class="hljs-variable">$url</span>,self::RESPONSE_OUTER_NODE_AUTH_TOKEN);
    <span class="hljs-variable">$tokenResult</span> = [];
    <span class="hljs-keyword">if</span>(isset(<span class="hljs-variable">$response</span>[<span class="hljs-string">'code'</span>]) &amp;&amp; <span class="hljs-variable">$response</span>[<span class="hljs-string">'code'</span>] != self::STATUS_CODE_SUCCESS){
        <span class="hljs-variable">$exceptColumns</span> = [<span class="hljs-string">'code'</span>,<span class="hljs-string">'msg'</span>,<span class="hljs-string">'sub_code'</span>,<span class="hljs-string">'sub_msg'</span>];
        foreach (<span class="hljs-variable">$exceptColumns</span> as <span class="hljs-variable">$column</span>){
            <span class="hljs-variable">$tokenResult</span>[<span class="hljs-variable">$column</span>] = formatArrValue(<span class="hljs-variable">$response</span>,<span class="hljs-variable">$column</span>,<span class="hljs-string">''</span>);
        }
    }<span class="hljs-keyword">else</span>{
        <span class="hljs-variable">$tokenResult</span> = <span class="hljs-variable">$response</span>;
    }
    <span class="hljs-built_in">return</span> <span class="hljs-variable">$tokenResult</span>;
}

/**
 * 获取二维码链接接口
 * 433ac5ea4c044378826afe1532bcVX78
 * https://openapi.alipay.com/gateway.do?timestamp=2013-01-01 08:08:08&amp;method=alipay.open.app.qrcode.create&amp;app_id=2893&amp;sign_type=RSA2&amp;sign=ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE&amp;version=1.0&amp;biz_content=
{<span class="hljs-string">"url_param"</span>:<span class="hljs-string">"/index.html?name=ali&amp;loc=hz"</span>, <span class="hljs-string">"query_param"</span>:<span class="hljs-string">"name=1&amp;age=2"</span>, <span class="hljs-string">"describe"</span>:<span class="hljs-string">"二维码描述"</span>}
*/
public static <span class="hljs-keyword">function</span> generateQrCode(<span class="hljs-variable">$mpPage</span> = <span class="hljs-string">'pages/index'</span>,<span class="hljs-variable">$queryParam</span> = [],<span class="hljs-variable">$describe</span>){
    <span class="hljs-variable">$param</span> = self::getQrcodeBaseParam(<span class="hljs-variable">$mpPage</span>,<span class="hljs-variable">$queryParam</span>,<span class="hljs-variable">$describe</span> );
    <span class="hljs-variable">$url</span> = self::buildRequestUrl(<span class="hljs-variable">$param</span>);
    <span class="hljs-variable">$response</span> = self::getResponse(<span class="hljs-variable">$url</span>,self::RESPONSE_OUTER_NODE_QR);
    <span class="hljs-built_in">return</span> <span class="hljs-variable">$response</span>;
}


/**
 * 获取返回的数据,对返回的结果做进一步的封装和解析,因为支付宝的每个接口的返回都是由一个特定的    
 * key组成的,因此这里直接封装了而一个通用的方法,对于不同的接口只需要更改相应的node节点就可以了
 */
public static <span class="hljs-keyword">function</span> getResponse(<span class="hljs-variable">$url</span>,<span class="hljs-variable">$responseNode</span>){
    <span class="hljs-variable">$json</span> = curlRequest(<span class="hljs-variable">$url</span>);
    <span class="hljs-variable">$response</span> = json_decode(<span class="hljs-variable">$json</span>,<span class="hljs-literal">true</span>);
    <span class="hljs-variable">$responseContent</span> = formatArrValue(<span class="hljs-variable">$response</span>,<span class="hljs-variable">$responseNode</span>,[]);
    <span class="hljs-variable">$errResponse</span> = formatArrValue(<span class="hljs-variable">$response</span>,self::RESPONSE_OUTER_NODE_ERROR_RESPONSE,[]);
    <span class="hljs-keyword">if</span>(<span class="hljs-variable">$errResponse</span>){
        <span class="hljs-built_in">return</span> <span class="hljs-variable">$errResponse</span>;
    }
    <span class="hljs-built_in">return</span> <span class="hljs-variable">$responseContent</span>;
}

/**
 * 获取请求的链接
 */
public static <span class="hljs-keyword">function</span> buildQrRequestUrl(<span class="hljs-variable">$mpPage</span> = <span class="hljs-string">'pages/index'</span>,<span class="hljs-variable">$queryParam</span> = []){
    <span class="hljs-variable">$paramStr</span> = http_build_query(self::getQrBaseParam(<span class="hljs-variable">$mpPage</span>,<span class="hljs-variable">$queryParam</span>));
    <span class="hljs-built_in">return</span> self::API_DOMAIN . <span class="hljs-variable">$paramStr</span>;
}



/**
 * 构建请求链接
 */
public static <span class="hljs-keyword">function</span> buildRequestUrl(<span class="hljs-variable">$param</span>){
    <span class="hljs-variable">$paramStr</span> = http_build_query(<span class="hljs-variable">$param</span>);
    <span class="hljs-built_in">return</span> self::API_DOMAIN . <span class="hljs-variable">$paramStr</span>;
}


/**
 * 获取用户的基础信息接口
 */
public static <span class="hljs-keyword">function</span> getAmpUserBaseParam(<span class="hljs-variable">$token</span>){
    <span class="hljs-variable">$busiParam</span> = [
        <span class="hljs-string">'auth_token'</span> =&gt; <span class="hljs-variable">$token</span>,
    ];
    <span class="hljs-variable">$param</span> = self::buildApiBuisinessParam(<span class="hljs-variable">$busiParam</span>,self::API_METHOD_GET_USER_INFO);
    <span class="hljs-built_in">return</span> <span class="hljs-variable">$param</span>;

}

/**
 *获取二维码的基础参数
 */
public static <span class="hljs-keyword">function</span> getQrcodeBaseParam(<span class="hljs-variable">$page</span>= <span class="hljs-string">'pages/index/index'</span>,<span class="hljs-variable">$queryParam</span> = [],<span class="hljs-variable">$describe</span> = <span class="hljs-string">''</span>){
    <span class="hljs-variable">$busiParam</span> = [
        <span class="hljs-string">'biz_content'</span> =&gt; self::getQrBizContent(<span class="hljs-variable">$page</span>,<span class="hljs-variable">$queryParam</span>,<span class="hljs-variable">$describe</span>)
    ];
    <span class="hljs-variable">$param</span> = self::buildApiBuisinessParam(<span class="hljs-variable">$busiParam</span>,self::API_METHOD_GENERATE_QR);
    <span class="hljs-built_in">return</span> <span class="hljs-variable">$param</span>;

}

/**
 *获取授权的基础参数
 */
public static <span class="hljs-keyword">function</span> getAuthBaseParam(<span class="hljs-variable">$code</span>,<span class="hljs-variable">$refreshToken</span> = <span class="hljs-string">''</span>){
    <span class="hljs-variable">$busiParam</span> = [
        <span class="hljs-string">'grant_type'</span> =&gt; <span class="hljs-string">'authorization_code'</span>,
        <span class="hljs-string">'code'</span> =&gt; <span class="hljs-variable">$code</span>,
        <span class="hljs-string">'refresh_token'</span> =&gt; <span class="hljs-variable">$refreshToken</span>,
    ];
    <span class="hljs-variable">$param</span> = self::buildApiBuisinessParam(<span class="hljs-variable">$busiParam</span>,self::API_METHOD_AUTH_TOKEN);
    <span class="hljs-built_in">return</span> <span class="hljs-variable">$param</span>;
}


/**
 * 构建业务参数
 */
public static <span class="hljs-keyword">function</span> buildApiBuisinessParam(<span class="hljs-variable">$businessParam</span>,<span class="hljs-variable">$apiMethod</span>){
    <span class="hljs-variable">$pubParam</span> = self::getApiPubParam(<span class="hljs-variable">$apiMethod</span>);
    <span class="hljs-variable">$businessParam</span> = array_merge(<span class="hljs-variable">$pubParam</span>,<span class="hljs-variable">$businessParam</span>);
    <span class="hljs-variable">$signContent</span> = self::getSignContent(<span class="hljs-variable">$businessParam</span>);
    error_log(<span class="hljs-string">'sign_content ===========&gt;'</span>.<span class="hljs-variable">$signContent</span>);
    <span class="hljs-variable">$rsaHelper</span> = new RsaHelper();
    <span class="hljs-variable">$sign</span> = <span class="hljs-variable">$rsaHelper</span>-&gt;createSign(<span class="hljs-variable">$signContent</span>);
    error_log(<span class="hljs-string">'sign ===========&gt;'</span>.<span class="hljs-variable">$sign</span>);
    <span class="hljs-variable">$businessParam</span>[<span class="hljs-string">'sign'</span>] = <span class="hljs-variable">$sign</span>;
    <span class="hljs-built_in">return</span> <span class="hljs-variable">$businessParam</span>;
}


/**
 * 公共参数
 *
 */
public static <span class="hljs-keyword">function</span> getApiPubParam(<span class="hljs-variable">$apiMethod</span>){
    <span class="hljs-variable">$ampBaseInfo</span> = BusinessHelper::getAmpBaseInfo();
    <span class="hljs-variable">$param</span> = [
        <span class="hljs-string">'timestamp'</span> =&gt; date(<span class="hljs-string">'Y-m-d His'</span>) ,
        <span class="hljs-string">'method'</span> =&gt; <span class="hljs-variable">$apiMethod</span>,
        <span class="hljs-string">'app_id'</span> =&gt; formatArrValue(<span class="hljs-variable">$ampBaseInfo</span>,<span class="hljs-string">'appid'</span>,config(<span class="hljs-string">'param.amp.appid'</span>)),
        <span class="hljs-string">'sign_type'</span> =&gt;self::SIGN_TYPE_RSA2,
        <span class="hljs-string">'charset'</span> =&gt;self::FILE_CHARSET_UTF8,
        <span class="hljs-string">'version'</span> =&gt;self::VERSION,
    ];
    <span class="hljs-built_in">return</span> <span class="hljs-variable">$param</span>;
}


/**
 * 获取签名的内容
 */
public static <span class="hljs-keyword">function</span> getSignContent(<span class="hljs-variable">$params</span>) {
    ksort(<span class="hljs-variable">$params</span>);
    <span class="hljs-variable">$stringToBeSigned</span> = <span class="hljs-string">""</span>;
    <span class="hljs-variable">$i</span> = 0;
    foreach (<span class="hljs-variable">$params</span> as <span class="hljs-variable">$k</span> =&gt; <span class="hljs-variable">$v</span>) {
        <span class="hljs-keyword">if</span> (!empty(<span class="hljs-variable">$v</span>) &amp;&amp; <span class="hljs-string">"@"</span> != substr(<span class="hljs-variable">$v</span>, 0, 1)) {
            <span class="hljs-keyword">if</span> (<span class="hljs-variable">$i</span> == 0) {
                <span class="hljs-variable">$stringToBeSigned</span> .= <span class="hljs-string">"<span class="hljs-variable">$k</span>"</span> . <span class="hljs-string">"="</span> . <span class="hljs-string">"<span class="hljs-variable">$v</span>"</span>;
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-variable">$stringToBeSigned</span> .= <span class="hljs-string">"&amp;"</span> . <span class="hljs-string">"<span class="hljs-variable">$k</span>"</span> . <span class="hljs-string">"="</span> . <span class="hljs-string">"<span class="hljs-variable">$v</span>"</span>;
            }
            <span class="hljs-variable">$i</span>++;
        }
    }
    <span class="hljs-built_in">unset</span> (<span class="hljs-variable">$k</span>, <span class="hljs-variable">$v</span>);
    <span class="hljs-built_in">return</span> <span class="hljs-variable">$stringToBeSigned</span>;
}


public static <span class="hljs-keyword">function</span> convertArrToQueryParam(<span class="hljs-variable">$param</span>){
    <span class="hljs-variable">$queryParam</span> = [];
    foreach (<span class="hljs-variable">$param</span> as <span class="hljs-variable">$key</span> =&gt; <span class="hljs-variable">$val</span>){
        <span class="hljs-variable">$obj</span> = <span class="hljs-variable">$key</span>.<span class="hljs-string">'='</span>.<span class="hljs-variable">$val</span>;
        array_push(<span class="hljs-variable">$queryParam</span>,<span class="hljs-variable">$obj</span>);
    }
    <span class="hljs-variable">$queryStr</span> = implode(<span class="hljs-string">'&amp;'</span>,<span class="hljs-variable">$queryParam</span>);
    <span class="hljs-built_in">return</span> <span class="hljs-variable">$queryStr</span>;
}

/**
 * 转换字符集编码
 * @param <span class="hljs-variable">$data</span>
 * @param <span class="hljs-variable">$targetCharset</span>
 * @<span class="hljs-built_in">return</span> string
 */
public static <span class="hljs-keyword">function</span> characet(<span class="hljs-variable">$data</span>, <span class="hljs-variable">$targetCharset</span>) {
    <span class="hljs-keyword">if</span> (!empty(<span class="hljs-variable">$data</span>)) {
        <span class="hljs-variable">$fileType</span> = self::FILE_CHARSET_UTF8;
        <span class="hljs-keyword">if</span> (strcasecmp(<span class="hljs-variable">$fileType</span>, <span class="hljs-variable">$targetCharset</span>) != 0) {
            <span class="hljs-variable">$data</span> = mb_convert_encoding(<span class="hljs-variable">$data</span>, <span class="hljs-variable">$targetCharset</span>, <span class="hljs-variable">$fileType</span>);
        }
    }
    <span class="hljs-built_in">return</span> <span class="hljs-variable">$data</span>;
}

/**
 * 获取业务参数内容
 */
public static <span class="hljs-keyword">function</span> getQrBizContent(<span class="hljs-variable">$page</span>, <span class="hljs-variable">$queryParam</span> = [],<span class="hljs-variable">$describe</span> = <span class="hljs-string">''</span>){
    <span class="hljs-keyword">if</span>(is_array(<span class="hljs-variable">$queryParam</span>)){
        <span class="hljs-variable">$queryParam</span> = http_build_query(<span class="hljs-variable">$queryParam</span>);
    }
    <span class="hljs-variable">$obj</span> = [
        <span class="hljs-string">'url_param'</span> =&gt; <span class="hljs-variable">$page</span>,
        <span class="hljs-string">'query_param'</span> =&gt; <span class="hljs-variable">$queryParam</span>,
        <span class="hljs-string">'describe'</span> =&gt; <span class="hljs-variable">$describe</span>
    ];
    <span class="hljs-variable">$bizContent</span> = json_encode(<span class="hljs-variable">$obj</span>,JSON_UNESCAPED_UNICODE);
    <span class="hljs-built_in">return</span> <span class="hljs-variable">$bizContent</span>;
}

}复制代码

AmpHeler 工具类关键代码解析

相关常量

//支付宝的api接口地址
const API_DOMAIN = "https://openapi.alipay.com/gateway.do?";
//获取支付宝二维码的接口方法
const API_METHOD_GENERATE_QR = 'alipay.open.app.qrcode.create';
//获取token的接口方法
const API_METHOD_AUTH_TOKEN = 'alipay.system.oauth.token';
//获取用户信息的接口方法
const API_METHOD_GET_USER_INFO = 'alipay.user.info.share';
//支付宝的签名方式,由RSA2和RSA两种
const SIGN_TYPE_RSA2 = 'RSA2';
//版本号,此处固定挑那些就可以了
const VERSION = '1.0';
//UTF8编码
const FILE_CHARSET_UTF8 = "UTF-8";
//GBK编码
const FILE_CHARSET_GBK = "GBK";
//二维码接口调用成功的 返回节点
const RESPONSE_OUTER_NODE_QR = 'alipay_open_app_qrcode_create_response';
//token接口调用成功的 返回节点
const RESPONSE_OUTER_NODE_AUTH_TOKEN = 'alipay_system_oauth_token_response';
//用户信息接口调用成功的 返回节点
const RESPONSE_OUTER_NODE_USER_INFO = 'alipay_user_info_share_response';
//错误的返回的时候的节点
const RESPONSE_OUTER_NODE_ERROR_RESPONSE = 'error_response';

const STATUS_CODE_SUCCESS = 10000;
const STATUS_CODE_EXCEPT = 20000;复制代码

getAmpUserInfoByAuthCode 方法

这个方法是获取用户信息的接口方法,只需要传入客户端传递的 code,就可以获取到用户的完整信息

getAmpToken 方法

这个方法是获取支付宝接口的 token 的方法,是一个公用方法,后面所有的支付宝的口调用,都可以使用这个方法先获取 token

getResponse 方法

考虑到会调用各个支付宝的接口,因此这里封装这个方法是为了方便截取接口返回成功之后的信息,提高代码的阅读性

getApiPubParam 方法

这个方法是为了获取公共的参数,包括版本号,编码,appid,签名类型等基础业务参数

getSignContent 方法

这个方法是获取签名的内容,入参是一个数组,最后输出的是参数的拼接字符串

buildApiBuisinessParam(businessParam,apiMethod)

这个是构建 api 独立的业务参数部分方法,businessParam 参数是支付宝各个接口的业务参数部分(出去公共参数),$apiMethod 是对应的接口的方法名称,如获取 token 的方法名为 alipay.system.oauth.token

签名帮助类

<?php
/**
 * Created by PhpStorm.
 * User: Auser
 * Date: 2018/12/4
 * Time: 15:37
 */

namespace App\Http\Helper;

/**
*$rsa2 = new Rsa2();
*$data = ‘mydata’; // 待签名字符串
*$strSign = $rsa2->createSign($data); // 生成签名
*$is_ok = $rsa2->verifySign($data, $strSign); // 验证签名
*/
class RsaHelper
{

private static <span class="hljs-variable">$PRIVATE_KEY</span>;
private static <span class="hljs-variable">$PUBLIC_KEY</span>;


<span class="hljs-keyword">function</span> <span class="hljs-function"><span class="hljs-title">__construct</span></span>(){
    self::<span class="hljs-variable">$PRIVATE_KEY</span> = config(<span class="hljs-string">'param.amp.private_key'</span>);
    self::<span class="hljs-variable">$PUBLIC_KEY</span> = config(<span class="hljs-string">'param.amp.public_key'</span>);
}

/**
 * 获取私钥
 * @<span class="hljs-built_in">return</span> bool|resource
 */
private static <span class="hljs-keyword">function</span> <span class="hljs-function"><span class="hljs-title">getPrivateKey</span></span>()
{
    <span class="hljs-variable">$privKey</span> = self::<span class="hljs-variable">$PRIVATE_KEY</span>;
    <span class="hljs-variable">$privKey</span> = <span class="hljs-string">"-----BEGIN RSA PRIVATE KEY-----"</span>.PHP_EOL.wordwrap(<span class="hljs-variable">$privKey</span>, 64, PHP_EOL, <span class="hljs-literal">true</span>).PHP_EOL.<span class="hljs-string">"-----END RSA PRIVATE KEY-----"</span>;
    (<span class="hljs-variable">$privKey</span>) or die(<span class="hljs-string">'您使用的私钥格式错误,请检查RSA私钥配置'</span>);
    error_log(<span class="hljs-string">'private_key is ===========&gt;: '</span>.<span class="hljs-variable">$privKey</span>);
    <span class="hljs-built_in">return</span> openssl_pkey_get_private(<span class="hljs-variable">$privKey</span>);
}
/**
 * 获取公钥
 * @<span class="hljs-built_in">return</span> bool|resource
 */
private static <span class="hljs-keyword">function</span> <span class="hljs-function"><span class="hljs-title">getPublicKey</span></span>()
{
    <span class="hljs-variable">$publicKey</span> = self::<span class="hljs-variable">$PUBLIC_KEY</span>;
    <span class="hljs-variable">$publicKey</span> = <span class="hljs-string">"-----BEGIN RSA PRIVATE KEY-----"</span>.PHP_EOL.wordwrap(<span class="hljs-variable">$publicKey</span>, 64, PHP_EOL, <span class="hljs-literal">true</span>).PHP_EOL.<span class="hljs-string">"-----END RSA PRIVATE KEY-----"</span>;
    error_log(<span class="hljs-string">'public key is : ===========&gt;'</span>.<span class="hljs-variable">$publicKey</span>);
    <span class="hljs-built_in">return</span> openssl_pkey_get_public(<span class="hljs-variable">$publicKey</span>);
}
/**
 * 创建签名
 * @param string <span class="hljs-variable">$data</span> 数据
 * @<span class="hljs-built_in">return</span> null|string
 */
public <span class="hljs-keyword">function</span> createSign(<span class="hljs-variable">$data</span> = <span class="hljs-string">''</span>)
{
    //  var_dump(self::getPrivateKey());die;
    <span class="hljs-keyword">if</span> (!is_string(<span class="hljs-variable">$data</span>)) {
        <span class="hljs-built_in">return</span> null;
    }
    <span class="hljs-built_in">return</span> openssl_sign(<span class="hljs-variable">$data</span>, <span class="hljs-variable">$sign</span>, self::getPrivateKey(),OPENSSL_ALGO_SHA256 ) ? base64_encode(<span class="hljs-variable">$sign</span>) : null;
}
/**
 * 验证签名
 * @param string <span class="hljs-variable">$data</span> 数据
 * @param string <span class="hljs-variable">$sign</span> 签名
 * @<span class="hljs-built_in">return</span> bool
 */
public <span class="hljs-keyword">function</span> verifySign(<span class="hljs-variable">$data</span> = <span class="hljs-string">''</span>, <span class="hljs-variable">$sign</span> = <span class="hljs-string">''</span>)
{
    <span class="hljs-keyword">if</span> (!is_string(<span class="hljs-variable">$sign</span>) || !is_string(<span class="hljs-variable">$sign</span>)) {
        <span class="hljs-built_in">return</span> <span class="hljs-literal">false</span>;
    }
    <span class="hljs-built_in">return</span> (bool)openssl_verify(
        <span class="hljs-variable">$data</span>,
        base64_decode(<span class="hljs-variable">$sign</span>),
        self::getPublicKey(),
        OPENSSL_ALGO_SHA256
    );
}

}复制代码

调用

$originUserData = AmpHelper::getAmpUserInfoByAuthCode($code);
echo $originUserData;复制代码

注意 getAmpUserInfoByAuthCode 方法,调用接口成功,会返回支付宝用户的正确信息,示例如下

{
    "alipay_user_info_share_response": {
        "code": "10000",
        "msg": "Success",
        "user_id": "2088102104794936",
        "avatar": "http://tfsimg.alipay.com/images/partner/T1uIxXXbpXXXXXXXX",
        "province": "安徽省",
        "city": "安庆",
        "nick_name": "支付宝小二",
        "is_student_certified": "T",
        "user_type": "1",
        "user_status": "T",
        "is_certified": "T",
        "gender": "F"
    },
    "sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
}复制代码

踩坑点

  1. 在开发之前一定要仔细阅读用户的授权流程指引文档,否则很容出错
  2. 对于用户信息接口,在获取授权信息接口并没有做明确的说明,所以需要先梳理清楚
  3. 支付宝的签名机制和微信的有很大不同,对于习惯了微信小程序开发的人来说,刚开始可能有点不适应,所以需要多看看 sdk 里面的实现


  • php

    PHP(PHP:Hypertext Preprocessor)是一种在电脑上执行的脚本语言,主要是用途在于处理动态网页,也包含了命令列执行接口(command line interface),或者产生图形使用…

    21 引用
感谢    赞同    分享    收藏    关注    反对    举报    ...