php微信支付之APP支付方法


Posted in PHP onMarch 04, 2015

本文实例讲述了微信开放平台移动应用集成微信支付功能。分享给大家供大家参考。具体分析如下:

WechatAppPay文件代码如下:

<?php

namespace common\services\WechatPay;

class WechatAppPay extends WechatPayBase

{

    //package参数

    public $package = [];

    //异步通知参数

    public $notify = [];

    //推送预支付订单参数

    protected $config = [];

    //存储access token和获取时间的文件

    protected $file;

    //access token

    protected $accessToken;

    //取access token的url

    const ACCESS_TOKEN_URL = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s';

    //生成预支付订单提交地址

    const POST_ORDER_URL = 'https://api.weixin.qq.com/pay/genprepay?access_token=%s';

    public function __construct()

    {

        $this->file = __DIR__ . '/payAccessToken.txt';

    }

    /**

     * 创建APP支付最终返回参数

     * @throws \Exception

     * @return multitype:string NULL

     */

    public function createAppPayData()

    {

        $this->generateConfig();

        $prepayid = $this->getPrepayid();

        try{

            $array = [

                'appid' => $this->appid,

                'appkey' => $this->paySignkey,

                'noncestr' => $this->getRandomStr(),

                'package' => 'Sign=WXPay',

                'partnerid' => $this->partnerId,

                'prepayid' => $prepayid,

                'timestamp' => (string)time(),

            ];

            $array['sign'] = $this->sha1Sign($array);

            unset($array['appkey']);

        } catch(\Exception $e) {

            throw new \Exception($e->getMessage());

        }

        return $array;

    }

    /**

     * 验证支付成功后的通知参数

     * 

     * @throws \Exception

     * @return boolean

     */

    public function verifyNotify()

    {

        try{

            $staySignStr = $this->notify;

            unset($staySignStr['sign']);

            $sign = $this->signData($staySignStr);

            return $this->notify['sign'] === $sign;

        } catch(\Exception $e) {

            throw new \Exception($e->getMessage());

        }

    }

    /**

     * 魔术方法,给添加支付参数进来

     * 

     * @param string $name  参数名

     * @param string $value  参数值

     */

    public function __set($name, $value)

    {

        $this->$name = $value;

    }

    /**

     * 设置access token

     * @param string $token

     * @throws \Exception

     * @return boolean

     */

    public function setAccessToken()

    {

        try{

            if(!file_exists($this->file) || !is_file($this->file)) {

                $f = fopen($this->file, 'a');

                fclose($f);

            }

            $content = file_get_contents($this->file);

            if(!empty($content)) {

                $info = json_decode($content, true);

                if( time() - $info['getTime'] < 7150 ) {

                    $this->accessToken = $info['accessToken'];

                    return true;

                }

            }

            //文件内容为空或access token已失效,重新获取

            $this->outputAccessTokenToFile();

        } catch(\Exception $e) {

            throw new \Exception($e->getMessage());

        }

        return true;

    }

    /**

     * 写入access token 到文件

     * @throws \Exception

     * @return boolean

     */

    protected function outputAccessTokenToFile()

    {

        try{

            $f = fopen($this->file, 'wb');

            $token = [

                'accessToken' => $this->getAccessToken(),

                'getTime' => time(),

            ];

            flock($f, LOCK_EX);

            fwrite($f, json_encode($token));

            flock($f, LOCK_UN);

            fclose($f);

            $this->accessToken = $token['accessToken'];

        } catch(\Exception $e) {

            throw new \Exception($e->getMessage());

        }

        return true;

    }

    /**

     * 取access token

     * 

     * @throws \Exception

     * @return string

     */

    protected function getAccessToken()

    {

        $url = sprintf(self::ACCESS_TOKEN_URL, $this->appid, $this->appSecret);

        $result = json_decode( $this->getUrl($url), true );

        if(isset($result['errcode'])) {

            throw new \Exception("get access token failed:{$result['errmsg']}");

        }

        return $result['access_token'];

    }

    /**

     * 取预支付会话标识

     * 

     * @throws \Exception

     * @return string

     */

    protected function getPrepayid()

    {

        $data = json_encode($this->config);

        $url = sprintf(self::POST_ORDER_URL, $this->accessToken);

        $result = json_decode( $this->postUrl($url, $data), true );

        if( isset($result['errcode']) && $result['errcode'] != 0 ) {

            throw new \Exception($result['errmsg']);

        }

        if( !isset($result['prepayid']) ) {

            throw new \Exception('get prepayid failed, url request error.');

        }

        return $result['prepayid'];

    }

    /**

     * 组装预支付参数

     * 

     * @throws \Exception

     */

    protected function generateConfig()

    {

        try{

            $this->config = [

                    'appid' => $this->appid,

                    'traceid' => $this->traceid,

                    'noncestr' => $this->getRandomStr(),

                    'timestamp' => time(),

                    'package' => $this->generatePackage(),

                    'sign_method' => $this->sign_method,

            ];

            $this->config['app_signature'] = $this->generateSign();

        } catch(\Exception $e) {

            throw new \Exception($e->getMessage());

        }

    }

    /**

     * 生成package字段

     * 

     * 生成规则:

     * 1、生成sign的值signValue

     * 2、对package参数再次拼接成查询字符串,值需要进行urlencode

     * 3、将sign=signValue拼接到2生成的字符串后面得到最终的package字符串

     * 

     * 第2步urlencode空格需要编码成%20而不是+

     * 

     * RFC 1738会把 空格编码成+

     * RFC 3986会把空格编码成%20

     * 

     * @return string

     */

    protected function generatePackage()

