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

OSCHINA-MIRROR/TheNorthMemory-wechatpay-php

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
UPGRADING.md 26 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
gitlife-traslator Отправлено 01.12.2024 10:22 a96da73

Руководство по обновлению

Обновление с версии 1.3 до версии 1.4

Версия 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.2 до версии 1.3

Основное изменение в версии 1.3 — добавление подсказок для интерфейсов и параметров в IDE. Рекомендуется устанавливать этот пакет отдельно с помощью команды composer --dev (Add requirement to require-dev.). Производственная среда не требует этого обновления.

Обновление с версии 1.1 до версии 1.2

Обновление до версии 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.0 до 1.1

В версии 1.1 были внесены небольшие изменения в реализацию внутренних промежуточных программ. Изменения коснулись APIv3 исключений:

  1. Последовательность промежуточного программного обеспечения стека была немного изменена. Промежуточное программное обеспечение для подписи запросов (signer) было перемещено из верхней части стека в нужное место перед отправкой тела запроса. Это изменение не влияет на работу приложения.
  2. Промежуточное ПО для проверки ответов (verifier) было перемещено из верхней части стека перед ошибками HTTP (GuzzleHttp\Middleware::httpErrors). Ошибки HTTP (4XX, 5XX) обрабатываются встроенным промежуточным программным обеспечением GuzzleHttp\Middleware::httpErrors. Проверка ответа выполняется только для нормальных результатов (HTTP 20X).

Промежуточное ПО verifier было переработано и включает следующие изменения:

  • Исключения были изменены с UnexpectedValueException на GuzzleHttp\Exception\RequestException. Поскольку запрос/ответ уже завершены, и ответ содержит результат (HTTP 20X), SDK клиент может получить объект ответа при возникновении исключения. Это позволяет определить конкретное содержимое ответа.
  • В процессе проверки ответа возможно возникновение исключения UnexpectedValueException из WeChatPay\Crypto\Rsa::verify. После изменения это исключение также обрабатывается RequestException, что позволяет приложению получить экземпляр этого исключения через RequestException::getPrevious().

Эти изменения не влияют на нормальную логику работы (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.

Миграция с wechatpay-guzzle-middleware 0.2 на 1.0

Как указано в истории изменений (CHANGELOG.md), эта библиотека несовместима с версией wechatpay/wechatpay-guzzle-middleware:~0.2 начиная с версии 1.0. Причины несовместимости включают:

  1. Обновление Guzzle до версии 7. Guzzle 7 содержит множество несовместимых обновлений, которые обсуждались в выпуске Laravel8 зависимостей (https://github.com/wechatpay-apiv3/wechatpay-guzzle-middleware/issues/54). Guzzle 7 требует PHP версии не ниже 7.2.5 и предоставляет функции, такие как подпись параметров функций и подпись возвращаемых значений, что значительно повышает надёжность библиотеки с точки зрения языка разработки.
  2. Рефакторинг и исправление проблем с дизайном, связанных с чувствительной информацией и шифрованием (https://github.com/wechatpay-apiv3/wechatpay-guzzle-middleware/issues/25).
  3. Перепроектирование функций библиотеки и методов для поддержки подписей уведомлений о обратных вызовах (https://github.com/wechatpay-apiv3/wechatpay-guzzle-middleware/issues/42).
  4. Изменение composer.json для перемещения guzzlehttp/guzzle из require-dev в require, что устраняет необходимость ручной установки.
  5. Сокращение ручного объединения параметров клиента до Builder::factory, обеспечивая унифицированное создание клиента через SDK.
  6. Добавление цепочки вызовов оболочки, предоставляя встроенную поддержку для цепочечных вызовов APIv3.
  7. Добавление поддержки APIv2, рекомендуя продавцам сначала обновить поддержку APIv2 в этой библиотеке, а затем перейти на соответствующую поддержку APIv3.
  8. Расширение покрытия модульных тестов для Linux, macOS и Windows во время выполнения.
  9. Изменение пространства имён на WeChatPay.

Руководство по миграции

Минимальная требуемая версия PHP — 7.2.5. Рекомендуется, чтобы разработчики приложений оценили совместимость среды выполнения перед началом процесса миграции.

Изменения в composer.json

Зависимости обновлены:

     "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],
+ ]);

Изменение методов вызова

GET запросы

Можно использовать синтаксический сахар, предоставленный этой библиотекой, для упрощения структуры кода запроса:

 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']);

Переход с php_sdk_v3.0.10 на 1.0

Этот 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 во время выполнения.

1
https://api.gitlife.ru/oschina-mirror/TheNorthMemory-wechatpay-php.git
git@api.gitlife.ru:oschina-mirror/TheNorthMemory-wechatpay-php.git
oschina-mirror
TheNorthMemory-wechatpay-php
TheNorthMemory-wechatpay-php
main