IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> PHP知识库 -> PHP微信支付之navicate支付(tp6) -> 正文阅读

[PHP知识库]PHP微信支付之navicate支付(tp6)

版本:v3

适用语言:PHP(建议7.2以上,本人8.0)、tp6

官方文档:

微信支付开发者文档

说明:微信支付有多种实现方式,此处我使用两种方式来实现,一种是使用官方sdk改写(建议使用),一种是直接调用接口。

  • 目录结构
    1. cert目录:存放证书目录(如果是Linux服务器,需赋予创建文件权限)
    2. config目录:配置文件目录
    3. controller目录:
      1. Demo.php 请求示例demo
      2. AesUtil.php 解密方法
      3. GetCert.php 下载微信支付证书方法
      4. Notify.php 微信支付成功回调示例
      5. Wechat.php 微信支付下单示例
    4. route目录:路由

二、代码说明

  1. 直接调用

说明:直接调用比较简单,url地址填写navicate下单地址即可,请求参数安装文档填写必须参数即可,此处需注意:下单接口文档并没有提及请求header的内容,但是此处是必须要添加请求header!!!具体header组成如下图代码所示。特别是'Authorization在文章下面我写有生成的该参数值的方法。

/**
 *
 * navicate支付,直接调用
 */
public function navicate2()
{
    $uri = 'https://api.mch.weixin.qq.com/v3/pay/transactions/native';
    $data = [
        "amount" => [
            "total" => 1,
            "currency" => "CNY",
        ],
        "mchid" => "商户号",
        "description" => "微信支付测试",
        "notify_url" => "回调地址",
        "out_trade_no" => "订单号",
        "appid" => "appid",
    ];
    $header = [
        'Authorization: WECHATPAY2-SHA256-RSA2048 mchid="商户号",nonce_str="随机字符串",timestamp="时间戳",serial_no="证书序列表",signature="签名"',  //  身份认证信息
        'Content-Type:application/json',
        'Accept:application/json',
        'User-Agent:' . $_SERVER['HTTP_USER_AGENT']
    ];
    $res = curlPost($uri, $data, 2, $header);
    halt($res);
}

  1. Sdk使用(我用的是开发者版本的sdk)

文档地址:

微信支付-开发者文档

第一步:composer安装

composer require wechatpay/wechatpay-guzzle-middleware

第二步:开始编写代码,此处需要注意的是需要两个密钥文件都需要放在可访问目录下(商户私钥和微信支付平台证书),商户密钥是从微信支付平台下载的,微信支付平台证书是通过代码进行下载的,下载网址为:https://api.mch.weixin.qq.com/v3/certificates具体方法在下方展示。此处代码为navicate下单部分关键代码,一些作用类的代码在下方。

2.1 WeChat.php代码如下

<?php

declare (strict_types=1);

namespace app\pay\controller;


use GuzzleHttp\Client;

use GuzzleHttp\Exception\RequestException;

use GuzzleHttp\HandlerStack;

use WechatPay\GuzzleMiddleware\Util\PemUtil;

use WechatPay\GuzzleMiddleware\WechatPayMiddleware;


class Wechat

