作者 郭盛
1 个管道 的构建 通过 耗费 4 秒

用户授权接口以及一些配置文件

<?php
namespace app\api\controller;
use app\common\controller\Api;
/**
* 示例接口
*/
class Demo extends Api
{
//如果$noNeedLogin为空表示所有接口都需要登录才能请求
//如果$noNeedRight为空表示所有接口都需要验证权限才能请求
//如果接口已经设置无需登录,那也就无需鉴权了
//
// 无需登录的接口,*表示全部
protected $noNeedLogin = ['test', 'test1'];
// 无需鉴权的接口,*表示全部
protected $noNeedRight = ['test2'];
/**
* 测试方法
*
* @ApiTitle (测试名称)
* @ApiSummary (测试描述信息)
* @ApiMethod (POST)
* @ApiRoute (/api/demo/test/id/{id}/name/{name})
* @ApiHeaders (name=token, type=string, required=true, description="请求的Token")
* @ApiParams (name="id", type="integer", required=true, description="会员ID")
* @ApiParams (name="name", type="string", required=true, description="用户名")
* @ApiParams (name="data", type="object", sample="{'user_id':'int','user_name':'string','profile':{'email':'string','age':'integer'}}", description="扩展数据")
* @ApiReturnParams (name="code", type="integer", required=true, sample="0")
* @ApiReturnParams (name="msg", type="string", required=true, sample="返回成功")
* @ApiReturnParams (name="data", type="object", sample="{'user_id':'int','user_name':'string','profile':{'email':'string','age':'integer'}}", description="扩展数据返回")
* @ApiReturn ({
'code':'1',
'msg':'返回成功'
})
*/
public function test()
{
$this->success('返回成功', $this->request->param());
}
/**
* 无需登录的接口
*
*/
public function test1()
{
$this->success('返回成功', ['action' => 'test1']);
}
/**
* 需要登录的接口
*
*/
public function test2()
{
$this->success('返回成功', ['action' => 'test2']);
}
/**
* 需要登录且需要验证有相应组的权限
*
*/
public function test3()
{
$this->success('返回成功', ['action' => 'test3']);
}
}
<?php
namespace app\api\controller;
use app\common\controller\Api;
use fast\Random;
/**
* Token接口
*/
class Token extends Api
{
protected $noNeedLogin = [];
protected $noNeedRight = '*';
/**
* 检测Token是否过期
*
*/
public function check()
{
$token = $this->auth->getToken();
$tokenInfo = \app\common\library\Token::get($token);
$this->success('', ['token' => $tokenInfo['token'], 'expires_in' => $tokenInfo['expires_in']]);
}
/**
* 刷新Token
*
*/
public function refresh()
{
//删除源Token
$token = $this->auth->getToken();
\app\common\library\Token::delete($token);
//创建新Token
$token = Random::uuid();
\app\common\library\Token::set($token, $this->auth->id, 2592000);
$tokenInfo = \app\common\library\Token::get($token);
$this->success('', ['token' => $tokenInfo['token'], 'expires_in' => $tokenInfo['expires_in']]);
}
}
... ... @@ -3,318 +3,253 @@
namespace app\api\controller;
use app\common\controller\Api;
use app\common\library\Ems;
use app\common\library\Sms;
use fast\Random;
use fast\Http;
use think\Db;
use think\Validate;
use wxapp\aes\WXBizDataCrypt;
/**
* 会员接口
* 登录接口
*/
class User extends Api
{
protected $noNeedLogin = ['login', 'mobilelogin', 'register', 'resetpwd', 'changeemail', 'changemobile', 'third'];
protected $noNeedLogin = '*';
protected $noNeedRight = '*';
public function _initialize()
{
parent::_initialize();
}
// public function _initialize()
// {
// parent::_initialize();
// }
/**
* 会员中心
* @ApiTitle (获取sessionKey和openid)
* @ApiSummary (获取sessionKey和openid)
* @ApiMethod (POST)
* @ApiRoute (/api/user/getSessionKey)
* @ApiParams (name="code", type="string", required=true, description="小程序code")
* @ApiReturn({
"code": 1,
"msg": "获取成功",
"time": "1553839125",
"data": {
"session_key": "session_key",//token
"openid": "openid",//openid
},
})
*/
public function index()
public function getSessionKey()
{
$this->success('', ['welcome' => $this->auth->nickname]);
}
$validate = new Validate([
'code' => 'require',
]);
/**
* 会员登录
*
* @param string $account 账号
* @param string $password 密码
*/
public function login()
{
$account = $this->request->request('account');
$password = $this->request->request('password');
if (!$account || !$password) {
$this->error(__('Invalid parameters'));
$validate->message([
'code.require' => '缺少参数code!',
]);
$data = $this->request->param();
if (!$validate->check($data)) {
$this->error(['code' => '40003', 'msg' => $validate->getError()]);
}
$ret = $this->auth->login($account, $password);
if ($ret) {
$data = ['userinfo' => $this->auth->getUserinfo()];
$this->success(__('Logged in successful'), $data);
} else {
$this->error($this->auth->getError());
$code = $data['code'];
$appId = config('app_id');
$appSecret = config('app_secret');
$response = "https://api.weixin.qq.com/sns/jscode2session?appid=$appId&secret=$appSecret&js_code=$code&grant_type=authorization_code";
$response = $this->http_get($response);
$response = json_decode($response, true);
if(!empty($response['errcode'])) {
$this->error('操作失败',$response['errcode']);
}
$this->success('获取成功',$response);
}
//curl get请求
public function http_get($url){
$curl = curl_init();//启动一个CURL会话
curl_setopt($curl, CURLOPT_URL,$url);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); // 对认证证书来源的检查
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); // 从证书中检查SSL加密算法是否存在
curl_setopt($curl, CURLOPT_TIMEOUT, 30); // 设置超时限制防止死循环
curl_setopt($curl, CURLOPT_HEADER, false);//不开启header
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); // 获取的信息以文件流的形式返回
$result = curl_exec($curl); //执行操作
curl_close($curl);
return $result;
}
/**
* 手机验证码登录
*
* @param string $mobile 手机号
* @param string $captcha 验证码
* @ApiTitle (小程序登录注册)
* @ApiSummary (小程序登录注册)
* @ApiMethod (POST)
* @ApiRoute (/api/user/login)
* @ApiParams (name="openid", type="string", required=true, description="openid")
* @ApiParams (name="session_key", type="string", required=true, description="session_key")
* @ApiParams (name="encrypted_data", type="string", required=true, description="encrypted_data")
* @ApiParams (name="iv", type="string", required=true, description="iv")
* @ApiReturn({
"code": 1,
"msg": "登陆成功",
"time": "1553839125",
"data": {
"token": "token",//登录唯一标识
},
})
*/
public function mobilelogin()
public function login()
{
$mobile = $this->request->request('mobile');
$captcha = $this->request->request('captcha');
if (!$mobile || !$captcha) {
$this->error(__('Invalid parameters'));
}
if (!Validate::regex($mobile, "^1\d{10}$")) {
$this->error(__('Mobile is incorrect'));
$validate = new Validate([
'openid' => 'require',
'session_key' => 'require',
'encrypted_data' => 'require',
'iv' => 'require',
]);
$validate->message([
'openid.require' => '缺少参数openid!',
'session_key.require' => '缺少参数session_key!',
'encrypted_data.require' => '缺少参数encrypted_data!',
'iv.require' => '缺少参数iv!',
]);
$data = $this->request->param();
if (!$validate->check($data)) {
$this->error(['code' => '40003', 'msg' => $validate->getError()]);
}
if (!Sms::check($mobile, $captcha, 'mobilelogin')) {
$this->error(__('Captcha is incorrect'));
$appId = config('app_id');
$openid = $data['openid'];
$sessionKey = $data['session_key'];
$pc = new WXBizDataCrypt($appId, $sessionKey);
$errCode = $pc->decryptData($data['encrypted_data'], $data['iv'], $wxUserData);
if ($errCode != 0) {
$this->error('检验数据失败!', ['errCode' => $errCode, 'param' => $data]);
}
$user = \app\common\model\User::getByMobile($mobile);
if ($user) {
if ($user->status != 'normal') {
$this->error(__('Account is locked'));
$findThirdPartyUser = Db::name("third")
->where('openid', $openid)
->where('app_id', $appId)
->find();
$currentTime = time();
$ip = $this->request->ip(0, true);
$wxUserData['sessionKey'] = $sessionKey;
unset($wxUserData['watermark']);
if ($findThirdPartyUser) {
$token = generate_user_token($findThirdPartyUser['user_id']);
$userData = [
'loginip' => $ip,
'logintime' => $currentTime,
'login_times' => Db::raw('login_times+1'),
'more' => json_encode($wxUserData)
];
if (isset($wxUserData['unionId'])) {
$userData['union_id'] = $wxUserData['unionId'];
}
//如果已经有账号则直接登录
$ret = $this->auth->direct($user->id);
} else {
$ret = $this->auth->register($mobile, Random::alnum(), '', $mobile, []);
}
if ($ret) {
Sms::flush($mobile, 'mobilelogin');
$data = ['userinfo' => $this->auth->getUserinfo()];
$this->success(__('Logged in successful'), $data);
} else {
$this->error($this->auth->getError());
}
}
/**
* 注册会员
*
* @param string $username 用户名
* @param string $password 密码
* @param string $email 邮箱
* @param string $mobile 手机号
* @param string $code 验证码
*/
public function register()
{
$username = $this->request->request('username');
$password = $this->request->request('password');
$email = $this->request->request('email');
$mobile = $this->request->request('mobile');
$code = $this->request->request('code');
if (!$username || !$password) {
$this->error(__('Invalid parameters'));
}
if ($email && !Validate::is($email, "email")) {
$this->error(__('Email is incorrect'));
}
if ($mobile && !Validate::regex($mobile, "^1\d{10}$")) {
$this->error(__('Mobile is incorrect'));
}
$ret = Sms::check($mobile, $code, 'register');
if (!$ret) {
$this->error(__('Captcha is incorrect'));
}
$ret = $this->auth->register($username, $password, $email, $mobile, []);
if ($ret) {
$data = ['userinfo' => $this->auth->getUserinfo()];
$this->success(__('Sign up successful'), $data);
Db::name("third")
->where('openid', $openid)
->where('app_id', $appId)
->update($userData);
$this->success("登录成功!", ['token' => $token]);
} else {
$this->error($this->auth->getError());
Db::startTrans();
$userId = Db::name("user")->insertGetId([
'status' => 'normal',
'id_num' => date('Ymd').str_pad(mt_rand(1, 99999),5,'0',STR_PAD_LEFT).'8',
'gender' => $wxUserData['gender'],
'nickname' => $wxUserData['nickName'],
'avatar' => $wxUserData['avatarUrl'],
'joinip' => $ip,
'jointime' => $currentTime,
'createtime' => $currentTime,
'updatetime' => $currentTime,
'loginip' => $ip,
'logintime' => $currentTime,
]);
$row = Db::name("third")->insert([
'openid' => $openid,
'user_id' => $userId,
'nickname' => $wxUserData['nickName'],
'app_id' => $appId,
'loginip' => $ip,
'union_id' => '',
'logintime' => $currentTime,
'createtime' => $currentTime,
'login_times' => 1,
'more' => json_encode($wxUserData)
]);
if ($userId && $row) {
Db::commit();
$token = generate_user_token($userId);
$this->success("登录成功!", ['token' => $token]);
} else {
Db::rollback();
$this->error('登录失败');
}
}
}
/**
* 注销登录
*/
public function logout()
{
$this->auth->logout();
$this->success(__('Logout successful'));
}
/**
* 修改会员个人信息
*
* @param string $avatar 头像地址
* @param string $username 用户名
* @param string $nickname 昵称
* @param string $bio 个人简介
* @ApiTitle (通过code获取token)
* @ApiSummary (通过code获取token)
* @ApiMethod (POST)
* @ApiRoute (/api/user/getToken)
* @ApiParams (name="code", type="string", required=true, description="code")
* @ApiReturn({
"code": 1,
"msg": "SUCCESS",
"time": "1553839125",
"data": {
"token": "token",//登录唯一标识
},
})
*/
public function profile()
{
$user = $this->auth->getUser();
$username = $this->request->request('username');
$nickname = $this->request->request('nickname');
$bio = $this->request->request('bio');
$avatar = $this->request->request('avatar', '', 'trim,strip_tags,htmlspecialchars');
if ($username) {
$exists = \app\common\model\User::where('username', $username)->where('id', '<>', $this->auth->id)->find();
if ($exists) {
$this->error(__('Username already exists'));
}
$user->username = $username;
}
$user->nickname = $nickname;
$user->bio = $bio;
$user->avatar = $avatar;
$user->save();
$this->success();
}
public function getToken(){
$validate = new Validate([
'code' => 'require',
]);
/**
* 修改邮箱
*
* @param string $email 邮箱
* @param string $captcha 验证码
*/
public function changeemail()
{
$user = $this->auth->getUser();
$email = $this->request->post('email');
$captcha = $this->request->request('captcha');
if (!$email || !$captcha) {
$this->error(__('Invalid parameters'));
}
if (!Validate::is($email, "email")) {
$this->error(__('Email is incorrect'));
}
if (\app\common\model\User::where('email', $email)->where('id', '<>', $user->id)->find()) {
$this->error(__('Email already exists'));
}
$result = Ems::check($email, $captcha, 'changeemail');
if (!$result) {
$this->error(__('Captcha is incorrect'));
}
$verification = $user->verification;
$verification->email = 1;
$user->verification = $verification;
$user->email = $email;
$user->save();
Ems::flush($email, 'changeemail');
$this->success();
}
$validate->message([
'code.require' => '缺少参数code!',
]);
/**
* 修改手机号
*
* @param string $email 手机号
* @param string $captcha 验证码
*/
public function changemobile()
{
$user = $this->auth->getUser();
$mobile = $this->request->request('mobile');
$captcha = $this->request->request('captcha');
if (!$mobile || !$captcha) {
$this->error(__('Invalid parameters'));
}
if (!Validate::regex($mobile, "^1\d{10}$")) {
$this->error(__('Mobile is incorrect'));
$data = $this->request->param();
if (!$validate->check($data)) {
$this->error(['code'=>'40003','msg'=>$validate->getError()]);
}
if (\app\common\model\User::where('mobile', $mobile)->where('id', '<>', $user->id)->find()) {
$this->error(__('Mobile already exists'));
}
$result = Sms::check($mobile, $captcha, 'changemobile');
if (!$result) {
$this->error(__('Captcha is incorrect'));
}
$verification = $user->verification;
$verification->mobile = 1;
$user->verification = $verification;
$user->mobile = $mobile;
$user->save();
Sms::flush($mobile, 'changemobile');
$this->success();
}
/**
* 第三方登录
*
* @param string $platform 平台名称
* @param string $code Code码
*/
public function third()
{
$url = url('user/index');
$platform = $this->request->request("platform");
$code = $this->request->request("code");
$config = get_addon_config('third');
if (!$config || !isset($config[$platform])) {
$this->error(__('Invalid parameters'));
$code = $data['code'];
$appId = config('app_id');
$appSecret = config('app_secret');
$response = Http::sendRequest("https://api.weixin.qq.com/sns/jscode2session?appid=$appId&secret=$appSecret&js_code=$code&grant_type=authorization_code");
if (!empty($response['errcode'])) {
$this->error('操作失败:',$response['errcode']);
}
$app = new \addons\third\library\Application($config);
//通过code换access_token和绑定会员
$result = $app->{$platform}->getUserInfo(['code' => $code]);
if ($result) {
$loginret = \addons\third\library\Service::connect($platform, $result);
if ($loginret) {
$data = [
'userinfo' => $this->auth->getUserinfo(),
'thirdinfo' => $result
];
$this->success(__('Logged in successful'), $data);
}
$third = Db::name('third')->where(['openid'=>json_decode($response['msg'],true)['openid']])->find();
if(empty($third)){
$this->error('查无此人');
}
$this->error(__('Operation failed'), $url);
$user_token = Db::name('user_token')->where('user_id',$third['user_id'])->find();
$data['token'] = $user_token['token'];
$this->success('SUCCESS',$data);
}
/**
* 重置密码
*
* @param string $mobile 手机号
* @param string $newpassword 新密码
* @param string $captcha 验证码
*/
public function resetpwd()
{
$type = $this->request->request("type");
$mobile = $this->request->request("mobile");
$email = $this->request->request("email");
$newpassword = $this->request->request("newpassword");
$captcha = $this->request->request("captcha");
if (!$newpassword || !$captcha) {
$this->error(__('Invalid parameters'));
}
if ($type == 'mobile') {
if (!Validate::regex($mobile, "^1\d{10}$")) {
$this->error(__('Mobile is incorrect'));
}
$user = \app\common\model\User::getByMobile($mobile);
if (!$user) {
$this->error(__('User not found'));
}
$ret = Sms::check($mobile, $captcha, 'resetpwd');
if (!$ret) {
$this->error(__('Captcha is incorrect'));
}
Sms::flush($mobile, 'resetpwd');
} else {
if (!Validate::is($email, "email")) {
$this->error(__('Email is incorrect'));
}
$user = \app\common\model\User::getByEmail($email);
if (!$user) {
$this->error(__('User not found'));
}
$ret = Ems::check($email, $captcha, 'resetpwd');
if (!$ret) {
$this->error(__('Captcha is incorrect'));
}
Ems::flush($email, 'resetpwd');
}
//模拟一次登录
$this->auth->direct($user->id);
$ret = $this->auth->changepwd($newpassword, '', true);
if ($ret) {
$this->success(__('Reset password successful'));
} else {
$this->error($this->auth->getError());
}
public function member(){
$this->success('SUCCESS',$this->user);
}
}
... ...
<?php
namespace app\api\controller;
use app\common\controller\Api;
use app\common\model\User;
/**
* 验证接口
*/
class Validate extends Api
{
protected $noNeedLogin = '*';
protected $layout = '';
protected $error = null;
public function _initialize()
{
parent::_initialize();
}
/**
* 检测邮箱
*
* @param string $email 邮箱
* @param string $id 排除会员ID
*/
public function check_email_available()
{
$email = $this->request->request('email');
$id = (int)$this->request->request('id');
$count = User::where('email', '=', $email)->where('id', '<>', $id)->count();
if ($count > 0) {
$this->error(__('邮箱已经被占用'));
}
$this->success();
}
/**
* 检测用户名
*
* @param string $username 用户名
* @param string $id 排除会员ID
*/
public function check_username_available()
{
$email = $this->request->request('username');
$id = (int)$this->request->request('id');
$count = User::where('username', '=', $email)->where('id', '<>', $id)->count();
if ($count > 0) {
$this->error(__('用户名已经被占用'));
}
$this->success();
}
/**
* 检测手机
*
* @param string $mobile 手机号
* @param string $id 排除会员ID
*/
public function check_mobile_available()
{
$mobile = $this->request->request('mobile');
$id = (int)$this->request->request('id');
$count = User::where('mobile', '=', $mobile)->where('id', '<>', $id)->count();
if ($count > 0) {
$this->error(__('该手机号已经占用'));
}
$this->success();
}
/**
* 检测手机
*
* @param string $mobile 手机号
*/
public function check_mobile_exist()
{
$mobile = $this->request->request('mobile');
$count = User::where('mobile', '=', $mobile)->count();
if (!$count) {
$this->error(__('手机号不存在'));
}
$this->success();
}
/**
* 检测邮箱
*
* @param string $mobile 邮箱
*/
public function check_email_exist()
{
$email = $this->request->request('email');
$count = User::where('email', '=', $email)->count();
if (!$count) {
$this->error(__('邮箱不存在'));
}
$this->success();
}
/**
* 检测手机验证码
*
* @param string $mobile 手机号
* @param string $captcha 验证码
* @param string $event 事件
*/
public function check_sms_correct()
{
$mobile = $this->request->request('mobile');
$captcha = $this->request->request('captcha');
$event = $this->request->request('event');
if (!\app\common\library\Sms::check($mobile, $captcha, $event)) {
$this->error(__('验证码不正确'));
}
$this->success();
}
/**
* 检测邮箱验证码
*
* @param string $email 邮箱
* @param string $captcha 验证码
* @param string $event 事件
*/
public function check_ems_correct()
{
$email = $this->request->request('email');
$captcha = $this->request->request('captcha');
$event = $this->request->request('event');
if (!\app\common\library\Ems::check($email, $captcha, $event)) {
$this->error(__('验证码不正确'));
}
$this->success();
}
}
... ... @@ -12,12 +12,21 @@ use think\Loader;
use think\Request;
use think\Response;
use think\Route;
use think\Db;
/**
* API控制器基类
*/
class Api
{
//token
protected $token = '';
//用户 id
protected $userId = 0;
//用户
protected $user;
/**
* @var Request Request 实例
... ... @@ -91,7 +100,23 @@ class Api
*/
protected function _initialize()
{
if (Config::get('url_domain_deploy')) {
$token = $this->request->header('token');
if(empty($token)){
return;
}
$this->token = $token;
$user = Db::name('user_token')
->alias('a')
->field('b.*')
->where(['a.token' => $token])
->join('__USER__ b', 'a.user_id = b.id')
->find();
if (!empty($user)) {
$this->user = $user;
$this->userId = $user['id'];
}
/*if (Config::get('url_domain_deploy')) {
$domain = Route::rules('domain');
if (isset($domain['api'])) {
if (isset($_SERVER['HTTP_ORIGIN'])) {
... ... @@ -155,7 +180,7 @@ class Api
Config::set('upload', array_merge(Config::get('upload'), $upload));
// 加载当前控制器语言包
$this->loadlang($controllername);
$this->loadlang($controllername);*/
}
/**
... ... @@ -319,4 +344,35 @@ class Api
return true;
}
/**
* 获取当前登录用户的id
* @return int
*/
protected function getUserId()
{
if (empty($this->userId)) {
$this->success('');
}
$user = Db::name('user')->where(['id'=>$this->userId])->find();
if($user['status'] != 'normal'){
$this->error('您已被拉黑');
}
return $this->userId;
}
/**
* 获取当前用户openid
* @return mixed
* @return int
*/
protected function getOpenId()
{
if (empty($this->userId)) {
$this->error('用户未登录');
}
$third = Db::name('third')->where(['user_id'=>$this->userId])->find();
return $third['openid'];
}
}
... ...
<?php
namespace wxapp\aes;
/**
* error code 说明.
* <ul>
* <li>-41001: encodingAesKey 非法</li>
* <li>-41003: aes 解密失败</li>
* <li>-41004: 解密后得到的buffer非法</li>
* <li>-41005: base64加密失败</li>
* <li>-41016: base64解密失败</li>
* </ul>
*/
class ErrorCode
{
public static $OK = 0;
public static $IllegalAesKey = -41001;
public static $IllegalIv = -41002;
public static $IllegalBuffer = -41003;
public static $DecodeBase64Error = -41004;
}
\ No newline at end of file
... ...
<?php
namespace wxapp\aes;
/**
* PKCS7Encoder class
*
* 提供基于PKCS7算法的加解密接口.
*/
class PKCS7Encoder
{
public static $block_size = 16;
/**
* 对需要加密的明文进行填充补位
* @param $text 需要进行填充补位操作的明文
* @return 补齐明文字符串
*/
function encode($text)
{
$block_size = PKCS7Encoder::$block_size;
$text_length = strlen($text);
//计算需要填充的位数
$amount_to_pad = PKCS7Encoder::$block_size - ($text_length % PKCS7Encoder::$block_size);
if ($amount_to_pad == 0) {
$amount_to_pad = PKCS7Encoder::block_size;
}
//获得补位所用的字符
$pad_chr = chr($amount_to_pad);
$tmp = "";
for ($index = 0; $index < $amount_to_pad; $index++) {
$tmp .= $pad_chr;
}
return $text . $tmp;
}
/**
* 对解密后的明文进行补位删除
* @param decrypted 解密后的明文
* @return 删除填充补位后的明文
*/
function decode($text)
{
$pad = ord(substr($text, -1));
if ($pad < 1 || $pad > 32) {
$pad = 0;
}
return substr($text, 0, (strlen($text) - $pad));
}
}
... ...
<?php
namespace wxapp\aes;
/**
* Prpcrypt class
*
*
*/
class Prpcrypt
{
public $key;
public function __construct($k)
{
$this->key = $k;
}
/**
* 对密文进行解密
* @param string $aesCipher 需要解密的密文
* @param string $aesIV 解密的初始向量
* @return string 解密得到的明文
*/
public function decrypt($aesCipher, $aesIV)
{
if (function_exists('mcrypt_module_open')) {
try {
$module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
mcrypt_generic_init($module, $this->key, $aesIV);
//解密
$decrypted = mdecrypt_generic($module, $aesCipher);
mcrypt_generic_deinit($module);
mcrypt_module_close($module);
} catch (\Exception $e) {
return [ErrorCode::$IllegalBuffer, null];
}
} else if (function_exists('openssl_decrypt')) {
$decrypted = openssl_decrypt($aesCipher, 'AES-128-CBC', $this->key, OPENSSL_RAW_DATA, $aesIV);
if ($decrypted === false) return [ErrorCode::$IllegalBuffer, null];
}
try {
//去除补位字符
$pkc_encoder = new PKCS7Encoder;
$result = $pkc_encoder->decode($decrypted);
} catch (\Exception $e) {
//print $e;
return [ErrorCode::$IllegalBuffer, null];
}
return [0, $result];
}
}
\ No newline at end of file
... ...
<?php
namespace wxapp\aes;
/**
* 对微信小程序用户加密数据进行解密.
*
* @copyright Copyright (c) 1998-2014 Tencent Inc.
*/
class WXBizDataCrypt
{
private $appid;
private $sessionKey;
/**
* 构造函数
* @param $sessionKey string 用户在小程序登录后获取的会话密钥
* @param $appid string 小程序的appid
*/
public function __construct($appid, $sessionKey)
{
$this->sessionKey = $sessionKey;
$this->appid = $appid;
}
/**
* 检验数据的真实性,并且获取解密后的明文.
* @param $encryptedData string 加密的用户数据
* @param $iv string 与用户数据一同返回的初始向量
* @param $data string 解密后的原文
*
* @return int 成功0,失败返回对应的错误码
*/
public function decryptData($encryptedData, $iv, &$data)
{
if (strlen($this->sessionKey) != 24) {
return ErrorCode::$IllegalAesKey;
}
$aesKey = base64_decode($this->sessionKey);
if (strlen($iv) != 24) {
return ErrorCode::$IllegalIv;
}
$aesIV = base64_decode($iv);
$aesCipher = base64_decode($encryptedData);
$pc = new Prpcrypt($aesKey);
$result = $pc->decrypt($aesCipher, $aesIV);
if ($result[0] != 0) {
return $result[0];
}
$dataObj = json_decode($result[1], true);
if (empty($dataObj)) {
return ErrorCode::$IllegalBuffer;
}
if ($dataObj['watermark']['appid'] != $this->appid) {
return ErrorCode::$IllegalBuffer;
}
$data = $dataObj;
return ErrorCode::$OK;
}
}
... ...
<?php
use wxapp\aes\WXBizDataCrypt;
$appid = 'wx4f4bc4dec97d474b';
$sessionKey = 'tiihtNczf5v6AKRyjwEUhQ==';
$encryptedData = "CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZM
QmRzooG2xrDcvSnxIMXFufNstNGTyaGS
9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+
3hVbJSRgv+4lGOETKUQz6OYStslQ142d
NCuabNPGBzlooOmB231qMM85d2/fV6Ch
evvXvQP8Hkue1poOFtnEtpyxVLW1zAo6
/1Xx1COxFvrc2d7UL/lmHInNlxuacJXw
u0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn
/Hz7saL8xz+W//FRAUid1OksQaQx4CMs
8LOddcQhULW4ucetDf96JcR3g0gfRK4P
C7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB
6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns
/8wR2SiRS7MNACwTyrGvt9ts8p12PKFd
lqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYV
oKlaRv85IfVunYzO0IKXsyl7JCUjCpoG
20f0a04COwfneQAGGwd5oa+T8yO5hzuy
Db/XcxxmK01EpqOyuxINew==";
$iv = 'r7BXXKkLb8qrSNn05n0qiA==';
$pc = new WXBizDataCrypt($appid, $sessionKey);
$errCode = $pc->decryptData($encryptedData, $iv, $data);
if ($errCode == 0) {
print($data . "\n");
} else {
print($errCode . "\n");
}
... ...
<?php
namespace wxapp\pay;
/*
* 小程序微信支付
*/
class WeixinPay {
protected $appid;
protected $mch_id;
protected $key;
protected $openid;
protected $out_trade_no;
protected $body;
protected $total_fee;
protected $notify_url;
function __construct() {
$this->appid = config('app_id');
$this->mch_id = config('wx_mch_id');
$this->key = config('wx_pay_key');
}
public function pay($openid,$out_trade_no,$body,$total_fee,$notify_url) {
$this->openid = $openid;
$this->out_trade_no = $out_trade_no;
$this->body = $body;
$this->total_fee = $total_fee;
$this->notify_url = $notify_url;
//统一下单接口
$return = $this->weixinapp();
return $return;
}
//统一下单接口
protected function unifiedorder() {
$url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
$parameters = array(
'appid' => $this->appid, //小程序ID
'mch_id' => $this->mch_id, //商户号
'nonce_str' => $this->createNoncestr(), //随机字符串
'body' => $this->body,
'out_trade_no'=> $this->out_trade_no,
'total_fee' => 1,//$this->total_fee*100,
'spbill_create_ip' => get_client_ip(0,true),
'notify_url' => $this->notify_url,
'openid' => $this->openid, //用户id
'trade_type' => 'JSAPI'//交易类型
);
//统一下单签名
$parameters['sign'] = $this->getSign($parameters);
$xmlData = $this->arrayToXml($parameters);
$return = $this->xmlToArray($this->postXmlCurl($xmlData, $url, 60));
return $return;
}
private static function postXmlCurl($xml, $url, $second = 30)
{
$ch = curl_init();
//设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); //严格校验
//设置header
curl_setopt($ch, CURLOPT_HEADER, FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
//post提交方式
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);
curl_setopt($ch, CURLOPT_TIMEOUT, 40);
set_time_limit(0);
//运行curl
$data = curl_exec($ch);
//返回结果
if ($data) {
curl_close($ch);
return $data;
} else {
$error = curl_errno($ch);
curl_close($ch);
throw new ErrorException("curl出错,错误码:$error");
}
}
//数组转换成xml
private function arrayToXml($arr) {
$xml = "<root>";
foreach ($arr as $key => $val) {
if (is_array($val)) {
$xml .= "<" . $key . ">" . $this->arrayToXml($val) . "</" . $key . ">";
} else {
$xml .= "<" . $key . ">" . $val . "</" . $key . ">";
}
}
$xml .= "</root>";
return $xml;
}
//xml转换成数组
public function xmlToArray($xml) {
//禁止引用外部xml实体
libxml_disable_entity_loader(true);
$xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
$val = json_decode(json_encode($xmlstring), true);
return $val;
}
//微信小程序接口
private function weixinapp() {
//统一下单接口
$unifiedorder = $this->unifiedorder();
if(isset($unifiedorder['return_code'])&&$unifiedorder['return_code']=='FAIL'){
return $unifiedorder;
}
$parameters = array(
'appId' => $this->appid, //小程序ID
'timeStamp' => '' . time() . '', //时间戳
'nonceStr' => $this->createNoncestr(), //随机串
'package' => 'prepay_id=' . $unifiedorder['prepay_id'], //数据包
'signType' => 'MD5'//签名方式
);
//签名
$parameters['paySign'] = $this->getSign($parameters);
return $parameters;
}
//作用:产生随机字符串,不长于32位
private function createNoncestr($length = 32) {
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
$str = "";
for ($i = 0; $i < $length; $i++) {
$str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
return $str;
}
//作用:生成签名
public function getSign($Obj) {
foreach ($Obj as $k => $v) {
$Parameters[$k] = $v;
}
//签名步骤一:按字典序排序参数
ksort($Parameters);
$String = $this->formatBizQueryParaMap($Parameters, false);
//签名步骤二:在string后加入KEY
$String = $String . "&key=" . $this->key;
//签名步骤三:MD5加密
$String = md5($String);
//签名步骤四:所有字符转为大写
$result_ = strtoupper($String);
return $result_;
}
///作用:格式化参数,签名过程需要使用
private function formatBizQueryParaMap($paraMap, $urlencode) {
$buff = "";
ksort($paraMap);
foreach ($paraMap as $k => $v) {
if ($urlencode) {
$v = urlencode($v);
}
$buff .= $k . "=" . $v . "&";
}
$reqPar='';
if (strlen($buff) > 0) {
$reqPar = substr($buff, 0, strlen($buff) - 1);
}
return $reqPar;
}
}
\ No newline at end of file
... ...
<?php
namespace wxapp\pay;
class WeixinRefund {
//证书目录
protected $SSLCERT_PATH;
protected $SSLKEY_PATH;
protected $appid;
protected $mchid;
protected $key;
protected $openid;
protected $outTradeNo;
protected $totalFee;
protected $outRefundNo;
protected $refundFee;
function __construct(){
//初始化退款类需要的变量
$this->appid = config('app_id');
$this->mchid = config('wx_mch_id');
$this->key = config('wx_pay_key');
$this->SSLCERT_PATH = config('cert_path');
$this->SSLKEY_PATH = config('key_path');
}
public function refund($openid,$outTradeNo,$totalFee,$outRefundNo,$refundFee){
$this->openid = $openid;
$this->outTradeNo = $outTradeNo;
$this->totalFee = $totalFee;
$this->outRefundNo = $outRefundNo;
$this->refundFee = $refundFee;
//对外暴露的退款接口
$result = $this->wxrefundapi();
return $result;
}
private function wxrefundapi(){
//通过微信api进行退款流程
$parma = array(
'appid'=> $this->appid,
'mch_id'=> $this->mchid,
'nonce_str'=> $this->createNoncestr(),
'out_refund_no'=> $this->outRefundNo,//退款订单号
'out_trade_no'=> $this->outTradeNo,//商户订单号
'total_fee'=> $this->totalFee*100,//订单总金额
'refund_fee'=> $this->refundFee*100,//申请退款金额
);
$parma['sign'] = $this->getSign($parma);
$xmldata = $this->arrayToXml($parma);
$xmlresult = $this->postXmlSSLCurl($xmldata,'https://api.mch.weixin.qq.com/secapi/pay/refund');
$result = $this->xmlToArray($xmlresult);
return $result;
}
//作用:产生随机字符串,不长于32位
private function createNoncestr($length = 32) {
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
$str = "";
for ($i = 0; $i < $length; $i++) {
$str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
return $str;
}
//作用:生成签名
private function getSign($Obj) {
foreach ($Obj as $k => $v) {
$Parameters[$k] = $v;
}
//签名步骤一:按字典序排序参数
ksort($Parameters);
$String = $this->formatBizQueryParaMap($Parameters, false);
//签名步骤二:在string后加入KEY
$String = $String . "&key=" . $this->key;
//签名步骤三:MD5加密
$String = md5($String);
//签名步骤四:所有字符转为大写
$result_ = strtoupper($String);
return $result_;
}
//数组转换成xml
private function arrayToXml($arr) {
$xml = "<root>";
foreach ($arr as $key => $val) {
if (is_array($val)) {
$xml .= "<" . $key . ">" . arrayToXml($val) . "</" . $key . ">";
} else {
$xml .= "<" . $key . ">" . $val . "</" . $key . ">";
}
}
$xml .= "</root>";
return $xml;
}
//xml转换成数组
private function xmlToArray($xml) {
//禁止引用外部xml实体
libxml_disable_entity_loader(true);
$xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
$val = json_decode(json_encode($xmlstring), true);
return $val;
}
//需要使用证书的请求
function postXmlSSLCurl($xml,$url,$second=30){
$ch = curl_init();
//超时时间
curl_setopt($ch,CURLOPT_TIMEOUT,$second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
//设置header
curl_setopt($ch,CURLOPT_HEADER,FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE);
//设置证书
//使用证书:cert 与 key 分别属于两个.pem文件
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLCERT, $this->SSLCERT_PATH);
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLKEY, $this->SSLKEY_PATH);
//post提交方式
curl_setopt($ch,CURLOPT_POST, true);
curl_setopt($ch,CURLOPT_POSTFIELDS,$xml);
$data = curl_exec($ch);
//返回结果
if($data){
curl_close($ch);
return $data;
} else {
$error = curl_errno($ch);
echo "curl出错,错误码:$error"."<br>";
curl_close($ch);
return false;
}
}
///作用:格式化参数,签名过程需要使用
private function formatBizQueryParaMap($paraMap, $urlencode) {
$buff = "";
ksort($paraMap);
foreach ($paraMap as $k => $v) {
if ($urlencode) {
$v = urlencode($v);
}
$buff .= $k . "=" . $v . "&";
}
$reqPar = '';
if (strlen($buff) > 0) {
$reqPar = substr($buff, 0, strlen($buff) - 1);
}
return $reqPar;
}
}
\ No newline at end of file
... ...
此 diff 太大无法显示。