1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/TheNorthMemory-wechatpay-php

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Это зеркальный репозиторий, синхронизируется ежедневно с исходного репозитория.
Клонировать/Скачать
Внести вклад в разработку кода
Синхронизировать код
Отмена
Подсказка: Поскольку Git не поддерживает пустые директории, создание директории приведёт к созданию пустого файла .keep.
Loading...
README.md

WeChatPay OpenAPI SDK

[A]Sync Chainable WeChatPay v2&v3's OpenAPI SDK for PHP

GitHub actions Packagist Stars Packagist Downloads Packagist Version Packagist PHP Version Support Packagist License

概览

基于 Guzzle HTTP Client 的微信支付 PHP 开发库。

功能介绍

  1. 微信支付 APIv2 和 APIv3 的 Guzzle HTTP 客户端,支持 同步异步 发送请求,并自动进行请求签名和应答验签

  2. 链式实现的 URI Template

  3. 敏感信息加解密

  4. 回调通知的验签和解密

项目状态

当前版本为 1.4.10 版。 项目版本遵循 语义化版本号。 如果你使用的版本 <=v1.3.2,升级前请参考 升级指南

环境要求

项目支持的环境如下:

  • Guzzle 7.0,PHP >= 7.2.5
  • Guzzle 6.5,PHP >= 7.1.2

我们推荐使用目前处于 Active Support 阶段的 PHP 8 和 Guzzle 7。

安装

推荐使用 PHP 包管理工具 Composer 安装 SDK:

composer require wechatpay/wechatpay

开始

:information_source: 以下是 微信支付 API v3 的指引。如果你是 API v2 的使用者,请看 README_APIv2

概念

  • 商户 API 证书,是用来证实商户身份的。证书中包含商户号、证书序列号、证书有效期等信息,由证书授权机构(Certificate Authority ,简称 CA)签发,以防证书被伪造或篡改。详情见 什么是商户API证书?如何获取商户API证书?

  • 商户 API 私钥。你申请商户 API 证书时,会生成商户私钥,并保存在本地证书文件夹的文件 apiclient_key.pem 中。为了证明 API 请求是由你发送的,你应使用商户 API 私钥对请求进行签名。

    :key: 不要把私钥文件暴露在公共场合,如上传到 Github,写在 App 代码中等。

  • 微信支付平台证书。微信支付平台证书是指:由微信支付负责申请,包含微信支付平台标识、公钥信息的证书。你需使用微信支付平台证书中的公钥验证 API 应答和回调通知的签名。

    :bookmark: 通用的 composer 命令,像安装依赖包一样 下载平台证书 文件,供SDK初始化使用。

  • 证书序列号。每个证书都有一个由 CA 颁发的唯一编号,即证书序列号。

  • 微信支付平台公钥,是微信支付平台的公钥,用于应答及回调通知的数据签名,可在 微信支付商户平台 -> 账户中心 -> API安全 直接下载。

  • 微信支付平台公钥ID,是微信支付平台公钥的唯一标识,可在 微信支付商户平台 -> 账户中心 -> API安全 直接查看。

示例程序:微信支付平台证书下载

<?php

require_once('vendor/autoload.php');

use WeChatPay\Builder;
use WeChatPay\Crypto\Rsa;

// 设置参数

// 商户号
$merchantId = '190000****';

// 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名
$merchantPrivateKeyFilePath = 'file:///path/to/merchant/apiclient_key.pem';
$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);

// 「商户API证书」的「证书序列号」
$merchantCertificateSerial = '3775B6A45ACD588826D15E583A95F5DD********';

// 从本地文件中加载「微信支付平台证书」或者「微信支付平台公钥」,用来验证微信支付应答的签名
$platformCertificateOrPublicKeyFilePath = 'file:///path/to/wechatpay/certificate_or_publickey.pem';
$platformPublicKeyInstance = Rsa::from($platformCertificateOrPublicKeyFilePath, Rsa::KEY_TYPE_PUBLIC);

