wechatpay-apiv3/wechatpay-php

xxxxxxxxxxx is not valid header value

Closed this issue · 2 comments

运行环境

- OS:WSL Ubuntu 22.04
- PHP:7.4.33
- wechatpay-php:1.4.9

描述你的问题现象

调用发送代金券接口

class WechatCouponController extends BaseController
{
    public function sendToUser()
    {
        $merchantId = '1665783367';

        $merchantConfig = config('wechat.payment.' . $merchantId);

        $merchantPrivateKeyFilePath = 'file://' . $merchantConfig['key_path'];
        $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);

        $platformCertificateFilePath = 'file://' . $merchantConfig['cert_path'];

        $getSerialCommand = sprintf("openssl x509 -in %s -noout -serial", $merchantConfig['cert_path']);
        $serialString = shell_exec($getSerialCommand);
        $merchantCertificateSerial = explode('=', $serialString)[1];

        $platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);

        $platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);

        $instance = Builder::factory([
            'mchid'      => $merchantId,
            'serial'     => $merchantCertificateSerial,
            'privateKey' => $merchantPrivateKeyInstance,
            'certs'      => [
                $platformCertificateSerial => $platformPublicKeyInstance,
            ],
        ]);

        $response = $instance->chain('v3/marketing/favor/users/oMry16zOr9x023Gv67kMqD9OynNc/coupons')
            ->post([
                'json' => [
                    'stock_id' => '18646824',
                    'out_request_no' => date('YmdHis'). rand(100000, 999999),
                    'appid' => 'wx86e50409cf989a76',
                    'stock_creator_mchid' => $merchantId,
                ]
            ]);

        dump($response->getStatusCode(), $response->getBody()->getContents());
    }
}

报错堆栈

