Руководство по обновлению
Версия 1.4 предоставляет ограниченную поддержку Guzzle6, которая совместима как минимум с версией v6.5.0. Это связано с тем, что метод reset()
в классе GuzzleHttp\Handler\MockHandler
, который используется для тестирования, доступен только в этой версии. Подробности можно найти в Guzzle #2143.
Для Guzzle6 требуется PHP версии >= 5.5. При обратной совместимости библиотека использует функцию serialNumberHex support
из PHP (#7151), которая доступна начиная с PHP 7.1.2.
Чтобы обеспечить ограниченную совместимость с Guzzle6, библиотека отказывается от использования методов jsonEncode
и jsonDecode
в \GuzzleHttp\Utils
и вместо этого использует собственные методы json_encode
и json_decode
. В крайних случаях (например, при некорректных метаданных) это может привести к замене исключений, которые должны быть выданы клиенту, на исключения, выдаваемые сервером, в нескольких интерфейсах APIv3 для загрузки медиафайлов. Эта ситуация может усложнить отладку, но она считается контролируемой, поэтому использование методов \GuzzleHttp\Utils
было приостановлено. Планируется вернуться к их использованию после завершения поддержки Guzzle6.
Предупреждение: PHP 7.1 завершил официальную поддержку 1 декабря 2019 года, и эта библиотека также имеет ограниченную поддержку на PHP 7.1. Разработчики и продавцы должны самостоятельно оценить риски продолжения использования PHP 7.1.
Кроме того, тестовые примеры, зависящие от PHPUnit8, были скорректированы до версии v8.5.16, поскольку используемый метод TestCase::expectError
содержит ошибку в версиях PHP 7.4 и 8.0, согласно bug #4663.
В средах Guzzle7+PHP7.2/7.3/7.4/8.0 обновление не влияет.
Основное изменение в версии 1.3 — добавление подсказок для интерфейсов и параметров в IDE. Рекомендуется устанавливать этот пакет отдельно с помощью команды composer --dev
(Add requirement to require-dev.
). Производственная среда не требует этого обновления.
Обновление до версии 1.2 включает усиление обработки RSA-ключей и выпуск функции Rsa::from
, которая заменяет PemUtil::loadPrivateKey
. Также были выпущены методы Rsa::fromPkcs1
, Rsa::fromPkcs8
, Rsa::fromSpki
и Rsa::pkcs1ToSpki
. Они поддерживают загрузку ключей без потери точности непосредственно из облачных хранилищ, таких как базы данных или NoSQL.
Rsa::from
поддерживает загрузку ключей из файлов, строк, полных строк RSA-ключей или X509-сертификатов. Тестовые примеры доступны в файле tests/Crypto/RsaTest.php.Rsa::fromPkcs1
— синтаксический сахар для загрузки ключей в формате PKCS#1, где входными данными является строка в кодировке base64.Rsa::fromPkcs8
— синтаксический сахар для загрузки закрытых ключей в формате PKCS#8, где входными данными является строка в кодировке base64.Rsa::fromSpki
— синтаксический сахар для загрузки открытых ключей в формате SPKI, где входными данными является строка в кодировке base64.Rsa::pkcs1ToSpki
— функция преобразования формата RSA-ключа, где входными данными также является строка в кодировке base64.Особенно важно, что для функции APIv2, позволяющей оплачивать банковские карты, теперь можно шифровать конфиденциальную информацию. Для этого можно использовать открытый ключ, полученный через интерфейс get RSA public key, загрузив его с помощью Rsa::from($pub_key, Rsa::KEY_TYPE_PUBLIC)
и используя Rsa::encrypt
. Подробные инструкции можно найти в README.
Методы PemUtil::loadPrivateKey
и PemUtil::loadPrivateKeyFromString
помечены как «не рекомендуется», и текущая поддержка версий v1.1 и v1.0 будет продолжена. Ожидается, что эти методы будут удалены в версии v2.0.
Рекомендуется обновлять загрузку RSA-ключей следующим образом:
Загрузка закрытого ключа продавца из файла:
+use WeChatPay\Crypto\Rsa;
-$merchantPrivateKeyFilePath = '/path/to/merchant/apiclient_key.pem';
-$merchantPrivateKeyInstance = PemUtil::loadPrivateKey($merchantPrivateKeyFilePath);
+$merchantPrivateKeyFilePath = 'file:///path/to/merchant/apiclient_key.pem';// 注意 `file://` 开头协议不能少
+$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
Загрузка сертификата платформы из файла:
-$platformCertificateFilePath = '/path/to/wechatpay/cert.pem';
-$platformCertificateInstance = PemUtil::loadCertificate($platformCertificateFilePath);
-// 解析平台证书序列号
-$platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateInstance);
+$platformCertificateFilePath = 'file:///path/to/wechatpay/cert.pem';// 注意 `file://` 开头协议不能少
+$platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
+// 解析「平台证书」序列号,「平台证书」当前五年一换,缓存后就是个常量
+$platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);
Соответственно, инициализация фабрики для сертификатов платформы изменяется следующим образом:
'certs' => [
- $platformCertificateSerial => $platformCertificateInstance,
+ $platformCertificateSerial => $platformPublicKeyInstance,
],
APIv3, связанный с подписью RSA-данных:
-use WeChatPay\Util\PemUtil;
-$merchantPrivateKeyFilePath = '/path/to/merchant/apiclient_key.pem';
-$merchantPrivateKeyInstance = PemUtil::loadPrivateKey($merchantPrivateKeyFilePath);
+$merchantPrivateKeyFilePath = 'file:///path/to/merchant/apiclient_key.pem';
+$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath);
Проверка подписи APIv3:
-use WeChatPay\Util\PemUtil;
// 根据通知的平台证书序列 номера, 查询本地平台证书文件,
// 假定为 `/path/to/wechatpay/inWechatpaySerial.pem`
-$certInstance = PemUtil::loadCertificate('/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,
- $certInstance
+ $platformPublicKeyInstance
);
Более продвинутая загрузка RSA публичных/приватных ключей способом, например, из Rsa::fromPkcs1, Rsa::fromPkcs8, Rsa::fromSpki и других синтаксических сахаров для загрузки, можно обратиться к тестовому примеру RsaTest.php. При необходимости используйте его по своему усмотрению.
В версии 1.1 были внесены небольшие изменения в реализацию внутренних промежуточных программ. Изменения коснулись APIv3 исключений:
Промежуточное ПО verifier было переработано и включает следующие изменения:
Эти изменения не влияют на нормальную логику работы (HTTP 20X) и требуют адаптации обработки исключений на стороне приложения:
Для синхронной модели рекомендуется заменить обработку UnexpectedValueException на обработку GuzzleHttp\Exception\RequestException следующим образом:
try {
$instance
->v3->pay->transactions->native
->post(['json' => []]);
- } catch (\UnexpectedValueException $e) {
+ } catch (\GuzzleHttp\Exception\RequestException $e) {
// do something
}
Для асинхронной модели рекомендуется всегда проверять, является ли текущее исключение экземпляром GuzzleHttp\Exception\RequestException, как показано в примере кода в файле README.md.
Как указано в истории изменений (CHANGELOG.md), эта библиотека несовместима с версией wechatpay/wechatpay-guzzle-middleware:~0.2 начиная с версии 1.0. Причины несовместимости включают:
Минимальная требуемая версия PHP — 7.2.5. Рекомендуется, чтобы разработчики приложений оценили совместимость среды выполнения перед началом процесса миграции.
Зависимости обновлены:
"require": {
- "guzzlehttp/guzzle": "^6.3",
- "wechatpay/wechatpay-guzzle-middleware": "^0.2.0"
+ "wechatpay/wechatpay": "^1.0"
}
use GuzzleHttp\Exception\RequestException;
- use WechatPay\GuzzleMiddleware\WechatPayMiddleware;
+ use WeChatPay\Builder;
- use WechatPay\GuzzleMiddleware\Util\PemUtil;
+ use WeChatPay\Util\PemUtil;
$merchantId = '1000100';
$merchantSerialNumber = 'XXXXXXXXXX';
$merchantPrivateKey = PemUtil::loadPrivateKey('/path/to/mch/private/key.pem');
$wechatpayCertificate = PemUtil::loadCertificate('/path/to/wechatpay/cert.pem');
+$wechatpayCertificateSerialNumber = PemUtil::parseCertificateSerialNo($wechatpayCertificate);
- $wechatpayMiddleware = WechatPayMiddleware::builder()
- ->withMerchant($merchantId, $merchantSerialNumber, $merchantPrivateKey)
- ->withWechatPay([ $wechatpayCertificate ])
- ->build();
- $stack = GuzzleHttp\HandlerStack::create();
- $stack->push($wechatpayMiddleware, 'wechatpay');
- $client = new GuzzleHttp\Client(['handler' => $stack]);
+ $instance = Builder::factory([
+ 'mchid' => $merchantId,
+ 'serial' => $merchantSerialNumber,
+ 'privateKey' => $merchantPrivateKey,
+ 'certs' => [$wechatpayCertificateSerialNumber => $wechatpayCertificate],
+ ]);
Можно использовать синтаксический сахар, предоставленный этой библиотекой, для упрощения структуры кода запроса:
try {
$resp = $client->request('GET', 'https://api.mch.weixin.qq.com/v3/...', [
'headers' => ['Accept' => 'application/json']
]);
} catch (RequestException $e) {
``` **ПОСТ запрос**
Сокращённый код запроса:
```diff
try {
- $resp = $client->request('POST', 'https://api.mch.weixin.qq.com/v3/...', [
+ $resp = $instance->chain('v3/...')->post([
'json' => [ // JSON запрос тела
'field1' => 'value1',
'field2' => 'value2'
],
- 'headers' => [ 'Accept' => 'application/json' ]
]);
} catch (RequestException $e) {
//do something
}
Загрузка медиафайлов
- use WechatPay\GuzzleMiddleware\Util\MediaUtil;
+ use WeChatPay\Util\MediaUtil;
$media = new MediaUtil('/your/file/path/with.extension');
try {
- $resp = $client->request('POST', 'https://api.mch.weixin.qq.com/v3/[merchant/media/video_upload|marketing/favor/media/image-upload]', [
+ $resp = $instance->chain('v3/marketing/favor/media/image-upload')->post([
'body' => $media->getStream(),
'headers' => [
- 'Accept' => 'application/json',
'content-type' => $media->getContentType(),
]
]);
} catch (Exception $e) {
// do something
}
try {
- $resp = $client->post('merchant/media/upload', [
+ $resp = $instance->chain('v3/merchant/media/upload')->post([
'body' => $media->getStream(),
'headers' => [
- 'Accept' => 'application/json',
'content-type' => $media->getContentType(),
]
]);
} catch (Exception $e) {
// do something
}
Шифрование и дешифрование конфиденциальной информации
- use WechatPay\GuzzleMiddleware\Util\SensitiveInfoCrypto;
+ use WeChatPay\Crypto\Rsa;
- $encryptor = new SensitiveInfoCrypto(PemUtil::loadCertificate('/path/to/wechatpay/cert.pem'));
+ $encryptor = function($msg) use ($wechatpayCertificate) { return Rsa::encrypt($msg, $wechatpayCertificate); };
try {
- $resp = $client->post('/v3/applyment4sub/applyment/', [
+ $resp = $instance->chain('v3/applyment4sub/applyment/')->post([
'json' => [
'business_code' => 'APL_98761234',
'contact_info' => [
'contact_name' => $encryptor('value of `contact_name`'),
'contact_id_number' => $encryptor('value of `contact_id_number'),
'mobile_phone' => $encryptor('value of `mobile_phone`'),
'contact_email' => $encryptor('value of `contact_email`'),
],
//...
],
'headers' => [
- 'Wechatpay-Serial' => 'must be the serial number via the downloaded pem file of `/v3/certificates`',
+ 'Wechatpay-Serial' => $wechatpayCertificateSerialNumber,
- 'Accept' => 'application/json',
],
]);
} catch (Exception $e) {
// do something
}
Инструмент для загрузки сертификата платформы
В первый раз, когда вы загружаете сертификат платформы, эта библиотека полностью использует возможности менеджера промежуточного программного обеспечения \GuzzleHttp\HandlerStack
для управления стеком. В соответствии с порядком выполнения стека, перед регистрацией промежуточного программного обеспечения для проверки результата verifier
, регистрируется промежуточное программное обеспечение certsInjector
. После этого регистрируется certsRecorder
для «разрыва» «бесконечного цикла».
Библиотека предоставляет инструмент для загрузки сертификатов, который не изменяет логику проверки результатов, и полную реализацию можно найти в файле bin/CertificateDownloader.php
.
Расшифровка сертификата платформы AesGcm
- use WechatPay\GuzzleMiddleware\Util\AesUtil;
+ use WeChatPay\Crypto\AesGcm;
- $decrypter = new AesUtil($opts['key']);
- $plain = $decrypter->decryptToString($encCert['associated_data'], $encCert['nonce'], $encCert['ciphertext']);
+ $plain = AesGcm::decrypt($encCert['ciphertext'], $opts['key'], $encCert['nonce'], $encCert['associated_data']);
Этот SDK версии php_sdk_v3.0.10 доступен после загрузки документации APIv2. Здесь представлен план миграции.
Измените параметры ручного режима файла на режим инициализации экземпляра:
- // ③、修改lib/WxPay.Config.php为自己申请的商户号的信息(配置详见说明)
+ use WeChatPay/Builder;
+ $instance = new Builder([
+ 'mchid' => $mchid,
+ 'serial' => 'nop',
+ 'privateKey' => 'any',
+ 'secret' => $apiv2Key,
+ 'certs' => ['any' => null],
+ 'merchant' => ['key' => '/path/to/cert/apiclient_key.pem', 'cert' => '/path/to/cert/apiclient_cert.pem'],
+ ]);
``` **Перевод текста на русский язык:**
### Платёж через код оплаты
```diff
- require_once "../lib/WxPay.Api.php";
- require_once "WxPay.MicroPay.php";
-
- $auth_code = $_REQUEST["auth_code"];
- $input = new WxPayMicroPay();
- $input->SetAuth_code($auth_code);
- $input->SetBody("Тест оплаты с помощью карты");
- $input->SetTotal_fee("1");
- $input->SetOut_trade_no("sdkphp".date("YmdHis"));
-
- $microPay = new MicroPay();
- printf_info($microPay->pay($input));
+ Используйте WeChatPay\Formatter;
+ используйте WeChatPay\Transformer;
+ // Непосредственно создайте параметры запроса массива
+ $input = [
+ 'appid' => $appid, // Получить текущие параметры запроса из config
+ 'mch_id' => $mchid, // Получить текущие параметры запроса из config
+ 'auth_code' => $auth_code,
+ 'body' => 'Тест оплаты с помощью карты',
+ 'total_fee' => '1',
+ 'out_trade_no' => 'sdkphp' . date('YmdHis'),
+ 'spbill_create_ip' => $mechineIp,
+ ];
+ // Отправьте запрос и получите результат, подавляя предупреждение E_USER_DEPRECATED
+ $resp = @$instance->chain('v2/pay/micropay')->post(['xml' => $input]);
+ $order = Transformer::toArray((string)$resp->getBody());
+ // print_r($order);
+ $input = [
+ 'appid' => $appid, // 从config拿到当前请求参数上
+ 'mch_id' => $mchid, // 从config拿到当前请求参数上
+ 'out_trade_no' => $outTradeNo,
+ ];
+ // 发起请求并取得结果,抑制`E_USER_DEPRECATED`提示
+ $resp = @$instance->chain('v2/secapi/pay/reverse')->postAsync(['xml' => $input, 'security' => true])->wait();
+ $result = Transformer::toArray((string)$resp->getBody());
+ // print_r($result);
Другие APIv2 миграции и запросы интерфейсов похожи на вышеприведённые примеры. В них представлены только примеры нормального возврата, необходимо добавить структуры try catch / otherwise для обработки исключений.
Таким образом, после переноса можно с удовольствием использовать официальные интерфейсы платежей WeChat с использованием Chainable, PromiseA+ и мощного PHP8 во время выполнения.
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.