Browse Source

修改

tags/v1.0.5
wuliangbo 11 months ago
parent
commit
a227a06c22
19 changed files with 1198 additions and 6 deletions
  1. +1
    -0
      .gitignore
  2. +6
    -6
      src/OnlineRetailer/Notify/Handler.php
  3. +125
    -0
      src/Pay/Application.php
  4. +119
    -0
      src/Pay/Combine/Client.php
  5. +32
    -0
      src/Pay/Combine/ServiceProvider.php
  6. +120
    -0
      src/Pay/Direct/Client.php
  7. +32
    -0
      src/Pay/Direct/ServiceProvider.php
  8. +118
    -0
      src/Pay/Jssdk/Client.php
  9. +33
    -0
      src/Pay/Jssdk/ServiceProvider.php
  10. +34
    -0
      src/Pay/Kernel/BaseClient.php
  11. +23
    -0
      src/Pay/Kernel/Exceptions/EncryptException.php
  12. +23
    -0
      src/Pay/Kernel/Exceptions/InvalidExtensionException.php
  13. +23
    -0
      src/Pay/Kernel/Exceptions/InvalidSignException.php
  14. +212
    -0
      src/Pay/Notify/Handler.php
  15. +33
    -0
      src/Pay/Notify/Paid.php
  16. +48
    -0
      src/Pay/Notify/Refunded.php
  17. +60
    -0
      src/Pay/Notify/Scanned.php
  18. +124
    -0
      src/Pay/Partner/Client.php
  19. +32
    -0
      src/Pay/Partner/ServiceProvider.php

+ 1
- 0
.gitignore View File

@@ -7,3 +7,4 @@ sftp-config.json
/.split
/composer.lock
.php_cs.cache
.idea

+ 6
- 6
src/OnlineRetailer/Notify/Handler.php View File

@@ -88,7 +88,7 @@ abstract class Handler

/**
* @param array $attributes
* @param bool $sign
* @param bool $sign
*
* @return $this
*/
@@ -137,20 +137,20 @@ abstract class Handler
}

if ($this->check) {
$this->validate($this->app['request']);
// $this->validate($this->app['request']);
}