{
	"code": 0,
	"msg": "\"WECHATPAY2-SHA256-RSA2048 mchid=\"1665783367\",serial_no=\"2E5DA8C17D5BE9DF699C1C2A6F7CA1A26685AED8\n\",timestamp=\"1710386514\",nonce_str=\"I5BhceK5i1lILDpdbua2lwLOx0Cn5e7D\",signature=\"VL5pHi/2KMBIjCBNFNd51VMNNoseew08Jr7NINr5SFfAVV4JA8V4heEBOMPrZD8mHVXgKTApbhOJ5CmJQACCt2dRxlAv7NUSL4l2wZH3BIIaT2el2kJ006vAkZPmQh/TJsPs7uCqojY7UdtrUX3NDrifl3ySA1fcR83YvfbrtJ8bB23hqrRoG2XI54QObgmYOZbUDRisDw51KotFETtnjJ1IMJZR7sDhItHGztz4UrLeAL7CX0GLjEPTBNxNzW9S116iWiAIPlgn/Fy5EXgyRgvsksCetOlqAgVmv9+UI9gNwxl0q147eP4ZSc9KscuQmMIaoRaUZK2NklofUcjFxA==\"\" is not valid header value.",
	"data": {
		"line": 260,
		"file": "/www/user_center/vendor/guzzlehttp/psr7/src/MessageTrait.php",
		"trace": [
			{
				"file": "/www/user_center/vendor/guzzlehttp/psr7/src/MessageTrait.php",
				"line": 209,
				"function": "assertValue",
				"class": "GuzzleHttp\\Psr7\\Request",
				"type": "->"
			},
			{
				"function": "GuzzleHttp\\Psr7\\{closure}",
				"class": "GuzzleHttp\\Psr7\\Request",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/guzzlehttp/psr7/src/MessageTrait.php",
				"line": 212,
				"function": "array_map"
			},
			{
				"file": "/www/user_center/vendor/guzzlehttp/psr7/src/MessageTrait.php",
				"line": 174,
				"function": "trimAndValidateHeaderValues",
				"class": "GuzzleHttp\\Psr7\\Request",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/guzzlehttp/psr7/src/MessageTrait.php",
				"line": 75,
				"function": "normalizeHeaderValue",
				"class": "GuzzleHttp\\Psr7\\Request",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/wechatpay/wechatpay/src/ClientJsonTrait.php",
				"line": 76,
				"function": "withHeader",
				"class": "GuzzleHttp\\Psr7\\Request",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/guzzlehttp/guzzle/src/Middleware.php",
				"line": 248,
				"function": "WeChatPay\\{closure}",
				"class": "WeChatPay\\ClientDecorator",
				"type": "::"
			},
			{
				"file": "/www/user_center/vendor/guzzlehttp/guzzle/src/Middleware.php",
				"line": 31,
				"function": "GuzzleHttp\\{closure}",
				"class": "GuzzleHttp\\Middleware",
				"type": "::"
			},
			{
				"file": "/www/user_center/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php",
				"line": 71,
				"function": "GuzzleHttp\\{closure}",
				"class": "GuzzleHttp\\Middleware",
				"type": "::"
			},
			{
				"file": "/www/user_center/vendor/guzzlehttp/guzzle/src/Middleware.php",
				"line": 66,
				"function": "__invoke",
				"class": "GuzzleHttp\\RedirectMiddleware",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/wechatpay/wechatpay/src/ClientJsonTrait.php",
				"line": 187,
				"function": "GuzzleHttp\\{closure}",
				"class": "GuzzleHttp\\Middleware",
				"type": "::"
			},
			{
				"file": "/www/user_center/vendor/guzzlehttp/guzzle/src/HandlerStack.php",
				"line": 75,
				"function": "WeChatPay\\{closure}",
				"class": "WeChatPay\\ClientDecorator",
				"type": "::"
			},
			{
				"file": "/www/user_center/vendor/guzzlehttp/guzzle/src/Client.php",
				"line": 333,
				"function": "__invoke",
				"class": "GuzzleHttp\\HandlerStack",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/guzzlehttp/guzzle/src/Client.php",
				"line": 169,
				"function": "transfer",
				"class": "GuzzleHttp\\Client",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/guzzlehttp/guzzle/src/Client.php",
				"line": 189,
				"function": "requestAsync",
				"class": "GuzzleHttp\\Client",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/wechatpay/wechatpay/src/ClientDecorator.php",
				"line": 138,
				"function": "request",
				"class": "GuzzleHttp\\Client",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/wechatpay/wechatpay/src/BuilderTrait.php",
				"line": 45,
				"function": "request",
				"class": "WeChatPay\\ClientDecorator",
				"type": "->"
			},
			{
				"file": "/www/user_center/app/Http/Controllers/Api/WechatCouponController.php",
				"line": 46,
				"function": "post",
				"class": "class@anonymous\u0000/www/user_center/vendor/wechatpay/wechatpay/src/Builder.php:43$4cd",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Routing/Controller.php",
				"line": 54,
				"function": "sendToUser",
				"class": "App\\Http\\Controllers\\Api\\WechatCouponController",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php",
				"line": 45,
				"function": "callAction",
				"class": "Illuminate\\Routing\\Controller",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Routing/Route.php",
				"line": 262,
				"function": "dispatch",
				"class": "Illuminate\\Routing\\ControllerDispatcher",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Routing/Route.php",
				"line": 205,
				"function": "runController",
				"class": "Illuminate\\Routing\\Route",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
				"line": 721,
				"function": "run",
				"class": "Illuminate\\Routing\\Route",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
				"line": 128,
				"function": "Illuminate\\Routing\\{closure}",
				"class": "Illuminate\\Routing\\Router",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php",
				"line": 50,
				"function": "Illuminate\\Pipeline\\{closure}",
				"class": "Illuminate\\Pipeline\\Pipeline",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
				"line": 167,
				"function": "handle",
				"class": "Illuminate\\Routing\\Middleware\\SubstituteBindings",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
				"line": 103,
				"function": "Illuminate\\Pipeline\\{closure}",
				"class": "Illuminate\\Pipeline\\Pipeline",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
				"line": 723,
				"function": "then",
				"class": "Illuminate\\Pipeline\\Pipeline",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
				"line": 698,
				"function": "runRouteWithinStack",
				"class": "Illuminate\\Routing\\Router",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
				"line": 662,
				"function": "runRoute",
				"class": "Illuminate\\Routing\\Router",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
				"line": 651,
				"function": "dispatchToRoute",
				"class": "Illuminate\\Routing\\Router",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
				"line": 167,
				"function": "dispatch",
				"class": "Illuminate\\Routing\\Router",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
				"line": 128,
				"function": "Illuminate\\Foundation\\Http\\{closure}",
				"class": "Illuminate\\Foundation\\Http\\Kernel",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",
				"line": 21,
				"function": "Illuminate\\Pipeline\\{closure}",
				"class": "Illuminate\\Pipeline\\Pipeline",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php",
				"line": 31,
				"function": "handle",
				"class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
				"line": 167,
				"function": "handle",
				"class": "Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",
				"line": 21,
				"function": "Illuminate\\Pipeline\\{closure}",
				"class": "Illuminate\\Pipeline\\Pipeline",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php",
				"line": 40,
				"function": "handle",
				"class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
				"line": 167,
				"function": "handle",
				"class": "Illuminate\\Foundation\\Http\\Middleware\\TrimStrings",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php",
				"line": 27,
				"function": "Illuminate\\Pipeline\\{closure}",
				"class": "Illuminate\\Pipeline\\Pipeline",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
				"line": 167,
				"function": "handle",
				"class": "Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php",
				"line": 86,
				"function": "Illuminate\\Pipeline\\{closure}",
				"class": "Illuminate\\Pipeline\\Pipeline",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
				"line": 167,
				"function": "handle",
				"class": "Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/fruitcake/laravel-cors/src/HandleCors.php",
				"line": 52,
				"function": "Illuminate\\Pipeline\\{closure}",
				"class": "Illuminate\\Pipeline\\Pipeline",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
				"line": 167,
				"function": "handle",
				"class": "Fruitcake\\Cors\\HandleCors",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php",
				"line": 39,
				"function": "Illuminate\\Pipeline\\{closure}",
				"class": "Illuminate\\Pipeline\\Pipeline",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
				"line": 167,
				"function": "handle",
				"class": "Illuminate\\Http\\Middleware\\TrustProxies",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
				"line": 103,
				"function": "Illuminate\\Pipeline\\{closure}",
				"class": "Illuminate\\Pipeline\\Pipeline",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
				"line": 142,
				"function": "then",
				"class": "Illuminate\\Pipeline\\Pipeline",
				"type": "->"
			},
			{
				"file": "/www/user_center/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
				"line": 111,
				"function": "sendRequestThroughRouter",
				"class": "Illuminate\\Foundation\\Http\\Kernel",
				"type": "->"
			},
			{
				"file": "/www/user_center/public/index.php",
				"line": 52,
				"function": "handle",
				"class": "Illuminate\\Foundation\\Http\\Kernel",
				"type": "->"
			},
			{
				"file": "/www/user_center/server.php",
				"line": 21,
				"args": [
					"/www/user_center/public/index.php"
				],
				"function": "require_once"
			}
		]
	}
}