{


????/**

?????*

?????* 微信支付,此处需要有创建文件权限

?????*/

????private $client; ???// ?微信支付client

????private $merchantId; ???// ?商户号

????private $apiclient_key; // ?商户私钥地址

????private $wechat_key; // ?微信支付平台证书地址

????private $resArr; ???// ?返回错误信息

????private $appid; // ?公众号/小程序/app - appid

????const TYPE = 'wechat';

????public function __construct($type = 1)

????{

????????????$this->resArr = [

????????????'code' => 0,

????????????'msg' => 'success',

????????????'data' => ''

????????];

????????$this->apiclient_key = root_path() . '/app/pay/cert/apiclient_key.pem';

????????$this->wechat_key = root_path() . '/app/pay/cert/wechat.pem';

????????$this->appid = config('wechat.appid');

????????// 商户相关配置

????????$this->merchantId = config('wechat.merchantId'); // 商户号

????????$merchantSerialNumber = config('wechat.merchantSerialNumber'); // 商户API证书序列号

????????$merchantPrivateKey = PemUtil::loadPrivateKey($this->apiclient_key); // 商户私钥

????????// ?检测微信支付平台证书是否存在,不存在重新生成

????????if (!file_exists($this->wechat_key)) {

????????????$cert = new GetCert($this->apiclient_key);

????????????$res = $cert->makeCert();

????????????if (!$res) {

????????????????$this->resArr['code'] = -1;

????????????????$this->resArr['msg'] = '证书下载失败,请检查是否相关目录是否赋予权限!';

????????????????return $this->resArr;

????????????}

????????}

????????// 微信支付平台配置

????????$wechatpayCertificate = PemUtil::loadCertificate($this->wechat_key); // 微信支付平台证书

????????// 构造一个WechatPayMiddleware

????????$wechatpayMiddleware = WechatPayMiddleware::builder()

????????????->withMerchant($this->merchantId, $merchantSerialNumber, $merchantPrivateKey) // 传入商户相关配置

????????????->withWechatPay([$wechatpayCertificate]) // 可传入多个微信支付平台证书,参数类型为array

????????????->build();

????????// 将WechatPayMiddleware添加到Guzzle的HandlerStack中

????????$stack = HandlerStack::create();

????????$stack->push($wechatpayMiddleware, 'wechatpay');

????????// 创建Guzzle HTTP Client时,将HandlerStack传入

????????$this->client = new Client(['handler' => $stack]);

????}



????/**

?????* navicate下单

?????* $total:金额,单位(分)

?????* $out_trade_no:订单号

?????* $notify_url:回调地址

?????*/

????public function navicate($total = 1, $out_trade_no = '', $notify_url = '')

????{

????????try {

????????????$resp = $this->client->request(

????????????????'POST',

????????????????'https://api.mch.weixin.qq.com/v3/pay/transactions/native', // ?请求URL

????????????????[

????????????????????// JSON请求体

????????????????????'json' => [

????????????????????????"amount" => [

????????????????????????????"total" => $total,

????????????????????????????"currency" => "CNY",

????????????????????????],

????????????????????????"mchid" => $this->merchantId,

????????????????????????"description" => "微信支付测试",

????????????????????????"notify_url" => $notify_url,

????????????????????????"out_trade_no" => $out_trade_no,

????????????????????????"appid" => $this->appid,

????????????????????],

????????????????????'headers' => ['Accept' => 'application/json']

????????????????]

????????????);

????????????$statusCode = $resp->getStatusCode();

????????????if ($statusCode == 200) { //处理成功

????????????????$res = json_decode($resp->getBody()->getContents());

????????????????$this->resArr['data'] = $res->code_url;

????????????????return $this->resArr; ?// ?返回微信二维码地址

????????????} else if ($statusCode == 204) { //处理成功,无返回Body

????????????????$this->resArr['msg'] = '暂无body';

????????????????return $this->resArr;;

????????????}

????????} catch (RequestException $e) {

????????????// 进行错误处理

????????????echo $e->getMessage() . "\n";

????????????$this->resArr['code'] = -1;

????????????$this->resArr['msg'] = $e->getMessage();

????????????if ($e->hasResponse()) {

????????????????$this->resArr['msg'] = "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";

????????????}

????????????return $this->resArr;

????????}

????}

???????/**

?????* 查询订单-通过微信支付订单

?????*/

????public function findByTransactionId($id)

????{

????????try {

????????????$resp = $this->client->request(

????????????????'GET',

????????????????'https://api.mch.weixin.qq.com/v3/pay/transactions/id/' . $id . '?mchid=' . $this->merchantId // ?请求URL

????????????);

????????????$statusCode = $resp->getStatusCode();

????????????if ($statusCode == 200) { //处理成功

????????????????$res = json_decode($resp->getBody()->getContents());

????????????????$this->resArr['data'] = $res;

????????????????return $this->resArr; ?// ?返回微信二维码地址

????????????} else if ($statusCode == 204) { //处理成功,无返回Body

????????????????$this->resArr['msg'] = '暂无body';

????????????????return $this->resArr;;

????????????}

????????} catch (RequestException $e) {

????????????// 进行错误处理

????????????echo $e->getMessage() . "\n";

????????????$this->resArr['msg'] = $e->getMessage();

????????????if ($e->hasResponse()) {

????????????????$this->resArr['msg'] = "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";

????????????}

????????????return $this->resArr;

????????}



????}


????/**

?????* 查询订单-通过商户订单号

?????*/

????public function findByTransactionOrder($orderId)

????{

????????try {

????????????$resp = $this->client->request(

????????????????'GET',

????????????????'https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/' . $orderId . '?mchid=' . $this->merchantId // ?请求URL

????????????);

????????????$statusCode = $resp->getStatusCode();

????????????if ($statusCode == 200) { //处理成功

????????????????$res = json_decode($resp->getBody()->getContents());

????????????????$this->resArr['data'] = $res;

????????????????return $this->resArr; ?// ?返回微信二维码地址

????????????} else if ($statusCode == 204) { //处理成功,无返回Body

????????????????$this->resArr['msg'] = '暂无body';

????????????????return $this->resArr;;

????????????}

????????} catch (RequestException $e) {

????????????// 进行错误处理

????????????echo $e->getMessage() . "\n";

????????????$this->resArr['msg'] = $e->getMessage();

????????????if ($e->hasResponse()) {

????????????????$this->resArr['msg'] = "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";

????????????}

????????????return $this->resArr;

????????}

????}

}