try {
parse_str($this->app['request']->getContent(), $message);
$message = @json_decode($this->app['request']->getContent(), 1);
if ($message['resource']) {
$data = (new AesUtil($this->app->config->get('key_v3')))->decryptToString($message['resource']['associated_data'], $message['resource']['nonce'], $message['resource']['ciphertext']);
$data = (new AesUtil($this->app->config->get('key_v3') ?? $this->app->config->get('key')))->decryptToString($message['resource']['associated_data'], $message['resource']['nonce'], $message['resource']['ciphertext']);
$message = @json_decode($data, true);
if(!is_array($message)){
if (!is_array($message)) {
throw new Exception('Invalid request', 400);
}
}
} catch (\Throwable $e) {
throw new Exception('Invalid request XML: '.$e->getMessage(), 400);
throw new Exception('Invalid request XML: ' . $e->getMessage(), 400);
}

if (!is_array($message) || empty($message)) {


+ 125
- 0
src/Pay/Application.php View File

@@ -0,0 +1,125 @@
<?php
/**
* Created by PhpStorm.
* User: wuliangbo
* Date: 2020/7/14
* Time: 13:54
*/

namespace EasyWeChat\Pay;

use Closure;
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
use EasyWeChat\Kernel\ServiceContainer;

/**
* Class Application
* @package EasyWeChat\OnlineRetailer
* @property \EasyWeChat\Pay\Combine\Client $combine
* @property \EasyWeChat\Pay\Direct\Client $direct
* @property \EasyWeChat\Pay\Jssdk\Client $jssdk
* @property \EasyWeChat\Pay\Partner\Client $partner
*/
class Application extends ServiceContainer
{
protected $providers = [
Combine\ServiceProvider::class,
Direct\ServiceProvider::class,
Partner\ServiceProvider::class,
Jssdk\ServiceProvider::class
];

/**
* @var array
*/
protected $defaultConfig = [
'http' => [
'base_uri' => 'https://api.mch.weixin.qq.com/',
],
];

/**
* Set sub-merchant.
*
* @param string $mchId
* @param string|null $appId
*
* @return $this
*/
public function setSubMerchant(string $mchId, string $appId = null)
{
$this['config']->set('sub_mch_id', $mchId);
$this['config']->set('sub_appid', $appId);

return $this;
}

/**
* @param \Closure $closure
*
* @return \Symfony\Component\HttpFoundation\Response
*
* @codeCoverageIgnore
*
* @throws \EasyWeChat\Kernel\Exceptions\Exception
*/
public function handlePaidNotify(Closure $closure)
{
return (new Notify\Paid($this))->handle($closure);
}

/**
* @param \Closure $closure
*
* @return \Symfony\Component\HttpFoundation\Response
*
* @codeCoverageIgnore
*
* @throws \EasyWeChat\Kernel\Exceptions\Exception
*/
public function handleRefundedNotify(Closure $closure)
{
return (new Notify\Refunded($this))->handle($closure);
}

/**
* @param \Closure $closure
*
* @return \Symfony\Component\HttpFoundation\Response
*
* @codeCoverageIgnore
*
* @throws \EasyWeChat\Kernel\Exceptions\Exception
*/
public function handleScannedNotify(Closure $closure)
{
return (new Notify\Scanned($this))->handle($closure);
}

/**
* @param string|null $endpoint
*
* @return string
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
*/
public function getKey(string $endpoint = null)
{
if ('sandboxnew/pay/getsignkey' === $endpoint) {
return $this['config']->key;
}

$key = $this['config']->key;

if (empty($key)) {
throw new InvalidArgumentException('config key should not empty.');
}

if (32 !== strlen($key)) {
throw new InvalidArgumentException(sprintf("'%s' should be 32 chars length.", $key));
}

return $key;
}

}

+ 119
- 0
src/Pay/Combine/Client.php View File

@@ -0,0 +1,119 @@
<?php

/*
* This file is part of the overtrue/wechat.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace EasyWeChat\Pay\Combine;

use EasyWeChat\Pay\Kernel\BaseClient;

/**
* Class Client
* @package EasyWeChat\OnlineRetailer\Combine
*/
class Client extends BaseClient
{
/**
* app.
*
* @param $params
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function app($params)
{
$params['combine_appid'] = $this->app->config->get('app_id');
$params['combine_mchid'] = $this->app->config->get('mch_id');
$params['notify_url'] = $params['notify_url'] ?? $this->app['config']['notify_url'];
return $this->httpPostJson('v3/combine-transactions/app', $params);
}

/**
* 公众号|小程序.
*
* @param $params
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function jsapi($params)
{
$params['combine_appid'] = $this->app->config->get('app_id');
$params['combine_mchid'] = $this->app->config->get('mch_id');
$params['notify_url'] = $params['notify_url'] ?? $this->app['config']['notify_url'];
return $this->httpPostJson('v3/combine-transactions/jsapi', $params);
}

/**
* h5.
*
* @param $params
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function wap($params)
{
$params['combine_appid'] = $this->app->config->get('app_id');
$params['combine_mchid'] = $this->app->config->get('mch_id');
$params['notify_url'] = $params['notify_url'] ?? $this->app['config']['notify_url'];
return $this->httpPostJson('v3/combine-transactions/h5', $params);
}

/**
* 扫码支付.
*
* @param $params
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function scan($params)
{
$params['combine_appid'] = $this->app->config->get('app_id');
$params['combine_mchid'] = $this->app->config->get('mch_id');
$params['notify_url'] = $params['notify_url'] ?? $this->app['config']['notify_url'];
return $this->httpPostJson('v3/combine-transactions/native', $params);
}

/**
* 查询订单.
*
* @param $out_trade_no
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function query(string $out_trade_no)
{
return $this->httpGet('v3/combine-transactions/out-trade-no/' . $out_trade_no);
}

/**
* 关闭订单.
*
* @param string $out_trade_no
* @param array $params
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function close(string $out_trade_no, array $params)
{
$params['combine_appid'] = $this->app->config->get('app_id');
return $this->httpPostJson('v3/combine-transactions/out-trade-no/' . $out_trade_no . '/close', $params);
}
}

+ 32
- 0
src/Pay/Combine/ServiceProvider.php View File

@@ -0,0 +1,32 @@
<?php

/*
* This file is part of the overtrue/wechat.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace EasyWeChat\Pay\Combine;

use Pimple\Container;
use Pimple\ServiceProviderInterface;

/**
* Class ServiceProvider
* @package EasyWeChat\OnlineRetailer\Combine
*/
class ServiceProvider implements ServiceProviderInterface
{
/**
* {@inheritdoc}.
*/
public function register(Container $app)
{
$app['combine'] = function ($app) {
return new Client($app);
};
}
}

+ 120
- 0
src/Pay/Direct/Client.php View File

@@ -0,0 +1,120 @@
<?php

/*
* This file is part of the overtrue/wechat.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace EasyWeChat\Pay\Direct;

use EasyWeChat\Pay\Kernel\BaseClient;

/**
* Class Client
* @package EasyWeChat\OnlineRetailer\Combine
*/
class Client extends BaseClient
{
/**
* app.
*
* @param $params
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function app($params)
{
$params['appid'] = $this->app->config->get('app_id');
$params['mchid'] = $this->app->config->get('mch_id');
$params['notify_url'] = $params['notify_url'] ?? $this->app['config']['notify_url'];
return $this->httpPostJson('v3/pay/transactions/app', $params);
}

/**
* 公众号|小程序.
*
* @param $params
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function jsapi($params)
{
$params['appid'] = $this->app->config->get('app_id');
$params['mchid'] = $this->app->config->get('mch_id');
$params['notify_url'] = $params['notify_url'] ?? $this->app['config']['notify_url'];
return $this->httpPostJson('v3/pay/transactions/jsapi', $params);
}

/**
* h5.
*
* @param $params
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function wap($params)
{
$params['appid'] = $this->app->config->get('app_id');
$params['mchid'] = $this->app->config->get('mch_id');
$params['notify_url'] = $params['notify_url'] ?? $this->app['config']['notify_url'];
return $this->httpPostJson('v3/pay/transactions/h5', $params);
}

/**
* 扫码支付.
*
* @param $params
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function scan($params)
{
$params['appid'] = $this->app->config->get('app_id');
$params['mchid'] = $this->app->config->get('mch_id');
$params['notify_url'] = $params['notify_url'] ?? $this->app['config']['notify_url'];
return $this->httpPostJson('v3/pay/transactions/native', $params);
}

/**
* 查询订单.
*
* @param $out_trade_no
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function query(string $out_trade_no)
{
$params['mchid'] = $this->app->config->get('mch_id');
return $this->httpGet('v3/pay/transactions/out-trade-no/' . $out_trade_no);
}

/**
* 关闭订单.
*
* @param string $out_trade_no
* @param array $params
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function close(string $out_trade_no, array $params)
{
$params['mchid'] = $this->app->config->get('mch_id');
return $this->httpPostJson('v3/pay/transactions/out-trade-no/' . $out_trade_no . '/close', $params);
}
}

+ 32
- 0
src/Pay/Direct/ServiceProvider.php View File

@@ -0,0 +1,32 @@
<?php

/*
* This file is part of the overtrue/wechat.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace EasyWeChat\Pay\Direct;

use Pimple\Container;
use Pimple\ServiceProviderInterface;

/**
* Class ServiceProvider
* @package EasyWeChat\OnlineRetailer\Combine
*/
class ServiceProvider implements ServiceProviderInterface
{
/**
* {@inheritdoc}.
*/
public function register(Container $app)
{
$app['direct'] = function ($app) {
return new Client($app);
};
}
}

+ 118
- 0
src/Pay/Jssdk/Client.php View File

@@ -0,0 +1,118 @@
<?php

/*
* This file is part of the overtrue/wechat.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace EasyWeChat\Pay\Jssdk;

use EasyWeChat\BasicService\Jssdk\Client as JssdkClient;
use WechatPay\GuzzleMiddleware\Auth\PrivateKeySigner;
use WechatPay\GuzzleMiddleware\Util\PemUtil;

/**
* Class Client.
*
* @author overtrue <i@overtrue.me>
*/
class Client extends JssdkClient
{
/**
* [WeixinJSBridge] Generate js config for payment.
*
* <pre>
* WeixinJSBridge.invoke(
* 'getBrandWCPayRequest',
* ...
* );
* </pre>
*
* @param string $prepayId
* @param bool $json
*
* @return string|array
*/
public function bridgeConfig(string $prepayId, bool $json = true)
{
$params = [
'appId' => $this->app['config']->sub_appid ?: $this->app['config']->app_id,
'timeStamp' => strval(time()),
'nonceStr' => uniqid(),
'package' => "prepay_id=$prepayId"
];

$str = $params['appId'] . "\n" .
$params['timeStamp'] . "\n" .
$params['nonceStr'] . "\n" .
$params['package'] . "\n";
$params['signType'] = 'RSA';
$params['paySign'] = $this->sign($str);
return $json ? json_encode($params) : $params;
}

/**
* [JSSDK] Generate js config for payment.
*
* <pre>
* wx.chooseWXPay({...});
* </pre>
*
* @param string $prepayId
*
* @return array
*/
public function sdkConfig(string $prepayId): array
{
$config = $this->bridgeConfig($prepayId, false);

$config['timestamp'] = $config['timeStamp'];
unset($config['timeStamp']);

return $config;
}

/**
* Generate app payment parameters.
*
* @param string $prepayId
*
* @return array
*/
public function appConfig(string $prepayId): array
{
$params = [
'appid' => $this->app['config']->app_id,
'partnerid' => $this->app['config']->mch_id,
'prepayid' => $prepayId,
'noncestr' => uniqid(),
'timestamp' => time(),
'package' => 'Sign=WXPay',
];

$str = $params['appid'] . "\n" .
$params['timestamp'] . "\n" .
$params['noncestr'] . "\n" .
$prepayId . "\n";
$params['sign'] = $this->sign($str);
return $params;
}

/**
*
* @param string $str
* @return string
*/
protected function sign(string $str)
{
$privateKey = PemUtil::loadPrivateKey($this->app['config']->key_path);
$certificate = PemUtil::loadCertificate($this->app['config']->wx_cert_path);
$serialNo = PemUtil::parseCertificateSerialNo($certificate);
$encryptor = new PrivateKeySigner($serialNo, $privateKey);
return $encryptor->sign($str)->getSign();
}
}

+ 33
- 0
src/Pay/Jssdk/ServiceProvider.php View File

@@ -0,0 +1,33 @@
<?php

/*
* This file is part of the overtrue/wechat.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace EasyWeChat\Pay\Jssdk;

use Pimple\Container;
use Pimple\ServiceProviderInterface;

/**
* Class ServiceProvider.
*
* @author overtrue <i@overtrue.me>
*/
class ServiceProvider implements ServiceProviderInterface
{
/**
* {@inheritdoc}.
*/
public function register(Container $app)
{
$app['jssdk'] = function ($app) {
return new Client($app);
};
}
}

+ 34
- 0
src/Pay/Kernel/BaseClient.php View File

@@ -0,0 +1,34 @@
<?php

/*
* This file is part of the overtrue/wechat.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace EasyWeChat\Pay\Kernel;

use EasyWeChat\Pay\Application;
use EasyWeChat\OnlineRetailer\Kernel\BaseClient as PaymentClient;

/**
* Class BaseClient
* @package EasyWeChat\Combine\Kernel
*/
class BaseClient extends PaymentClient
{
/**
* BaseClient constructor.
*
* @param \EasyWeChat\Pay\Application $app
*/
public function __construct(Application $app)
{
$this->app = $app;

$this->setHttpClient($this->app['http_client']);
}
}

+ 23
- 0
src/Pay/Kernel/Exceptions/EncryptException.php View File

@@ -0,0 +1,23 @@
<?php

/*
* This file is part of the overtrue/wechat.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace EasyWeChat\OnlineRetailer\Kernel\Exceptions;

use EasyWeChat\Kernel\Exceptions\Exception;

/**
* Class EncryptException.
*
* @author liuml <liumenglei0211@163.com>
*/
class EncryptException extends Exception
{
}

+ 23
- 0
src/Pay/Kernel/Exceptions/InvalidExtensionException.php View File

@@ -0,0 +1,23 @@
<?php

/*
* This file is part of the overtrue/wechat.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace EasyWeChat\OnlineRetailer\Kernel\Exceptions;

use EasyWeChat\Kernel\Exceptions\Exception;

/**
* Class InvalidExtensionException.
*
* @author liuml <liumenglei0211@163.com>
*/
class InvalidExtensionException extends Exception
{
}

+ 23
- 0
src/Pay/Kernel/Exceptions/InvalidSignException.php View File

@@ -0,0 +1,23 @@
<?php

/*
* This file is part of the overtrue/wechat.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace EasyWeChat\OnlineRetailer\Kernel\Exceptions;

use EasyWeChat\Kernel\Exceptions\Exception;

/**
* Class InvalidSignException.
*
* @author liuml <liumenglei0211@163.com>
*/
class InvalidSignException extends Exception
{
}

+ 212
- 0
src/Pay/Notify/Handler.php View File

@@ -0,0 +1,212 @@
<?php

/*
* This file is part of the overtrue/wechat.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace EasyWeChat\Pay\Notify;

use Closure;
use EasyWeChat\Kernel\Exceptions\Exception;
use EasyWeChat\Kernel\Support;
use EasyWeChat\Payment\Kernel\Exceptions\InvalidSignException;
use Symfony\Component\HttpFoundation\Response;
use WechatPay\GuzzleMiddleware\Auth\CertificateVerifier;
use WechatPay\GuzzleMiddleware\Auth\WechatPay2Validator;
use WechatPay\GuzzleMiddleware\Util\AesUtil;
use WechatPay\GuzzleMiddleware\Util\PemUtil;

abstract class Handler
{
const SUCCESS = 'SUCCESS';
const FAIL = 'FAIL';

/**
* @var \EasyWeChat\Pay\Application
*/
protected $app;

/**
* @var array
*/
protected $message;

/**
* @var string|null
*/
protected $fail;

/**
* @var array
*/
protected $attributes = [];

/**
* Check sign.
* If failed, throws an exception.
*
* @var bool
*/
protected $check = true;

/**
* Respond with sign.
*
* @var bool
*/
protected $sign = false;

/**
* @param \EasyWeChat\Pay\Application $app
*/
public function __construct($app)
{
$this->app = $app;
}

/**
* Handle incoming notify.
*
* @param \Closure $closure
*
* @return \Symfony\Component\HttpFoundation\Response
*/
abstract public function handle(Closure $closure);

/**
* @param string $message
*/
public function fail(string $message)
{
$this->fail = $message;
}

/**
* @param array $attributes
* @param bool $sign
*
* @return $this
*/
public function respondWith(array $attributes, bool $sign = false)
{
$this->attributes = $attributes;
$this->sign = $sign;

return $this;
}

/**
* Build xml and return the response to WeChat.
*
* @return \Symfony\Component\HttpFoundation\Response
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
*/
public function toResponse(): Response
{
$base = [
'code' => is_null($this->fail) ? static::SUCCESS : static::FAIL,
'message' => $this->fail,
];

$attributes = array_merge($base, $this->attributes);

if ($this->sign) {
$attributes['sign'] = Support\generate_sign($attributes, $this->app->getKey());
}

return new Response(json_encode($base));
}

/**
* Return the notify message from request.
*
* @return array
* @throws Exception
* @throws InvalidSignException
*/
public function getMessage(): array
{
if (!empty($this->message)) {
return $this->message;
}

if ($this->check) {
// $this->validate($this->app['request']);
}

try {
$message = @json_decode($this->app['request']->getContent(), 1);
if ($message['resource']) {
$data = (new AesUtil($this->app->config->get('key_v3') ?? $this->app->config->get('key')))->decryptToString($message['resource']['associated_data'], $message['resource']['nonce'], $message['resource']['ciphertext']);
$message = @json_decode($data, true);
if (!is_array($message)) {
throw new Exception('Invalid request', 400);
}
}
} catch (\Throwable $e) {
throw new Exception('Invalid request XML: ' . $e->getMessage(), 400);
}

if (!is_array($message) || empty($message)) {
throw new Exception('Invalid request XML.', 400);
}

return $this->message = $message;
}

/**
* Decrypt message.
*
* @param string $key
*
* @return string|null
*
* @throws \EasyWeChat\Kernel\Exceptions\Exception
*/
public function decryptMessage(string $key)
{
$message = $this->getMessage();
if (empty($message[$key])) {
return null;
}

return Support\AES::decrypt(
base64_decode($message[$key], true),
md5($this->app['config']->key),
'',
OPENSSL_RAW_DATA,
'AES-256-ECB'
);
}

/**
* Validate the request params.
*
* @param $response
* @throws InvalidSignException
*/
protected function validate($response)
{
$certificate[] = PemUtil::loadCertificate($this->app->config->get('wx_cert_path'));
$validator = new WechatPay2Validator(new CertificateVerifier($certificate));
if (!$validator->validate($response)) {
throw new InvalidSignException();
}
}

/**
* @param mixed $result
*/
protected function strict($result)
{
if (true !== $result && is_null($this->fail)) {
$this->fail(strval($result));
}
}
}

+ 33
- 0
src/Pay/Notify/Paid.php View File

@@ -0,0 +1,33 @@
<?php

/*
* This file is part of the overtrue/wechat.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace EasyWeChat\Pay\Notify;

use Closure;

class Paid extends Handler
{
/**
* @param \Closure $closure
*
* @return \Symfony\Component\HttpFoundation\Response
*
* @throws \EasyWeChat\Kernel\Exceptions\Exception
*/
public function handle(Closure $closure)
{
$this->strict(
\call_user_func($closure, $this->getMessage(), [$this, 'fail'])
);

return $this->toResponse();
}
}

+ 48
- 0
src/Pay/Notify/Refunded.php View File

@@ -0,0 +1,48 @@
<?php

/*
* This file is part of the overtrue/wechat.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace EasyWeChat\Pay\Notify;

use Closure;
use EasyWeChat\Kernel\Support\XML;

class Refunded extends Handler
{
protected $check = false;

/**
* @param \Closure $closure
*
* @return \Symfony\Component\HttpFoundation\Response
*
* @throws \EasyWeChat\Kernel\Exceptions\Exception
*/
public function handle(Closure $closure)
{
$this->strict(
\call_user_func($closure, $this->getMessage(), $this->reqInfo(), [$this, 'fail'])
);

return $this->toResponse();
}

/**
* Decrypt the `req_info` from request message.
*
* @return array
*
* @throws \EasyWeChat\Kernel\Exceptions\Exception
*/
public function reqInfo()
{
return XML::parse($this->decryptMessage('req_info'));
}
}

+ 60
- 0
src/Pay/Notify/Scanned.php View File

@@ -0,0 +1,60 @@
<?php

/*
* This file is part of the overtrue/wechat.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace EasyWeChat\Pay\Notify;

use Closure;

class Scanned extends Handler
{
protected $check = false;

/**
* @var string|null
*/
protected $alert;

/**
* @param string $message
*/
public function alert(string $message)
{
$this->alert = $message;
}

/**
* @param \Closure $closure
*
* @return \Symfony\Component\HttpFoundation\Response
*
* @throws \EasyWeChat\Kernel\Exceptions\Exception
*/
public function handle(Closure $closure)
{
$result = \call_user_func($closure, $this->getMessage(), [$this, 'fail'], [$this, 'alert']);

$attributes = [
'result_code' => is_null($this->alert) && is_null($this->fail) ? static::SUCCESS : static::FAIL,
'err_code_des' => $this->alert,
];

if (is_null($this->alert) && is_string($result)) {
$attributes += [
'appid' => $this->app['config']->app_id,
'mch_id' => $this->app['config']->mch_id,
'nonce_str' => uniqid(),
'prepay_id' => $result,
];
}

return $this->respondWith($attributes, true)->toResponse();
}
}