你的商户序列号serial_no=\"2E5DA8C17D5BE9DF699C1C2A6F7CA1A26685AED8\n\"末尾带 0x0A 字符了,当然不合法了。

另外,获取证书序列号,你可以用SDK自带的函数来简化,例如:

$getSerialCommand = \WeChatPay\Util\PemUtil::parseCertificateSerialNo($platformCertificateFilePath);

在测试用例里,你可以找到更多种获取序列号的方法:

public function testParseCertificateSerialNo(): void
{
[$serialNo, $certFile, $certString, , , $certFileProtocolString] = self::$environment ?? ['', '', '', '', '', '', ''];
$serialNoFromPemUtilFile = PemUtil::parseCertificateSerialNo(PemUtil::loadCertificate($certFile));
$serialNoFromPemUtilString = PemUtil::parseCertificateSerialNo(PemUtil::loadCertificateFromString($certString));
$serialNoFromCertString = PemUtil::parseCertificateSerialNo($certString);
$serialNoFromCertFileProtocolString = PemUtil::parseCertificateSerialNo($certFileProtocolString);
self::assertEquals($serialNo, $serialNoFromPemUtilFile);
self::assertEquals($serialNo, $serialNoFromPemUtilString);
self::assertEquals($serialNo, $serialNoFromCertString);
self::assertEquals($serialNo, $serialNoFromCertFileProtocolString);

还有,->chain()方法请使用{placeholder}写法,例如:

$instance->chain('v3/marketing/favor/users/{open_id}/coupons')->post([
  'open_id' => 'oMry16zOr9x023Gv67kMqD9OynNc',
  'json' => []
])

好的,非常感谢!