2.2 Demo.php 代码如下

<?php

namespace app\pay\controller;

class Demo

{

????/**

?????* 说明:

?????* 1.可自行选择安装sodium扩展

?????* 2.参数请自行配置,所需参数:商户密钥文件、商户号、appid、v3密钥、证书序列号等

?????* 3.wechat控制器:封装支付、查询等接口方法

?????* 4.Notify控制器:回调通知示例

?????* 5.GetCert控制器:封装下载证书方法,参数无误可直接使用

?????* 6.AesUtil控制器:封装解密方法

????*/


????/**

?????* navicate支付

?????* 参数:{ total:金额,单位(分),out_trade_no:订单号,notify_url }

????*/

???function navicate(){

???????$pay = new Wechat('wechat');

???????// ??参数请看上面注释

???????$res = $pay->navicate(1, 'br' . time() . '1654', 'https://api.open.sdbaizhi.com/open/site/not');

???????halt($res);

???}

}

2.3 AesUtil.php 代码如下

<?php



namespace app\pay\controller;



class AesUtil

{

????/**

?????* AES key

?????*

?????* @var string

?????*/

????private $aesKey;

????const KEY_LENGTH_BYTE = 32;

????const AUTH_TAG_LENGTH_BYTE = 16;


????/**

?????* Constructor

?????*/

????public function __construct($aesKey)

????{

????????if (strlen($aesKey) != self::KEY_LENGTH_BYTE) {

????????????throw new InvalidArgumentException('无效的ApiV3Key,长度应为32个字节');

????????}

????????$this->aesKey = $aesKey;

????}



????/**

?????* Decrypt AEAD_AES_256_GCM ciphertext

?????*

?????* @param string $associatedData AES GCM additional authentication data

?????* @param string $nonceStr AES GCM nonce

?????* @param string $ciphertext AES GCM cipher text

?????*

?????* @return string|bool ?????Decrypted string on success or FALSE on failure

?????*/

????public function decryptToString($associatedData, $nonceStr, $ciphertext)

????{

????????$ciphertext = \base64_decode($ciphertext);

????????if (strlen($ciphertext) <= self::AUTH_TAG_LENGTH_BYTE) {

????????????return false;

????????}

????????// ext-sodium (default installed on >= PHP 7.2)

????????if (function_exists('\sodium_crypto_aead_aes256gcm_is_available') && \sodium_crypto_aead_aes256gcm_is_available()) {

???????????return \sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->aesKey);

??????}



????????// ext-libsodium (need install libsodium-php 1.x via pecl)

????????if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') && \Sodium\crypto_aead_aes256gcm_is_available()) {

????????????return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->aesKey);

??????}

????????// openssl (PHP >= 7.1 support AEAD)

????????if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) {

????????????$ctext = substr($ciphertext, 0, -self::AUTH_TAG_LENGTH_BYTE);

????????????$authTag = substr($ciphertext, -self::AUTH_TAG_LENGTH_BYTE);



????????????return \openssl_decrypt($ctext, 'aes-256-gcm', $this->aesKey, \OPENSSL_RAW_DATA, $nonceStr,

????????????$authTag, $associatedData);

??????}


????????throw new \RuntimeException('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php');

????}

}

2.4 GetCert.php 代码如下

<?php

declare (strict_types=1);

namespace app\pay\controller;


class GetCert

{

????/**

?????* 通过接口获取微信支付平台证书

?????*

?????*/

????private $apiclient_key; // ?商户私钥文件目录

????public function __construct($apiclient_key)

????{

????????$this->apiclient_key = !empty($apiclient_key) ?? root_path() . '/app/pay/cert/apiclient_key.pem';

????}


????/**

?????* 下载证书

?????*/

????public function makeCert()

????{

????????// ?获取平台证书列表url地址

????????$uri = 'https://api.mch.weixin.qq.com/v3/certificates';

????????// ?请求头部

????????$header = [

????????????'Authorization:' . $this->makeAuthorization(), ?// ?身份认证信息

????????????'Content-Type:application/json',

????????????'Accept:application/json',

????????????'User-Agent:' . $_SERVER['HTTP_USER_AGENT']

????????];

????????// ?获取请求结果

????????$res = json_decode(curlGet($uri, $header), true);

????????$associated_data = '';

????????$nonce = '';

????????$ciphertext = '';

????????foreach ($res['data'] as $k => $v) {

????????????$associated_data = $v['encrypt_certificate']['associated_data'];

????????????$nonce = $v['encrypt_certificate']['nonce'];

????????????$ciphertext = $v['encrypt_certificate']['ciphertext'];

????????}

????????$aesUitl = new AesUtil(config('wechat.aesKey'));

????????// ?证书解密

????????$certText = $aesUitl->decryptToString($associated_data, $nonce, $ciphertext);

????????// ?生成证书文件

????????$filePath = root_path() . '/app/pay/cert/wechat.pem';

????????$file = fopen($filePath, 'a');

????????fwrite($file, $certText);

????????fclose($file);

????????if (file_exists($filePath)) return true;

????????return false;

????}


????/**

?????* 生成身份认证信息

?????*/

????protected function makeAuthorization($http_method = 'GET')

????{

????????$url = 'https://api.mch.weixin.qq.com/v3/certificates'; // ?请求地址

????????$timestamp = time(); ???// ?时间戳

????????$nonce = getRandStr(); ?// ?随机字符串

????????$body = ''; // ?请求体

????????$url_parts = parse_url($url);

????????$canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));