    {

        $this->package['sign'] = $this->signData($this->package);

        return http_build_query($this->package, '', '&', PHP_QUERY_RFC3986);

    }

    /**

     * 生成签名

     * 

     * @return string

     */

    protected function generateSign()

    {

        $signArray = [

            'appid' => $this->appid,

            'appkey' => $this->paySignkey,

            'noncestr' => $this->config['noncestr'],

            'package' => $this->config['package'],

            'timestamp' => $this->config['timestamp'],

            'traceid' => $this->traceid,

        ];

        return $this->sha1Sign($signArray);

    }

    /**

     * 签名数据

     * 

     * 生成规则:

     * 1、字典排序,拼接成查询字符串格式,不需要urlencode

     * 2、上一步得到的字符串最后拼接上key=paternerKey

     * 3、MD5哈希字符串并转换成大写得到sign的值signValue

     * 

     * @param array $data 待签名数据

     * @return string 最终签名结果

     */

    protected function signData($data)

    {

        ksort($data);

        $str = $this->arrayToString($data);

        $str .= "&key={$this->partnerKey}";

        return strtoupper( $this->signMd5($str) );

    }

    /**

     * sha1签名

     * 签名规则

     * 1、字典排序

     * 2、拼接查询字符串

     * 3、sha1运算

     * 

     * @param array $arr

     * @return string

     */

    protected function sha1Sign($arr)

    {

        ksort($arr);

        return sha1( $this->arrayToString($arr) );

    }

}

希望本文所述对大家的php程序设计有所帮助。

PHP 相关文章推荐
discuz 首页四格:最新话题+最新回复+热门话题+精华文章插件
Aug 19 PHP
php用户注册页面利用js进行表单验证具体实例
Oct 17 PHP
php通过数组实现多条件查询实现方法(字符串分割)
May 06 PHP
php接口数据加密、解密、验证签名
Mar 12 PHP
54个提高PHP程序运行效率的方法
Jul 19 PHP
ThinkPHP实现递归无级分类――代码少
Jul 29 PHP
php基础设计模式大全(注册树模式、工厂模式、单列模式)
Aug 31 PHP
php版微信js-sdk支付接口类用法示例
Oct 12 PHP
thinkphp 验证码 的使用小结
May 07 PHP
php微信开发之关注事件
Jun 14 PHP
Laravel5.5以下版本中如何自定义日志行为详解
Aug 01 PHP
php支付宝手机网页支付类实例
Mar 04 #PHP
php银联网页支付实现方法
Mar 04 #PHP
php随机抽奖实例分析
Mar 04 #PHP
php二维数组合并及去重复的方法
Mar 04 #PHP
php中get_cfg_var()和ini_get()的用法及区别
Mar 04 #PHP
php用ini_get获取php.ini里变量值的方法
Mar 04 #PHP
浅谈PHP中单引号和双引号到底有啥区别呢?
Mar 04 #PHP
You might like
如何选购合适的收音机
2021/03/01 无线电
将时间以距今多久的形式表示,PHP,js双版本
2012/09/25 PHP
对象失去焦点时自己动提交数据的实现代码
2012/11/06 PHP
在Laravel 的 Blade 模版中实现定义变量
2019/10/14 PHP
根据判断浏览器类型屏幕分辨率自动调用不同CSS的代码
2007/02/22 Javascript
IE中jquery.form中ajax提交没反应解决方法分享
2012/09/11 Javascript
浅析document.ready和window.onload的区别讲解
2013/12/18 Javascript
escape函数解决js中ajax传递中文出现乱码问题
2014/10/30 Javascript
基于jquery实现的自动补全功能
2015/03/12 Javascript
深入浅析同源策略和跨域访问
2015/11/26 Javascript
Angular 2应用的8个主要构造块有哪些
2016/10/17 Javascript
解析ajaxFileUpload 异步上传文件简单使用
2016/12/30 Javascript
BootStrap学习系列之布局组件(下拉,按钮组[toolbar],上拉)
2017/01/03 Javascript
详解Vue2.0之去掉组件click事件的native修饰
2017/04/20 Javascript
详解webpack+es6+angular1.x项目构建
2017/05/02 Javascript
微信小程序 支付功能(前端)的实现
2017/05/24 Javascript
前端构建工具之gulp的配置与搭建详解
2017/06/12 Javascript
vue 之 .sync 修饰符示例详解
2018/04/21 Javascript
jquery实现二级导航下拉菜单效果实例
2019/05/14 jQuery
pm2启动ssr失败的解决方法
2019/06/29 Javascript
js动态获取时间的方法分析
2019/08/02 Javascript
react 生命周期实例分析
2020/05/18 Javascript
解决vue-router 切换tab标签关闭时缓存问题
2020/07/22 Javascript
three.js 如何制作魔方
2020/07/31 Javascript
vue 使用微信jssdk,调用微信相册上传图片功能
2020/11/13 Javascript
ESLint 是如何检查 .vue 文件的
2020/11/30 Vue.js
详解ES6 中的Object.assign()的用法实例代码
2021/01/11 Javascript
在Python中使用mongoengine操作MongoDB教程
2015/04/24 Python
快速了解Python中的装饰器
2018/01/11 Python
python2与python3共存问题的解决方法
2018/09/18 Python
Python骚操作之动态定义函数
2019/03/26 Python
函授自我鉴定
2013/11/06 职场文书
市场部业务员岗位职责
2014/04/02 职场文书
工商干部先进事迹
2014/05/14 职场文书
2015年劳动部工作总结
2015/05/23 职场文书
启动Tomcat时出现大量乱码的解决方法
2021/06/21 Java/Android