php微信支付之APP支付方法

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

  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程序设计有所帮助。