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 相关文章推荐
vs中通过剪切板循环来循环粘贴不同内容
Apr 30 PHP
php重定向的三种方法分享
Feb 22 PHP
批量去除PHP文件中bom的PHP代码
Mar 13 PHP
ThinkPHP后台首页index使用frameset时的注意事项分析
Aug 22 PHP
PHP动态编译出现Cannot find autoconf的解决方法
Nov 05 PHP
php中addslashes函数与sql防注入
Nov 17 PHP
php使用parse_url和parse_str解析URL
Feb 22 PHP
php中namespace use用法实例分析
Jan 22 PHP
PHP实现QQ快速登录的方法
Sep 28 PHP
PHP数组操作实例分析【添加,删除,计算,反转,排序,查找等】
Dec 24 PHP
Laravel框架控制器的middleware中间件用法分析
Sep 30 PHP
使用Rancher在K8S上部署高性能PHP应用程序的教程
Jul 10 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
php下的权限算法的实现
2007/04/28 PHP
php面向对象全攻略 (十四) php5接口技术
2009/09/30 PHP
php邮件发送,php发送邮件的类
2011/03/24 PHP
php中数组首字符过滤功能代码
2012/07/31 PHP
php使用pack处理二进制文件的方法
2014/07/03 PHP
9个经典的PHP代码片段分享
2014/12/18 PHP
php基于闭包实现函数的自调用(递归)实例分析
2016/11/11 PHP
javascript Zifa FormValid 0.1表单验证 代码打包下载
2007/06/08 Javascript
javascript游戏开发之《三国志曹操传》零部件开发(四)用地图块拼成大地图
2013/01/23 Javascript
jquery判断小数点两位和自动删除小数两位后的数字
2014/03/19 Javascript
js写出遮罩层登陆框和对联广告并自动跟随滚动条滚动
2014/04/29 Javascript
Bootstrap Modal对话框如何在关闭时触发事件
2016/12/02 Javascript
Require.JS中的几种define定义方式示例
2017/06/01 Javascript
vue-router单页面路由
2017/06/17 Javascript
angular.js + require.js构建模块化单页面应用的方法步骤
2017/07/19 Javascript
vue父组件向子组件传递多个数据的实例
2018/03/01 Javascript
js计算两个日期间的天数月的实例代码
2018/09/20 Javascript
解决VUE项目使用Element-ui 下拉组件的验证失效问题
2020/11/07 Javascript
[01:20]2018DOTA2亚洲邀请赛总决赛战队Mineski晋级之路
2018/04/07 DOTA
pyqt4教程之实现半透明的天气预报界面示例
2014/03/02 Python
python常见的格式化输出小结
2016/12/15 Python
python实现隐马尔科夫模型HMM
2018/03/25 Python
Python符号计算之实现函数极限的方法
2019/07/15 Python
关于初始种子自动选取的区域生长实例(python+opencv)
2020/01/16 Python
Python requests设置代理的方法步骤
2020/02/23 Python
说出数据连接池的工作机制是什么?
2013/04/19 面试题
大学生自我鉴定
2013/12/08 职场文书
高三毕业生自我鉴定
2013/12/20 职场文书
毕业实习评语
2014/02/10 职场文书
自荐书范文范例
2014/02/13 职场文书
公务员试用期满考核材料
2014/05/22 职场文书
2014年学生党支部工作总结
2014/12/20 职场文书
考研复习计划
2015/01/19 职场文书
放假通知范文
2015/04/14 职场文书
2019通用版劳动合同范本!
2019/07/11 职场文书
2021年国产动漫公司排行前十名,玄机科技上榜,第二推出过铠甲勇士
2022/03/18 杂记