// 「微信支付平台证书」的「证书序列号」或者是「微信支付平台公钥ID」
// 「平台证书序列号」及/或「平台公钥ID」可以从 商户平台 -> 账户中心 -> API安全 直接查询到
$platformCertificateSerialOrPublicKeyId = '7132D72A03E93CDDF8C03BBD1F37EEDF********';

// 构造一个 APIv3 客户端实例
$instance = Builder::factory([
    'mchid'      => $merchantId,
    'serial'     => $merchantCertificateSerial,
    'privateKey' => $merchantPrivateKeyInstance,
    'certs'      => [
        $platformCertificateSerialOrPublicKeyId => $platformPublicKeyInstance,
    ],
]);

// 发送请求
$resp = $instance->chain('v3/certificates')->get(
    /** @see https://docs.guzzlephp.org/en/stable/request-options.html#debug */
    // ['debug' => true] // 调试模式
);
echo (string) $resp->getBody(), PHP_EOL;

文档

同步请求

使用客户端提供的 getputpostpatchdelete 方法发送同步请求。以 ``` $resp = $instance ->chain('v3/pay/transactions/native') ->post(['json' => [ 'mchid' => '1900006XXX', 'out_trade_no' => 'native12177525012014070332333', 'appid' => 'wxdace645e0bc2cXXX', 'description' => 'Image形象店-深圳腾大-QQ公仔', 'notify_url' => 'https://weixin.qq.com/', 'amount' => [ 'total' => 1, 'currency' => 'CNY' ], ]]);

echo $resp->getStatusCode(), PHP_EOL; echo (string) $resp->getBody(), PHP_EOL;


После успешного запроса вы получите объект ответа `GuzzleHttp\Psr7\Response`.
Для дальнейшего изучения того, как получить доступ к информации в ответе, обратитесь к документации Guzzle: «Using Response» (https://docs.guzzlephp.org/en/stable/quickstart.html#using-responses).

### Асинхронный запрос

Чтобы отправить асинхронный запрос, используйте методы `getAsync`, `putAsync`, `postAsync`, `patchAsync` или `deleteAsync`, предоставляемые клиентом. Пример — запрос на возврат средств (https://pay.weixin.qq.com/docs/merchant/apis/native-payment/create.html):

```php
$promise = $instance
->chain('v3/refund/domestic/refunds')
->postAsync([
    'json' => [
        'transaction_id' => '1217752501201407033233368018',
        'out_refund_no'  => '1217752501201407033233368018',
        'amount'         => [
            'refund'   => 888,
            'total'    => 888,
            'currency' => 'CNY',
        ],
    ],
])
->then(static function($response) {
    // Нормальная логика обратного вызова
    echo (string) $response->getBody(), PHP_EOL;
    return $response;
})
->otherwise(static function($e) {
    // Обработка ошибок
    echo $e->getMessage(), PHP_EOL;
    if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
        $r = $e->getResponse();
        echo $r->getStatusCode() . ' ' . $r->getReasonPhrase(), PHP_EOL;
        echo (string) $r->getBody(), PHP_EOL, PHP_EOL, PHP_EOL;
    }
    echo $e->getTraceAsString(), PHP_EOL;
});
// Синхронное ожидание
$promise->wait();

Методы [get|post|put|patch|delete]Async возвращают обещания Guzzle (Guzzle Promises). Вы можете сделать две вещи:

  • В случае успеха используйте then() для обработки полученного Psr\Http\Message\ResponseInterface и, при необходимости, передайте его следующему then().
  • В случае неудачи используйте otherwise() для обработки исключений.

Наконец, используйте wait() для ожидания завершения выполнения запроса.

Синхронный или асинхронный

Большинству разработчиков рекомендуется использовать синхронный режим, поскольку он более понятен.

