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 相关文章推荐
MYSQL数据库初学者使用指南
Nov 16 PHP
dedecms系统的广告设置代码 基础版本
Apr 09 PHP
PHP下操作Linux消息队列完成进程间通信的方法
Jul 24 PHP
php中var_export与var_dump的区别分析
Aug 21 PHP
PH P5.2至5.5、5.6的新增功能详解
Jul 14 PHP
Laravel 5框架学习之用户认证
Apr 09 PHP
ThinkPHP路由机制简介
Mar 23 PHP
php中青蛙跳台阶的问题解决方法
Oct 14 PHP
PHP利用DWZ.CN服务生成短网址
Aug 11 PHP
php 使用 __call实现重载功能示例
Nov 18 PHP
Laravel框架数据库迁移操作实例详解
Apr 06 PHP
Thinkphp集成抖音SDK的实现方法
Apr 28 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
全国FM电台频率大全 - 11 浙江省
2020/03/11 无线电
discuz7 phpMysql操作类
2009/06/21 PHP
php和数据库结合的一个简单的web实例 代码分析 (php初学者)
2011/07/28 PHP
Linux下安装oracle客户端并配置php5.3
2014/10/12 PHP
PHP new static 和 new self详解
2017/02/19 PHP
PHP中PCRE正则解析代码详解
2019/04/26 PHP
Javascript 的addEventListener()及attachEvent()区别分析
2009/05/21 Javascript
JavaScript高级程序设计 读书笔记之九 本地对象Array
2012/02/27 Javascript
JQuery实现当鼠标停留在某区域3秒后自动执行
2014/09/09 Javascript
javascript学习笔记(五)原型和原型链详解
2014/10/08 Javascript
超实用的JavaScript表单代码段
2016/02/26 Javascript
jQuery 自定义下拉框(DropDown)附源码下载
2016/07/22 Javascript
JavaScript体验异步更好的解决办法
2018/01/08 Javascript
node实现生成带参数的小程序二维码并保存到本地功能示例
2018/12/05 Javascript
Vue+Typescript中在Vue上挂载axios使用时报错问题
2019/08/07 Javascript
Openlayers实现地图全屏显示
2020/09/28 Javascript
[41:17]完美世界DOTA2联赛PWL S3 access vs CPG 第二场 12.13
2020/12/17 DOTA
Python字符串格式化%s%d%f详解
2018/02/02 Python
解决pip install的时候报错timed out的问题
2018/06/12 Python
python 搜索大文件的实例代码
2019/07/08 Python
TensorFlow设置日志级别的几种方式小结
2020/02/04 Python
使用Tensorflow-GPU禁用GPU设置(CPU与GPU速度对比)
2020/06/30 Python
Jupyter notebook命令和编辑模式常用快捷键汇总
2020/11/17 Python
css3加js做一个简单的3D行星运转效果实例代码
2017/01/18 HTML / CSS
详解如何获取localStorage最大存储大小的方法
2020/05/21 HTML / CSS
印尼在线购买隐形眼镜网站:Lensza.co.id
2019/04/27 全球购物
英格兰足协官方商店:England Store
2019/07/12 全球购物
欢迎领导标语
2014/06/27 职场文书
四风问题个人自查剖析材料思想汇报
2014/09/21 职场文书
2014年食堂工作总结
2014/11/20 职场文书
迎新生晚会主持词
2015/06/30 职场文书
创业计划书之寿司
2019/07/19 职场文书
Oracle 区块链表创建过程详解
2021/05/15 Oracle
浅谈Go语言多态的实现与interface使用
2021/06/16 Golang
详解MongoDB的条件查询和排序
2021/06/23 MongoDB
详解JavaScript的计时器和按钮效果设置
2022/02/18 Javascript