????????$message = $http_method . "\n" .

????????????$canonical_url . "\n" .

????????????$timestamp . "\n" .

????????????$nonce . "\n" .

????????????$body . "\n";

????????// ?读取商户私钥

????????$mch_private_key = file_get_contents($this->apiclient_key);

????????// ?使用商户私钥对待签名串进行SHA256 with RSA签名

????????openssl_sign($message, $raw_sign, $mch_private_key, 'sha256WithRSAEncryption');

????????// ?对签名结果进行Base64编码得到签名值

????????$sign = base64_encode($raw_sign);

????????// ?组合身份认证信息

????????$schema = 'WECHATPAY2-SHA256-RSA2048';

????????$token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',

????????????$this->merchantId, $nonce, $timestamp, config('wechat.merchantSerialNumber'), $sign);

????????return $schema . ' ' . $token;

????}

}

2.5 Notify.php 代码如下

<?php

declare (strict_types=1);

namespace app\pay\controller;


use app\common\model\Order;


class Notify

{


????/**

?????*

?????* 回调通知

?????* 此处只选择我认为可用部分,其他内容请根据文档自行定义

?????* 微信签名暂未认证

????*/

????public function notify()

????{

????????// ?接收微信回调通知并转成数组,注意:v3版本以json格式回调,不再使用xml格式;以post请求

????????$postData = input('post.');

????????if ($postData['summary'] != '支付成功') return false;

????????$sign = request()->header('Wechatpay-Signature'); ??// ?接收微信签名

????????// ?信息解密

????????$associated_data = $postData['resource']['associated_data'];

????????$nonce = $postData['resource']['nonce'];

????????$ciphertxt = $postData['resource']['ciphertext'];

????????$aes = new AesUtil(config('wechat.aesKey'));

????????$res = json_decode($aes->decryptToString($associated_data, $nonce, $ciphertxt), true); ?// ?解密文件并生成数组

????????if (empty($res)) return false;

????????// ?判断appid与商户id是否一致

????????if ($res['appid'] != config('wechat.appid') && $res['mchid'] != config('wechat.merchantId')) return false;

????????// ?其他信息暂未列出,具体根据自己实际情况使用

????????$orderData = [

????????????'out_trade_no' => $res['out_trade_no'], ??// ?订单号

????????????'transaction_id' => $res['transaction_id'], ??// ?微信支付订单号

????????????'trade_type' => $res['trade_type'], ??// ?交易类型

????????????'total' => $res['amount']['total'], ??// ?订单总金额,单位(分)

????????????'payer_total' => $res['amount']['payer_total'], ??// ?用户支付金额,单位(分)

????????????'openid' => $res['payer']['openid'], ??// ?支付者openid

????????????'success_time' => $res['success_time'] ?// ?支付完成时间

????????];

????????$order = new Order();

????????try {

????????????$saveData = $order->save($orderData);

????????????if ($saveData !== false) {

????????????????json(['code' => 'SUCCESS', 'message' => '成功']);

????????????}

????????????json(['code' => 'ERRPR', 'message' => '失败']);

????????} catch (\Exception $e) {

????????????echo $e->getMessage();

????????????exit();

????????}

????}

}

2.6 config里面参数如下

<?php

// ?微信配置

return [

????'merchantId' => '商户号', ??// 微信支付商户号

????'merchantSerialNumber' => '证书序列号', ??// ?证书序列号

????'appid' => 'appid', ???// ?公众号appid

????'aesKey' => 'v3密钥', ???// ?v3密钥

];

更多信息:柏知网

  PHP知识库 最新文章
Laravel 下实现 Google 2fa 验证
UUCTF WP
DASCTF10月 web
XAMPP任意命令执行提升权限漏洞(CVE-2020-
[GYCTF2020]Easyphp
iwebsec靶场 代码执行关卡通关笔记
多个线程同步执行,多个线程依次执行,多个
php 没事记录下常用方法 (TP5.1)
php之jwt
2021-09-18
上一篇文章      下一篇文章      查看所有文章
加:2022-01-08 13:45:21  更:2022-01-08 13:45:51 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 15:11:25-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码