Если вы опытный разработчик с опытом асинхронного программирования, то в некоторых сценариях последовательного вызова API объединение нескольких операций через then() может быть элегантным решением. Например, потоковая загрузка файлов транзакционных отчётов в виде цепочки функций (https://developers.weixin.qq.com/community/pay/article/doc/000ec4521086b85fb81d6472a51013).

Цепочка URI шаблонов

URI Template (https://www.rfc-editor.org/rfc/rfc6570.html) — это способ выражения переменных в URI. WeChat Pay API использует этот метод для представления номеров или идентификаторов заказов в URL Path.

# Использование номера заказа WeChat для запроса заказа
GET /v3/pay/transactions/id/{transaction_id}

# Использование торгового номера продавца для запроса заказа
GET /v3/pay/transactions/out-trade-no/{out_trade_no}

Используя цепочку URI Templates (chain URI Template), вы можете легко писать URL, как код, и легко вводить пути и передавать параметры URL. После настройки пакета описания интерфейса вы также можете включить подсказки IDE (https://github.com/TheNorthMemory/wechatpay-openapi).

Основные элементы цепочки — сегменты в URI Path (segments), которые соединяются знаком ->. Правила соединения следующие:

  • Обычный сегмент
    • Пишите напрямую. Например, v3->pay->transactions->native.
    • Используйте chain(). Например, chain('v3/pay/transactions/native')
  • Сегмент, содержащий дефис (-)
    • Напишите в стиле верблюжьего регистра (camelCase). Например, merchant-service можно записать как merchantService.
    • Используйте формат {foo-bar}. Например, {merchant-service}.
  • Переменная пути в URL. Следует использовать этот формат, чтобы избежать самостоятельной сборки или использования chain(), что может привести к ошибкам в обработке регистра.
    • Рекомендуется использовать _variable_name_ для поддержки подсказок IDE. Например, v3->pay->transactions->id->_transaction_id_.
    • Использовать формат {'{variable_name}'} для написания. Например, v3->pay->transactions->id->{'{transaction_id}'}
  • Метод HTTP METHOD запроса как последнее действие в цепочке. Например, v3->pay->transactions->native->post([ ... ])
  • Значение переменной пути передаётся как параметр с тем же именем.
  • Параметры запроса передаются как параметр query. ``` $inWechatpayTimestamp = ''; // 请根据实际情况获取 $inWechatpaySerial = ''; // 请根据实际情况获取 $inWechatpayNonce = ''; // 请根据实际情况获取

$inBody = ''; // 请根据实际情况获取,例如: file_get_contents('php://input');

$apiv3Key = ''; // 在商户平台上设置的APIv3密钥

// 根据通知的平台证书序列号,查询本地平台证书文件, // 假定为 /path/to/wechatpay/inWechatpaySerial.pem $platformPublicKeyInstance = Rsa::from('file:///path/to/wechatpay/inWechatpaySerial.pem', Rsa::KEY_TYPE_PUBLIC);

// 检查通知时间偏移量,允许5分钟之内的偏移 $timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp); $verifiedStatus = Rsa::verify( // 构造验签名串 Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody), $inWechatpaySignature, $platformPublicKeyInstance ); if ($timeOffsetStatus && $verifiedStatus) { // 转换通知的JSON文本消息为PHP Array数组 $inBodyArray = (array)json_decode($inBody, true); // 使用PHP7的数据解构语法,从Array中解构并赋值变量 ['resource' => [ 'ciphertext' => $ciphertext, 'nonce' => $nonce, 'associated_data' => $aad ]] = $inBodyArray; // 加密文本消息解密 $inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad); // 把解密后的文本转换为PHP Array数组 $inBodyResourceArray = (array)json_decode($inBodyResource, true); // print_r($inBodyResourceArray);// 打印解密后的结果 }


Введение

Синхронный Chainable WeChatPay v2&v3 OpenAPI SDK для PHP. Расширить Свернуть
PHP и 2 других языков
Apache-2.0
Отмена

Обновления

Пока нет обновлений

Участники

все

Недавние действия

Загрузить больше
Больше нет результатов для загрузки
1
https://gitlife.ru/oschina-mirror/TheNorthMemory-wechatpay-php.git
git@gitlife.ru:oschina-mirror/TheNorthMemory-wechatpay-php.git
oschina-mirror
TheNorthMemory-wechatpay-php
TheNorthMemory-wechatpay-php
main