+ 124
- 0
src/Pay/Partner/Client.php View File

@@ -0,0 +1,124 @@
<?php

/*
* This file is part of the overtrue/wechat.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace EasyWeChat\Pay\Partner;

use EasyWeChat\Pay\Kernel\BaseClient;

/**
* Class Client
* @package EasyWeChat\OnlineRetailer\Combine
*/
class Client extends BaseClient
{
/**
* app.
*
* @param $params
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function app($params)
{
return $this->httpPostJson('v3/pay/partner/transactions/app', $this->buildParams($params));
}

/**
* 公众号|小程序.
*
* @param $params
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function jsapi($params)
{
return $this->httpPostJson('v3/pay/partner/transactions/jsapi', $this->buildParams($params));
}

/**
* h5.
*
* @param $params
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function wap($params)
{
return $this->httpPostJson('v3/pay/partner/transactions/h5', $this->buildParams($params));
}

/**
* 扫码支付.
*
* @param $params
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function scan($params)
{
return $this->httpPostJson('v3/pay/partner/transactions/native', $this->buildParams($params));
}

/**
* @param $params
* @return array
*/
protected function buildParams($params): array
{
$params['sp_appid'] = $this->app->config->get('app_id');
$params['sp_mchid'] = $this->app->config->get('mch_id');
$params['sub_appid'] = $this->app->config->get('sub_appid');
$params['sub_mchid'] = $this->app->config->get('sub_mch_id');
$params['notify_url'] = $params['notify_url'] ?? $this->app['config']['notify_url'];
return $params;
}

/**
* 查询订单.
*
* @param $out_trade_no
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function query(string $out_trade_no)
{
$params['sp_mchid'] = $this->app->config->get('mch_id');
$params['sub_mchid'] = $this->app->config->get('sub_mch_id');
return $this->httpGet('v3/pay/partner/transactions/out-trade-no/' . $out_trade_no);
}

/**
* 关闭订单.
*
* @param string $out_trade_no
* @param array $params
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function close(string $out_trade_no, array $params)
{
$params['sp_mchid'] = $this->app->config->get('mch_id');
$params['sub_mchid'] = $this->app->config->get('sub_mch_id');
return $this->httpPostJson('v3/pay/partner/transactions/out-trade-no/' . $out_trade_no . '/close', $params);
}
}

+ 32
- 0
src/Pay/Partner/ServiceProvider.php View File

@@ -0,0 +1,32 @@
<?php

/*
* This file is part of the overtrue/wechat.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace EasyWeChat\Pay\Partner;

use Pimple\Container;
use Pimple\ServiceProviderInterface;

/**
* Class ServiceProvider
* @package EasyWeChat\OnlineRetailer\Combine
*/
class ServiceProvider implements ServiceProviderInterface
{
/**
* {@inheritdoc}.
*/
public function register(Container $app)
{
$app['partner'] = function ($app) {
return new Client($app);
};
}
}

Loading…
Cancel
Save