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 相关文章推荐
基于PHP+MySQL的聊天室设计
Oct 09 PHP
将文件夹压缩成zip文件的php代码
Dec 14 PHP
PHP生成随机密码类分享
Jun 25 PHP
php使用pdo连接mssql server数据库实例
Dec 25 PHP
php中动态调用函数的方法
Mar 16 PHP
php实现比较两个字符串日期大小的方法
May 12 PHP
PHP读取mssql json数据中文乱码的解决办法
Apr 11 PHP
php文件系统处理方法小结
May 23 PHP
PHP使用FFmpeg获取视频播放总时长与码率等信息
Sep 13 PHP
[原创]php实现数组按拼音顺序排序的方法
May 03 PHP
PHP排序算法之直接插入排序(Straight Insertion Sort)实例分析
Apr 20 PHP
Laravel学习笔记之Artisan命令生成自定义模板的方法
Nov 22 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 全角转半角实现代码
2010/05/16 PHP
解析php如何将日志写进syslog
2013/06/28 PHP
Javascript模块化编程(三)require.js的用法及功能介绍
2013/01/17 Javascript
使用js 设置url参数
2013/07/08 Javascript
Ajax清除浏览器js、css、图片缓存的方法
2015/08/06 Javascript
JS+CSS实现的经典圆角下拉菜单效果代码
2015/10/21 Javascript
深入理解jquery自定义动画animate()
2016/05/24 Javascript
Node.js包管理器Yarn的入门介绍与安装
2016/10/17 Javascript
微信小程序后台解密用户数据实例详解
2017/06/28 Javascript
nodejs 子进程正确的打开方式
2017/07/03 NodeJs
Bootstrap Table中的多选框删除功能
2018/07/15 Javascript
VUE中使用MUI方法
2019/02/12 Javascript
原生JS实现留言板
2020/03/26 Javascript
Vue常用API、高级API的相关总结
2021/02/02 Vue.js
朴素贝叶斯算法的python实现方法
2014/11/18 Python
Python爬虫代理IP池实现方法
2017/01/05 Python
简单谈谈Python中的几种常见的数据类型
2017/02/10 Python
Python编程实现双击更新所有已安装python模块的方法
2017/06/05 Python
利用python将图片转换成excel文档格式
2017/12/30 Python
Django实现组合搜索的方法示例
2018/01/23 Python
详解关于Django中ORM数据库迁移的配置
2018/10/08 Python
Python装饰器用法实例分析
2019/01/14 Python
python如何保证输入键入数字的方法
2019/08/23 Python
基于python全局设置id 自动化测试元素定位过程解析
2019/09/04 Python
Python flask框架端口失效解决方案
2020/06/04 Python
Omio意大利:全欧洲低价大巴、火车和航班搜索和比价
2017/12/02 全球购物
语文教育专业应届生求职信
2013/11/23 职场文书
英语系本科生求职信
2014/07/15 职场文书
失职检讨书大全
2015/01/26 职场文书
清明节网上祭英烈寄语2015
2015/03/04 职场文书
2015年社区精神文明工作总结
2015/05/26 职场文书
2015年乡镇食品安全工作总结
2015/10/22 职场文书
2016党员干部政治学习心得体会
2016/01/23 职场文书
2016高校自主招生自荐信范文
2016/01/28 职场文书
《烈火英雄》观后感:致敬和平时代的英雄
2019/11/11 职场文书
AJAX引擎原理以及XmlHttpRequest对象的axios、fetch区别详解
2022/04/09 Javascript