544782275@qq.com 3 jaren geleden
bovenliggende
commit
d20a959137
74 gewijzigde bestanden met toevoegingen van 9933 en 436 verwijderingen
  1. 2 1
      composer.json
  2. 2792 0
      composer.lock
  3. 1 1
      vendor/autoload.php
  4. 39 9
      vendor/composer/ClassLoader.php
  5. 1 1
      vendor/composer/LICENSE
  6. 20 20
      vendor/composer/autoload_files.php
  7. 3 1
      vendor/composer/autoload_psr4.php
  8. 1 1
      vendor/composer/autoload_real.php
  9. 36 23
      vendor/composer/autoload_static.php
  10. 324 379
      vendor/composer/installed.json
  11. 20 0
      vendor/yansongda/pay/LICENSE
  12. 310 0
      vendor/yansongda/pay/README.md
  13. 43 0
      vendor/yansongda/pay/composer.json
  14. 83 0
      vendor/yansongda/pay/src/Contracts/GatewayApplicationInterface.php
  15. 20 0
      vendor/yansongda/pay/src/Contracts/GatewayInterface.php
  16. 98 0
      vendor/yansongda/pay/src/Events.php
  17. 31 0
      vendor/yansongda/pay/src/Events/ApiRequested.php
  18. 31 0
      vendor/yansongda/pay/src/Events/ApiRequesting.php
  19. 40 0
      vendor/yansongda/pay/src/Events/Event.php
  20. 33 0
      vendor/yansongda/pay/src/Events/MethodCalled.php
  21. 31 0
      vendor/yansongda/pay/src/Events/PayStarted.php
  22. 23 0
      vendor/yansongda/pay/src/Events/PayStarting.php
  23. 25 0
      vendor/yansongda/pay/src/Events/RequestReceived.php
  24. 25 0
      vendor/yansongda/pay/src/Events/SignFailed.php
  25. 19 0
      vendor/yansongda/pay/src/Exceptions/BusinessException.php
  26. 44 0
      vendor/yansongda/pay/src/Exceptions/Exception.php
  27. 20 0
      vendor/yansongda/pay/src/Exceptions/GatewayException.php
  28. 19 0
      vendor/yansongda/pay/src/Exceptions/InvalidArgumentException.php
  29. 19 0
      vendor/yansongda/pay/src/Exceptions/InvalidConfigException.php
  30. 19 0
      vendor/yansongda/pay/src/Exceptions/InvalidGatewayException.php
  31. 19 0
      vendor/yansongda/pay/src/Exceptions/InvalidSignException.php
  32. 423 0
      vendor/yansongda/pay/src/Gateways/Alipay.php
  33. 38 0
      vendor/yansongda/pay/src/Gateways/Alipay/AppGateway.php
  34. 40 0
      vendor/yansongda/pay/src/Gateways/Alipay/Gateway.php
  35. 46 0
      vendor/yansongda/pay/src/Gateways/Alipay/MiniGateway.php
  36. 47 0
      vendor/yansongda/pay/src/Gateways/Alipay/PosGateway.php
  37. 21 0
      vendor/yansongda/pay/src/Gateways/Alipay/RefundGateway.php
  38. 41 0
      vendor/yansongda/pay/src/Gateways/Alipay/ScanGateway.php
  39. 456 0
      vendor/yansongda/pay/src/Gateways/Alipay/Support.php
  40. 49 0
      vendor/yansongda/pay/src/Gateways/Alipay/TransferGateway.php
  41. 26 0
      vendor/yansongda/pay/src/Gateways/Alipay/WapGateway.php
  42. 104 0
      vendor/yansongda/pay/src/Gateways/Alipay/WebGateway.php
  43. 365 0
      vendor/yansongda/pay/src/Gateways/Wechat.php
  44. 62 0
      vendor/yansongda/pay/src/Gateways/Wechat/AppGateway.php
  45. 88 0
      vendor/yansongda/pay/src/Gateways/Wechat/Gateway.php
  46. 73 0
      vendor/yansongda/pay/src/Gateways/Wechat/GroupRedpackGateway.php
  47. 35 0
      vendor/yansongda/pay/src/Gateways/Wechat/MiniappGateway.php
  48. 59 0
      vendor/yansongda/pay/src/Gateways/Wechat/MpGateway.php
  49. 44 0
      vendor/yansongda/pay/src/Gateways/Wechat/PosGateway.php
  50. 77 0
      vendor/yansongda/pay/src/Gateways/Wechat/RedpackGateway.php
  51. 50 0
      vendor/yansongda/pay/src/Gateways/Wechat/RefundGateway.php
  52. 44 0
      vendor/yansongda/pay/src/Gateways/Wechat/ScanGateway.php
  53. 451 0
      vendor/yansongda/pay/src/Gateways/Wechat/Support.php
  54. 80 0
      vendor/yansongda/pay/src/Gateways/Wechat/TransferGateway.php
  55. 47 0
      vendor/yansongda/pay/src/Gateways/Wechat/WapGateway.php
  56. 114 0
      vendor/yansongda/pay/src/Listeners/KernelLogSubscriber.php
  57. 49 0
      vendor/yansongda/pay/src/Log.php
  58. 131 0
      vendor/yansongda/pay/src/Pay.php
  59. 6 0
      vendor/yansongda/supports/.scrutinizer.yml
  60. 20 0
      vendor/yansongda/supports/LICENSE
  61. 70 0
      vendor/yansongda/supports/README.md
  62. 39 0
      vendor/yansongda/supports/composer.json
  63. 605 0
      vendor/yansongda/supports/src/Arr.php
  64. 363 0
      vendor/yansongda/supports/src/Collection.php
  65. 7 0
      vendor/yansongda/supports/src/Config.php
  66. 91 0
      vendor/yansongda/supports/src/Log.php
  67. 240 0
      vendor/yansongda/supports/src/Logger.php
  68. 36 0
      vendor/yansongda/supports/src/Logger/StdoutHandler.php
  69. 570 0
      vendor/yansongda/supports/src/Str.php
  70. 142 0
      vendor/yansongda/supports/src/Traits/Accessable.php
  71. 32 0
      vendor/yansongda/supports/src/Traits/Arrayable.php
  72. 229 0
      vendor/yansongda/supports/src/Traits/HasHttpRequest.php
  73. 85 0
      vendor/yansongda/supports/src/Traits/Serializable.php
  74. 147 0
      vendor/yansongda/supports/src/Traits/ShouldThrottle.php

+ 2 - 1
composer.json

@@ -30,7 +30,8 @@
         "ext-curl": "*",
         "ext-pdo": "*",
         "ext-bcmath": "*",
-        "txthinking/mailer": "^2.0"
+        "txthinking/mailer": "^2.0",
+        "yansongda/pay": "^2.10"
     },
     "config": {
         "preferred-install": "dist"

+ 2792 - 0
composer.lock

@@ -0,0 +1,2792 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+        "This file is @generated automatically"
+    ],
+    "content-hash": "b2c2b931e5ea09e8e8e4836ddf90aa86",
+    "packages": [
+        {
+            "name": "easywechat-composer/easywechat-composer",
+            "version": "1.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/mingyoung/easywechat-composer.git",
+                "reference": "93cfce1ec842b9a5b1b0791a52afd18b833f114a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/mingyoung/easywechat-composer/zipball/93cfce1ec842b9a5b1b0791a52afd18b833f114a",
+                "reference": "93cfce1ec842b9a5b1b0791a52afd18b833f114a",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "composer-plugin-api": "^1.0 || ^2.0",
+                "php": ">=7.0"
+            },
+            "require-dev": {
+                "composer/composer": "^1.0 || ^2.0",
+                "phpunit/phpunit": "^6.5 || ^7.0"
+            },
+            "type": "composer-plugin",
+            "extra": {
+                "class": "EasyWeChatComposer\\Plugin"
+            },
+            "autoload": {
+                "psr-4": {
+                    "EasyWeChatComposer\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "张铭阳",
+                    "email": "mingyoungcheung@gmail.com"
+                }
+            ],
+            "description": "The composer plugin for EasyWeChat",
+            "time": "2020-07-23T11:06:47+00:00"
+        },
+        {
+            "name": "guzzlehttp/guzzle",
+            "version": "6.5.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/guzzle.git",
+                "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/9d4290de1cfd701f38099ef7e183b64b4b7b0c5e",
+                "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "ext-json": "*",
+                "guzzlehttp/promises": "^1.0",
+                "guzzlehttp/psr7": "^1.6.1",
+                "php": ">=5.5",
+                "symfony/polyfill-intl-idn": "^1.17.0"
+            },
+            "require-dev": {
+                "ext-curl": "*",
+                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
+                "psr/log": "^1.1"
+            },
+            "suggest": {
+                "psr/log": "Required for using the Log middleware"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "6.5-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "GuzzleHttp\\": "src/"
+                },
+                "files": [
+                    "src/functions_include.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                }
+            ],
+            "description": "Guzzle is a PHP HTTP client library",
+            "homepage": "http://guzzlephp.org/",
+            "keywords": [
+                "client",
+                "curl",
+                "framework",
+                "http",
+                "http client",
+                "rest",
+                "web service"
+            ],
+            "time": "2020-06-16T21:01:06+00:00"
+        },
+        {
+            "name": "guzzlehttp/promises",
+            "version": "1.4.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/promises.git",
+                "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/promises/zipball/8e7d04f1f6450fef59366c399cfad4b9383aa30d",
+                "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=5.5"
+            },
+            "require-dev": {
+                "symfony/phpunit-bridge": "^4.4 || ^5.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "GuzzleHttp\\Promise\\": "src/"
+                },
+                "files": [
+                    "src/functions_include.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                }
+            ],
+            "description": "Guzzle promises library",
+            "keywords": [
+                "promise"
+            ],
+            "time": "2021-03-07T09:25:29+00:00"
+        },
+        {
+            "name": "guzzlehttp/psr7",
+            "version": "1.8.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/psr7.git",
+                "reference": "35ea11d335fd638b5882ff1725228b3d35496ab1"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/psr7/zipball/35ea11d335fd638b5882ff1725228b3d35496ab1",
+                "reference": "35ea11d335fd638b5882ff1725228b3d35496ab1",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=5.4.0",
+                "psr/http-message": "~1.0",
+                "ralouphie/getallheaders": "^2.0.5 || ^3.0.0"
+            },
+            "provide": {
+                "psr/http-message-implementation": "1.0"
+            },
+            "require-dev": {
+                "ext-zlib": "*",
+                "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10"
+            },
+            "suggest": {
+                "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.7-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "GuzzleHttp\\Psr7\\": "src/"
+                },
+                "files": [
+                    "src/functions_include.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                },
+                {
+                    "name": "Tobias Schultze",
+                    "homepage": "https://github.com/Tobion"
+                }
+            ],
+            "description": "PSR-7 message implementation that also provides common utility methods",
+            "keywords": [
+                "http",
+                "message",
+                "psr-7",
+                "request",
+                "response",
+                "stream",
+                "uri",
+                "url"
+            ],
+            "time": "2021-03-21T16:25:00+00:00"
+        },
+        {
+            "name": "karsonzhang/fastadmin-addons",
+            "version": "1.2.10",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/karsonzhang/fastadmin-addons.git",
+                "reference": "276dbed2bcaaa4eaab2019b36e4886123c2d985c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/karsonzhang/fastadmin-addons/zipball/276dbed2bcaaa4eaab2019b36e4886123c2d985c",
+                "reference": "276dbed2bcaaa4eaab2019b36e4886123c2d985c",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "nelexa/zip": "^3.3",
+                "php": ">=7.0.0",
+                "symfony/var-exporter": "^4.4.13"
+            },
+            "type": "library",
+            "extra": {
+                "think-config": {
+                    "addons": "src/config.php"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "think\\": "src/"
+                },
+                "files": [
+                    "src/common.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "Karson",
+                    "email": "karson@fastadmin.net"
+                },
+                {
+                    "name": "xiaobo.sun",
+                    "email": "xiaobo.sun@qq.com"
+                }
+            ],
+            "description": "addons package for fastadmin",
+            "homepage": "https://github.com/karsonzhang/fastadmin-addons",
+            "time": "2021-04-07T04:02:27+00:00"
+        },
+        {
+            "name": "markbaker/complex",
+            "version": "1.5.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/MarkBaker/PHPComplex.git",
+                "reference": "c3131244e29c08d44fefb49e0dd35021e9e39dd2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/c3131244e29c08d44fefb49e0dd35021e9e39dd2",
+                "reference": "c3131244e29c08d44fefb49e0dd35021e9e39dd2",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": "^5.6.0|^7.0"
+            },
+            "require-dev": {
+                "dealerdirect/phpcodesniffer-composer-installer": "^0.5.0",
+                "phpcompatibility/php-compatibility": "^9.0",
+                "phpdocumentor/phpdocumentor": "2.*",
+                "phploc/phploc": "^4.0|^5.0|^6.0|^7.0",
+                "phpmd/phpmd": "2.*",
+                "phpunit/phpunit": "^4.8.35|^5.0|^6.0|^7.0",
+                "sebastian/phpcpd": "2.*",
+                "squizlabs/php_codesniffer": "^3.4.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Complex\\": "classes/src/"
+                },
+                "files": [
+                    "classes/src/functions/abs.php",
+                    "classes/src/functions/acos.php",
+                    "classes/src/functions/acosh.php",
+                    "classes/src/functions/acot.php",
+                    "classes/src/functions/acoth.php",
+                    "classes/src/functions/acsc.php",
+                    "classes/src/functions/acsch.php",
+                    "classes/src/functions/argument.php",
+                    "classes/src/functions/asec.php",
+                    "classes/src/functions/asech.php",
+                    "classes/src/functions/asin.php",
+                    "classes/src/functions/asinh.php",
+                    "classes/src/functions/atan.php",
+                    "classes/src/functions/atanh.php",
+                    "classes/src/functions/conjugate.php",
+                    "classes/src/functions/cos.php",
+                    "classes/src/functions/cosh.php",
+                    "classes/src/functions/cot.php",
+                    "classes/src/functions/coth.php",
+                    "classes/src/functions/csc.php",
+                    "classes/src/functions/csch.php",
+                    "classes/src/functions/exp.php",
+                    "classes/src/functions/inverse.php",
+                    "classes/src/functions/ln.php",
+                    "classes/src/functions/log2.php",
+                    "classes/src/functions/log10.php",
+                    "classes/src/functions/negative.php",
+                    "classes/src/functions/pow.php",
+                    "classes/src/functions/rho.php",
+                    "classes/src/functions/sec.php",
+                    "classes/src/functions/sech.php",
+                    "classes/src/functions/sin.php",
+                    "classes/src/functions/sinh.php",
+                    "classes/src/functions/sqrt.php",
+                    "classes/src/functions/tan.php",
+                    "classes/src/functions/tanh.php",
+                    "classes/src/functions/theta.php",
+                    "classes/src/operations/add.php",
+                    "classes/src/operations/subtract.php",
+                    "classes/src/operations/multiply.php",
+                    "classes/src/operations/divideby.php",
+                    "classes/src/operations/divideinto.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Mark Baker",
+                    "email": "mark@lange.demon.co.uk"
+                }
+            ],
+            "description": "PHP Class for working with complex numbers",
+            "homepage": "https://github.com/MarkBaker/PHPComplex",
+            "keywords": [
+                "complex",
+                "mathematics"
+            ],
+            "time": "2020-08-26T19:47:57+00:00"
+        },
+        {
+            "name": "markbaker/matrix",
+            "version": "1.2.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/MarkBaker/PHPMatrix.git",
+                "reference": "44bb1ab01811116f01fe216ab37d921dccc6c10d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/44bb1ab01811116f01fe216ab37d921dccc6c10d",
+                "reference": "44bb1ab01811116f01fe216ab37d921dccc6c10d",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": "^5.6.0|^7.0.0"
+            },
+            "require-dev": {
+                "dealerdirect/phpcodesniffer-composer-installer": "dev-master",
+                "phpcompatibility/php-compatibility": "dev-master",
+                "phploc/phploc": "^4",
+                "phpmd/phpmd": "dev-master",
+                "phpunit/phpunit": "^5.7|^6.0|7.0",
+                "sebastian/phpcpd": "^3.0",
+                "squizlabs/php_codesniffer": "^3.0@dev"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Matrix\\": "classes/src/"
+                },
+                "files": [
+                    "classes/src/Functions/adjoint.php",
+                    "classes/src/Functions/antidiagonal.php",
+                    "classes/src/Functions/cofactors.php",
+                    "classes/src/Functions/determinant.php",
+                    "classes/src/Functions/diagonal.php",
+                    "classes/src/Functions/identity.php",
+                    "classes/src/Functions/inverse.php",
+                    "classes/src/Functions/minors.php",
+                    "classes/src/Functions/trace.php",
+                    "classes/src/Functions/transpose.php",
+                    "classes/src/Operations/add.php",
+                    "classes/src/Operations/directsum.php",
+                    "classes/src/Operations/subtract.php",
+                    "classes/src/Operations/multiply.php",
+                    "classes/src/Operations/divideby.php",
+                    "classes/src/Operations/divideinto.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Mark Baker",
+                    "email": "mark@lange.demon.co.uk"
+                }
+            ],
+            "description": "PHP Class for working with matrices",
+            "homepage": "https://github.com/MarkBaker/PHPMatrix",
+            "keywords": [
+                "mathematics",
+                "matrix",
+                "vector"
+            ],
+            "time": "2021-01-26T14:36:01+00:00"
+        },
+        {
+            "name": "monolog/monolog",
+            "version": "1.26.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Seldaek/monolog.git",
+                "reference": "2209ddd84e7ef1256b7af205d0717fb62cfc9c33"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Seldaek/monolog/zipball/2209ddd84e7ef1256b7af205d0717fb62cfc9c33",
+                "reference": "2209ddd84e7ef1256b7af205d0717fb62cfc9c33",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=5.3.0",
+                "psr/log": "~1.0"
+            },
+            "provide": {
+                "psr/log-implementation": "1.0.0"
+            },
+            "require-dev": {
+                "aws/aws-sdk-php": "^2.4.9 || ^3.0",
+                "doctrine/couchdb": "~1.0@dev",
+                "graylog2/gelf-php": "~1.0",
+                "php-amqplib/php-amqplib": "~2.4",
+                "php-console/php-console": "^3.1.3",
+                "phpstan/phpstan": "^0.12.59",
+                "phpunit/phpunit": "~4.5",
+                "ruflin/elastica": ">=0.90 <3.0",
+                "sentry/sentry": "^0.13",
+                "swiftmailer/swiftmailer": "^5.3|^6.0"
+            },
+            "suggest": {
+                "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
+                "doctrine/couchdb": "Allow sending log messages to a CouchDB server",
+                "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
+                "ext-mongo": "Allow sending log messages to a MongoDB server",
+                "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
+                "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver",
+                "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
+                "php-console/php-console": "Allow sending log messages to Google Chrome",
+                "rollbar/rollbar": "Allow sending log messages to Rollbar",
+                "ruflin/elastica": "Allow sending log messages to an Elastic Search server",
+                "sentry/sentry": "Allow sending log messages to a Sentry server"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Monolog\\": "src/Monolog"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jordi Boggiano",
+                    "email": "j.boggiano@seld.be",
+                    "homepage": "http://seld.be"
+                }
+            ],
+            "description": "Sends your logs to files, sockets, inboxes, databases and various web services",
+            "homepage": "http://github.com/Seldaek/monolog",
+            "keywords": [
+                "log",
+                "logging",
+                "psr-3"
+            ],
+            "time": "2020-12-14T12:56:38+00:00"
+        },
+        {
+            "name": "nelexa/zip",
+            "version": "3.3.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Ne-Lexa/php-zip.git",
+                "reference": "501b52f6fc393a599b44ff348a42740e1eaac7c6"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Ne-Lexa/php-zip/zipball/501b52f6fc393a599b44ff348a42740e1eaac7c6",
+                "reference": "501b52f6fc393a599b44ff348a42740e1eaac7c6",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "ext-zlib": "*",
+                "paragonie/random_compat": "*",
+                "php": "^5.5.9 || ^7.0",
+                "psr/http-message": "^1.0",
+                "symfony/finder": "^3.0|^4.0|^5.0"
+            },
+            "require-dev": {
+                "ext-bz2": "*",
+                "ext-fileinfo": "*",
+                "ext-openssl": "*",
+                "ext-xml": "*",
+                "guzzlehttp/psr7": "^1.6",
+                "phpunit/phpunit": "^4.8|^5.7",
+                "symfony/var-dumper": "^3.0|^4.0|^5.0"
+            },
+            "suggest": {
+                "ext-bz2": "Needed to support BZIP2 compression",
+                "ext-fileinfo": "Needed to get mime-type file",
+                "ext-mcrypt": "Needed to support encrypt zip entries or use ext-openssl",
+                "ext-openssl": "Needed to support encrypt zip entries or use ext-mcrypt"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "PhpZip\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ne-Lexa",
+                    "email": "alexey@nelexa.ru",
+                    "role": "Developer"
+                }
+            ],
+            "description": "PhpZip is a php-library for extended work with ZIP-archives. Open, create, update, delete, extract and get info tool. Supports appending to existing ZIP files, WinZip AES encryption, Traditional PKWARE Encryption, ZipAlign tool, BZIP2 compression, external file attributes and ZIP64 extensions. Alternative ZipArchive. It does not require php-zip extension.",
+            "homepage": "https://github.com/Ne-Lexa/php-zip",
+            "keywords": [
+                "archive",
+                "extract",
+                "unzip",
+                "winzip",
+                "zip",
+                "zipalign",
+                "ziparchive"
+            ],
+            "time": "2020-07-11T21:01:42+00:00"
+        },
+        {
+            "name": "overtrue/pinyin",
+            "version": "3.0.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/overtrue/pinyin.git",
+                "reference": "3b781d267197b74752daa32814d3a2cf5d140779"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/overtrue/pinyin/zipball/3b781d267197b74752daa32814d3a2cf5d140779",
+                "reference": "3b781d267197b74752daa32814d3a2cf5d140779",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=5.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.8"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Overtrue\\Pinyin\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Carlos",
+                    "homepage": "http://github.com/overtrue"
+                }
+            ],
+            "description": "Chinese to pinyin translator.",
+            "homepage": "https://github.com/overtrue/pinyin",
+            "keywords": [
+                "Chinese",
+                "Pinyin",
+                "cn2pinyin"
+            ],
+            "time": "2017-07-10T07:20:01+00:00"
+        },
+        {
+            "name": "overtrue/socialite",
+            "version": "2.0.23",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/overtrue/socialite.git",
+                "reference": "0bc60597b589592243f074a4d9016aabd2e9cfb2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/overtrue/socialite/zipball/0bc60597b589592243f074a4d9016aabd2e9cfb2",
+                "reference": "0bc60597b589592243f074a4d9016aabd2e9cfb2",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "ext-json": "*",
+                "guzzlehttp/guzzle": "^5.0|^6.0|^7.0",
+                "php": ">=5.6",
+                "symfony/http-foundation": "^2.7|^3.0|^4.0|^5.0"
+            },
+            "require-dev": {
+                "mockery/mockery": "~1.2",
+                "phpunit/phpunit": "~6"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Overtrue\\Socialite\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "overtrue",
+                    "email": "anzhengchao@gmail.com"
+                }
+            ],
+            "description": "A collection of OAuth 2 packages that extracts from laravel/socialite.",
+            "keywords": [
+                "login",
+                "oauth",
+                "qq",
+                "social",
+                "wechat",
+                "weibo"
+            ],
+            "time": "2020-12-14T03:30:08+00:00"
+        },
+        {
+            "name": "overtrue/wechat",
+            "version": "4.2.11",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/w7corp/easywechat.git",
+                "reference": "853e0772e6aa53a71edf1b5d251c7ff1e6b2a2bf"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/w7corp/easywechat/zipball/853e0772e6aa53a71edf1b5d251c7ff1e6b2a2bf",
+                "reference": "853e0772e6aa53a71edf1b5d251c7ff1e6b2a2bf",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "easywechat-composer/easywechat-composer": "^1.1",
+                "ext-fileinfo": "*",
+                "ext-openssl": "*",
+                "ext-simplexml": "*",
+                "guzzlehttp/guzzle": "^6.2",
+                "monolog/monolog": "^1.22 || ^2.0",
+                "overtrue/socialite": "~2.0",
+                "php": ">=7.1",
+                "pimple/pimple": "^3.0",
+                "psr/simple-cache": "^1.0",
+                "symfony/cache": "^3.3 || ^4.3",
+                "symfony/event-dispatcher": "^4.3",
+                "symfony/http-foundation": "^2.7 || ^3.0 || ^4.0",
+                "symfony/psr-http-message-bridge": "^0.3 || ^1.0"
+            },
+            "require-dev": {
+                "friendsofphp/php-cs-fixer": "^2.15",
+                "mikey179/vfsstream": "^1.6",
+                "mockery/mockery": "^1.2.3",
+                "phpstan/phpstan": "^0.11.12",
+                "phpunit/phpunit": "^7.5"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "EasyWeChat\\": "src/"
+                },
+                "files": [
+                    "src/Kernel/Support/Helpers.php",
+                    "src/Kernel/Helpers.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "overtrue",
+                    "email": "anzhengchao@gmail.com"
+                }
+            ],
+            "description": "微信SDK",
+            "keywords": [
+                "sdk",
+                "wechat",
+                "weixin",
+                "weixin-sdk"
+            ],
+            "time": "2019-11-27T16:38:00+00:00"
+        },
+        {
+            "name": "paragonie/random_compat",
+            "version": "v9.99.100",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/paragonie/random_compat.git",
+                "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a",
+                "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">= 7"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "4.*|5.*",
+                "vimeo/psalm": "^1"
+            },
+            "suggest": {
+                "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
+            },
+            "type": "library",
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Paragon Initiative Enterprises",
+                    "email": "security@paragonie.com",
+                    "homepage": "https://paragonie.com"
+                }
+            ],
+            "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
+            "keywords": [
+                "csprng",
+                "polyfill",
+                "pseudorandom",
+                "random"
+            ],
+            "time": "2020-10-15T08:29:30+00:00"
+        },
+        {
+            "name": "phpoffice/phpspreadsheet",
+            "version": "1.12.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
+                "reference": "f79611d6dc1f6b7e8e30b738fc371b392001dbfd"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/f79611d6dc1f6b7e8e30b738fc371b392001dbfd",
+                "reference": "f79611d6dc1f6b7e8e30b738fc371b392001dbfd",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "ext-ctype": "*",
+                "ext-dom": "*",
+                "ext-fileinfo": "*",
+                "ext-gd": "*",
+                "ext-iconv": "*",
+                "ext-libxml": "*",
+                "ext-mbstring": "*",
+                "ext-simplexml": "*",
+                "ext-xml": "*",
+                "ext-xmlreader": "*",
+                "ext-xmlwriter": "*",
+                "ext-zip": "*",
+                "ext-zlib": "*",
+                "markbaker/complex": "^1.4",
+                "markbaker/matrix": "^1.2",
+                "php": "^7.1",
+                "psr/simple-cache": "^1.0"
+            },
+            "require-dev": {
+                "dompdf/dompdf": "^0.8.3",
+                "friendsofphp/php-cs-fixer": "^2.16",
+                "jpgraph/jpgraph": "^4.0",
+                "mpdf/mpdf": "^8.0",
+                "phpcompatibility/php-compatibility": "^9.3",
+                "phpunit/phpunit": "^7.5",
+                "squizlabs/php_codesniffer": "^3.5",
+                "tecnickcom/tcpdf": "^6.3"
+            },
+            "suggest": {
+                "dompdf/dompdf": "Option for rendering PDF with PDF Writer",
+                "jpgraph/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers",
+                "mpdf/mpdf": "Option for rendering PDF with PDF Writer",
+                "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Maarten Balliauw",
+                    "homepage": "https://blog.maartenballiauw.be"
+                },
+                {
+                    "name": "Mark Baker",
+                    "homepage": "https://markbakeruk.net"
+                },
+                {
+                    "name": "Franck Lefevre",
+                    "homepage": "https://rootslabs.net"
+                },
+                {
+                    "name": "Erik Tilt"
+                },
+                {
+                    "name": "Adrien Crivelli"
+                }
+            ],
+            "description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
+            "homepage": "https://github.com/PHPOffice/PhpSpreadsheet",
+            "keywords": [
+                "OpenXML",
+                "excel",
+                "gnumeric",
+                "ods",
+                "php",
+                "spreadsheet",
+                "xls",
+                "xlsx"
+            ],
+            "time": "2020-04-27T08:12:48+00:00"
+        },
+        {
+            "name": "pimple/pimple",
+            "version": "v3.2.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/silexphp/Pimple.git",
+                "reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/silexphp/Pimple/zipball/9e403941ef9d65d20cba7d54e29fe906db42cf32",
+                "reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=5.3.0",
+                "psr/container": "^1.0"
+            },
+            "require-dev": {
+                "symfony/phpunit-bridge": "^3.2"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.2.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Pimple": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                }
+            ],
+            "description": "Pimple, a simple Dependency Injection Container",
+            "homepage": "http://pimple.sensiolabs.org",
+            "keywords": [
+                "container",
+                "dependency injection"
+            ],
+            "time": "2018-01-21T07:42:36+00:00"
+        },
+        {
+            "name": "psr/cache",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/cache.git",
+                "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8",
+                "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Cache\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for caching libraries",
+            "keywords": [
+                "cache",
+                "psr",
+                "psr-6"
+            ],
+            "time": "2016-08-06T20:24:11+00:00"
+        },
+        {
+            "name": "psr/container",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/container.git",
+                "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+                "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Container\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common Container Interface (PHP FIG PSR-11)",
+            "homepage": "https://github.com/php-fig/container",
+            "keywords": [
+                "PSR-11",
+                "container",
+                "container-interface",
+                "container-interop",
+                "psr"
+            ],
+            "time": "2017-02-14T16:28:37+00:00"
+        },
+        {
+            "name": "psr/http-message",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-message.git",
+                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
+                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Message\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for HTTP messages",
+            "homepage": "https://github.com/php-fig/http-message",
+            "keywords": [
+                "http",
+                "http-message",
+                "psr",
+                "psr-7",
+                "request",
+                "response"
+            ],
+            "time": "2016-08-06T14:39:51+00:00"
+        },
+        {
+            "name": "psr/log",
+            "version": "1.1.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/log.git",
+                "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc",
+                "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Log\\": "Psr/Log/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for logging libraries",
+            "homepage": "https://github.com/php-fig/log",
+            "keywords": [
+                "log",
+                "psr",
+                "psr-3"
+            ],
+            "time": "2020-03-23T09:12:05+00:00"
+        },
+        {
+            "name": "psr/simple-cache",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/simple-cache.git",
+                "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
+                "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\SimpleCache\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interfaces for simple caching",
+            "keywords": [
+                "cache",
+                "caching",
+                "psr",
+                "psr-16",
+                "simple-cache"
+            ],
+            "time": "2017-10-23T01:57:42+00:00"
+        },
+        {
+            "name": "ralouphie/getallheaders",
+            "version": "3.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ralouphie/getallheaders.git",
+                "reference": "120b605dfeb996808c31b6477290a714d356e822"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
+                "reference": "120b605dfeb996808c31b6477290a714d356e822",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=5.6"
+            },
+            "require-dev": {
+                "php-coveralls/php-coveralls": "^2.1",
+                "phpunit/phpunit": "^5 || ^6.5"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/getallheaders.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ralph Khattar",
+                    "email": "ralph.khattar@gmail.com"
+                }
+            ],
+            "description": "A polyfill for getallheaders.",
+            "time": "2019-03-08T08:55:37+00:00"
+        },
+        {
+            "name": "symfony/cache",
+            "version": "v4.4.21",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/cache.git",
+                "reference": "b7ff54be3f3eb1ce09643692f0c309b1b27bc992"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/cache/zipball/b7ff54be3f3eb1ce09643692f0c309b1b27bc992",
+                "reference": "b7ff54be3f3eb1ce09643692f0c309b1b27bc992",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.1.3",
+                "psr/cache": "^1.0|^2.0",
+                "psr/log": "~1.0",
+                "symfony/cache-contracts": "^1.1.7|^2",
+                "symfony/service-contracts": "^1.1|^2",
+                "symfony/var-exporter": "^4.2|^5.0"
+            },
+            "conflict": {
+                "doctrine/dbal": "<2.6",
+                "symfony/dependency-injection": "<3.4",
+                "symfony/http-kernel": "<4.4|>=5.0",
+                "symfony/var-dumper": "<4.4"
+            },
+            "provide": {
+                "psr/cache-implementation": "1.0|2.0",
+                "psr/simple-cache-implementation": "1.0",
+                "symfony/cache-implementation": "1.0|2.0"
+            },
+            "require-dev": {
+                "cache/integration-tests": "dev-master",
+                "doctrine/cache": "^1.6",
+                "doctrine/dbal": "^2.6|^3.0",
+                "predis/predis": "^1.1",
+                "psr/simple-cache": "^1.0",
+                "symfony/config": "^4.2|^5.0",
+                "symfony/dependency-injection": "^3.4|^4.1|^5.0",
+                "symfony/filesystem": "^4.4|^5.0",
+                "symfony/http-kernel": "^4.4",
+                "symfony/var-dumper": "^4.4|^5.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Cache\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides an extended PSR-6, PSR-16 (and tags) implementation",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "caching",
+                "psr6"
+            ],
+            "time": "2021-03-14T19:28:18+00:00"
+        },
+        {
+            "name": "symfony/cache-contracts",
+            "version": "v1.1.10",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/cache-contracts.git",
+                "reference": "8d5489c10ef90aa7413e4921fc3c0520e24cbed7"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/8d5489c10ef90aa7413e4921fc3c0520e24cbed7",
+                "reference": "8d5489c10ef90aa7413e4921fc3c0520e24cbed7",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.1.3",
+                "psr/cache": "^1.0"
+            },
+            "suggest": {
+                "symfony/cache-implementation": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Contracts\\Cache\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Generic abstractions related to caching",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "abstractions",
+                "contracts",
+                "decoupling",
+                "interfaces",
+                "interoperability",
+                "standards"
+            ],
+            "time": "2020-09-02T16:08:58+00:00"
+        },
+        {
+            "name": "symfony/event-dispatcher",
+            "version": "v4.4.20",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/event-dispatcher.git",
+                "reference": "c352647244bd376bf7d31efbd5401f13f50dad0c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/c352647244bd376bf7d31efbd5401f13f50dad0c",
+                "reference": "c352647244bd376bf7d31efbd5401f13f50dad0c",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.1.3",
+                "symfony/event-dispatcher-contracts": "^1.1"
+            },
+            "conflict": {
+                "symfony/dependency-injection": "<3.4"
+            },
+            "provide": {
+                "psr/event-dispatcher-implementation": "1.0",
+                "symfony/event-dispatcher-implementation": "1.1"
+            },
+            "require-dev": {
+                "psr/log": "~1.0",
+                "symfony/config": "^3.4|^4.0|^5.0",
+                "symfony/dependency-injection": "^3.4|^4.0|^5.0",
+                "symfony/error-handler": "~3.4|~4.4",
+                "symfony/expression-language": "^3.4|^4.0|^5.0",
+                "symfony/http-foundation": "^3.4|^4.0|^5.0",
+                "symfony/service-contracts": "^1.1|^2",
+                "symfony/stopwatch": "^3.4|^4.0|^5.0"
+            },
+            "suggest": {
+                "symfony/dependency-injection": "",
+                "symfony/http-kernel": ""
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\EventDispatcher\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
+            "homepage": "https://symfony.com",
+            "time": "2021-01-27T09:09:26+00:00"
+        },
+        {
+            "name": "symfony/event-dispatcher-contracts",
+            "version": "v1.1.9",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/event-dispatcher-contracts.git",
+                "reference": "84e23fdcd2517bf37aecbd16967e83f0caee25a7"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/84e23fdcd2517bf37aecbd16967e83f0caee25a7",
+                "reference": "84e23fdcd2517bf37aecbd16967e83f0caee25a7",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.1.3"
+            },
+            "suggest": {
+                "psr/event-dispatcher": "",
+                "symfony/event-dispatcher-implementation": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Contracts\\EventDispatcher\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Generic abstractions related to dispatching event",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "abstractions",
+                "contracts",
+                "decoupling",
+                "interfaces",
+                "interoperability",
+                "standards"
+            ],
+            "time": "2020-07-06T13:19:58+00:00"
+        },
+        {
+            "name": "symfony/finder",
+            "version": "v4.4.20",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/finder.git",
+                "reference": "2543795ab1570df588b9bbd31e1a2bd7037b94f6"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/finder/zipball/2543795ab1570df588b9bbd31e1a2bd7037b94f6",
+                "reference": "2543795ab1570df588b9bbd31e1a2bd7037b94f6",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.1.3"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Finder\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Finds files and directories via an intuitive fluent interface",
+            "homepage": "https://symfony.com",
+            "time": "2021-02-12T10:48:09+00:00"
+        },
+        {
+            "name": "symfony/http-foundation",
+            "version": "v4.4.20",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/http-foundation.git",
+                "reference": "02d968647fe61b2f419a8dc70c468a9d30a48d3a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/02d968647fe61b2f419a8dc70c468a9d30a48d3a",
+                "reference": "02d968647fe61b2f419a8dc70c468a9d30a48d3a",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.1.3",
+                "symfony/mime": "^4.3|^5.0",
+                "symfony/polyfill-mbstring": "~1.1",
+                "symfony/polyfill-php80": "^1.15"
+            },
+            "require-dev": {
+                "predis/predis": "~1.0",
+                "symfony/expression-language": "^3.4|^4.0|^5.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\HttpFoundation\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Defines an object-oriented layer for the HTTP specification",
+            "homepage": "https://symfony.com",
+            "time": "2021-02-25T17:11:33+00:00"
+        },
+        {
+            "name": "symfony/mime",
+            "version": "v4.4.21",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/mime.git",
+                "reference": "50d7a1d569edad1f1321c59123c4c322c8daff7c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/mime/zipball/50d7a1d569edad1f1321c59123c4c322c8daff7c",
+                "reference": "50d7a1d569edad1f1321c59123c4c322c8daff7c",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.1.3",
+                "symfony/polyfill-intl-idn": "^1.10",
+                "symfony/polyfill-mbstring": "^1.0"
+            },
+            "conflict": {
+                "egulias/email-validator": "~3.0.0",
+                "symfony/mailer": "<4.4"
+            },
+            "require-dev": {
+                "egulias/email-validator": "^2.1.10|^3.1",
+                "symfony/dependency-injection": "^3.4|^4.1|^5.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Mime\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Allows manipulating MIME messages",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "mime",
+                "mime-type"
+            ],
+            "time": "2021-03-12T08:47:38+00:00"
+        },
+        {
+            "name": "symfony/polyfill-intl-idn",
+            "version": "v1.22.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-intl-idn.git",
+                "reference": "2d63434d922daf7da8dd863e7907e67ee3031483"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/2d63434d922daf7da8dd863e7907e67ee3031483",
+                "reference": "2d63434d922daf7da8dd863e7907e67ee3031483",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.1",
+                "symfony/polyfill-intl-normalizer": "^1.10",
+                "symfony/polyfill-php72": "^1.10"
+            },
+            "suggest": {
+                "ext-intl": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.22-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Intl\\Idn\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Laurent Bassin",
+                    "email": "laurent@bassin.info"
+                },
+                {
+                    "name": "Trevor Rowbotham",
+                    "email": "trevor.rowbotham@pm.me"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "idn",
+                "intl",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "time": "2021-01-22T09:19:47+00:00"
+        },
+        {
+            "name": "symfony/polyfill-intl-normalizer",
+            "version": "v1.22.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+                "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/43a0283138253ed1d48d352ab6d0bdb3f809f248",
+                "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "suggest": {
+                "ext-intl": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.22-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ],
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for intl's Normalizer class and related functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "intl",
+                "normalizer",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "time": "2021-01-22T09:19:47+00:00"
+        },
+        {
+            "name": "symfony/polyfill-mbstring",
+            "version": "v1.22.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-mbstring.git",
+                "reference": "5232de97ee3b75b0360528dae24e73db49566ab1"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/5232de97ee3b75b0360528dae24e73db49566ab1",
+                "reference": "5232de97ee3b75b0360528dae24e73db49566ab1",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "suggest": {
+                "ext-mbstring": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.22-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Mbstring\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for the Mbstring extension",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "mbstring",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "time": "2021-01-22T09:19:47+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php72",
+            "version": "v1.22.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php72.git",
+                "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9",
+                "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.22-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php72\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "time": "2021-01-07T16:49:33+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php80",
+            "version": "v1.22.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php80.git",
+                "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dc3063ba22c2a1fd2f45ed856374d79114998f91",
+                "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.22-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php80\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ],
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ion Bazan",
+                    "email": "ion.bazan@gmail.com"
+                },
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "time": "2021-01-07T16:49:33+00:00"
+        },
+        {
+            "name": "symfony/psr-http-message-bridge",
+            "version": "v1.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/psr-http-message-bridge.git",
+                "reference": "9d3e80d54d9ae747ad573cad796e8e247df7b796"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/9d3e80d54d9ae747ad573cad796e8e247df7b796",
+                "reference": "9d3e80d54d9ae747ad573cad796e8e247df7b796",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": "^7.1",
+                "psr/http-message": "^1.0",
+                "symfony/http-foundation": "^4.4 || ^5.0"
+            },
+            "require-dev": {
+                "nyholm/psr7": "^1.1",
+                "symfony/phpunit-bridge": "^4.4 || ^5.0",
+                "zendframework/zend-diactoros": "^1.4.1 || ^2.0"
+            },
+            "suggest": {
+                "nyholm/psr7": "For a super lightweight PSR-7/17 implementation"
+            },
+            "type": "symfony-bridge",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.3-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Bridge\\PsrHttpMessage\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "http://symfony.com/contributors"
+                }
+            ],
+            "description": "PSR HTTP message bridge",
+            "homepage": "http://symfony.com",
+            "keywords": [
+                "http",
+                "http-message",
+                "psr-17",
+                "psr-7"
+            ],
+            "time": "2019-11-25T19:33:50+00:00"
+        },
+        {
+            "name": "symfony/service-contracts",
+            "version": "v1.1.9",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/service-contracts.git",
+                "reference": "b776d18b303a39f56c63747bcb977ad4b27aca26"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/service-contracts/zipball/b776d18b303a39f56c63747bcb977ad4b27aca26",
+                "reference": "b776d18b303a39f56c63747bcb977ad4b27aca26",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.1.3",
+                "psr/container": "^1.0"
+            },
+            "suggest": {
+                "symfony/service-implementation": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Contracts\\Service\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Generic abstractions related to writing services",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "abstractions",
+                "contracts",
+                "decoupling",
+                "interfaces",
+                "interoperability",
+                "standards"
+            ],
+            "time": "2020-07-06T13:19:58+00:00"
+        },
+        {
+            "name": "symfony/var-exporter",
+            "version": "v4.4.20",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/var-exporter.git",
+                "reference": "3a3ea598bba6901d20b58c2579f68700089244ed"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/var-exporter/zipball/3a3ea598bba6901d20b58c2579f68700089244ed",
+                "reference": "3a3ea598bba6901d20b58c2579f68700089244ed",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.1.3"
+            },
+            "require-dev": {
+                "symfony/var-dumper": "^4.4.9|^5.0.9"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\VarExporter\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Allows exporting any serializable PHP data structure to plain PHP code",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "clone",
+                "construct",
+                "export",
+                "hydrate",
+                "instantiate",
+                "serialize"
+            ],
+            "time": "2021-01-27T09:09:26+00:00"
+        },
+        {
+            "name": "topthink/framework",
+            "version": "dev-master",
+            "source": {
+                "type": "git",
+                "url": "https://gitee.com/karson/framework",
+                "reference": "7d08e64b4d8e3352c0f855e63513d85aeaa41349"
+            },
+            "require": {
+                "php": ">=5.4.0",
+                "topthink/think-installer": "~1.0"
+            },
+            "require-dev": {
+                "johnkary/phpunit-speedtrap": "^1.0",
+                "mikey179/vfsstream": "~1.6",
+                "phpdocumentor/reflection-docblock": "^2.0",
+                "phploc/phploc": "2.*",
+                "phpunit/phpunit": "4.8.*",
+                "sebastian/phpcpd": "2.*"
+            },
+            "type": "think-framework",
+            "autoload": {
+                "psr-4": {
+                    "think\\": "library/think"
+                }
+            },
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "liu21st",
+                    "email": "liu21st@gmail.com"
+                }
+            ],
+            "description": "the new thinkphp framework",
+            "homepage": "http://thinkphp.cn/",
+            "keywords": [
+                "ORM",
+                "framework",
+                "thinkphp"
+            ],
+            "time": "2021-03-17T09:43:15+00:00"
+        },
+        {
+            "name": "topthink/think-captcha",
+            "version": "v1.0.7",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/top-think/think-captcha.git",
+                "reference": "0c55455df26a1626a60d0dc35d2d89002b741d44"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/top-think/think-captcha/zipball/0c55455df26a1626a60d0dc35d2d89002b741d44",
+                "reference": "0c55455df26a1626a60d0dc35d2d89002b741d44",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "think\\captcha\\": "src/"
+                },
+                "files": [
+                    "src/helper.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "yunwuxin",
+                    "email": "448901948@qq.com"
+                }
+            ],
+            "description": "captcha package for thinkphp5",
+            "time": "2016-07-06T01:47:11+00:00"
+        },
+        {
+            "name": "topthink/think-helper",
+            "version": "v1.0.7",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/top-think/think-helper.git",
+                "reference": "5f92178606c8ce131d36b37a57c58eb71e55f019"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/top-think/think-helper/zipball/5f92178606c8ce131d36b37a57c58eb71e55f019",
+                "reference": "5f92178606c8ce131d36b37a57c58eb71e55f019",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "think\\helper\\": "src"
+                },
+                "files": [
+                    "src/helper.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "yunwuxin",
+                    "email": "448901948@qq.com"
+                }
+            ],
+            "description": "The ThinkPHP5 Helper Package",
+            "time": "2018-10-05T00:43:21+00:00"
+        },
+        {
+            "name": "topthink/think-installer",
+            "version": "v1.0.14",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/top-think/think-installer.git",
+                "reference": "eae1740ac264a55c06134b6685dfb9f837d004d1"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/top-think/think-installer/zipball/eae1740ac264a55c06134b6685dfb9f837d004d1",
+                "reference": "eae1740ac264a55c06134b6685dfb9f837d004d1",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "composer-plugin-api": "^1.0||^2.0"
+            },
+            "require-dev": {
+                "composer/composer": "^1.0||^2.0"
+            },
+            "type": "composer-plugin",
+            "extra": {
+                "class": "think\\composer\\Plugin"
+            },
+            "autoload": {
+                "psr-4": {
+                    "think\\composer\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "yunwuxin",
+                    "email": "448901948@qq.com"
+                }
+            ],
+            "time": "2021-03-25T08:34:02+00:00"
+        },
+        {
+            "name": "topthink/think-queue",
+            "version": "v1.1.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/top-think/think-queue.git",
+                "reference": "250650eb0e8ea5af4cfdc7ae46f3f4e0a24ac245"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/top-think/think-queue/zipball/250650eb0e8ea5af4cfdc7ae46f3f4e0a24ac245",
+                "reference": "250650eb0e8ea5af4cfdc7ae46f3f4e0a24ac245",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "topthink/think-helper": ">=1.0.4",
+                "topthink/think-installer": ">=1.0.10"
+            },
+            "require-dev": {
+                "topthink/framework": "~5.0.0"
+            },
+            "type": "think-extend",
+            "extra": {
+                "think-config": {
+                    "queue": "src/config.php"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "think\\": "src"
+                },
+                "files": [
+                    "src/common.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "yunwuxin",
+                    "email": "448901948@qq.com"
+                }
+            ],
+            "description": "The ThinkPHP5 Queue Package",
+            "time": "2018-10-15T10:16:55+00:00"
+        },
+        {
+            "name": "txthinking/mailer",
+            "version": "v2.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/txthinking/Mailer.git",
+                "reference": "09013cf9dad3aac195f66ae5309e8c3343c018e9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/txthinking/Mailer/zipball/09013cf9dad3aac195f66ae5309e8c3343c018e9",
+                "reference": "09013cf9dad3aac195f66ae5309e8c3343c018e9",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=5.3.2",
+                "psr/log": "~1.0"
+            },
+            "require-dev": {
+                "monolog/monolog": "~1.13",
+                "phpunit/phpunit": "~4.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Tx\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Cloud",
+                    "email": "cloud@txthinking.com",
+                    "homepage": "http://www.txthinking.com",
+                    "role": "Thinker"
+                },
+                {
+                    "name": "Matt Sowers",
+                    "email": "msowers@erblearn.org"
+                }
+            ],
+            "description": "A very lightweight PHP SMTP mail sender",
+            "homepage": "http://github.com/txthinking/Mailer",
+            "keywords": [
+                "mail",
+                "smtp"
+            ],
+            "time": "2018-10-09T10:47:23+00:00"
+        },
+        {
+            "name": "yansongda/pay",
+            "version": "v2.10.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/yansongda/pay.git",
+                "reference": "8c258853b142c6d7589629b047ca5cb21232b509"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/yansongda/pay/zipball/8c258853b142c6d7589629b047ca5cb21232b509",
+                "reference": "8c258853b142c6d7589629b047ca5cb21232b509",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "ext-bcmath": "*",
+                "ext-json": "*",
+                "ext-libxml": "*",
+                "ext-openssl": "*",
+                "ext-simplexml": "*",
+                "php": ">=7.1.3",
+                "symfony/event-dispatcher": "^4.0 || ^5.0",
+                "symfony/http-foundation": "^4.0 || ^5.0.7",
+                "yansongda/supports": "^2.0"
+            },
+            "require-dev": {
+                "friendsofphp/php-cs-fixer": "^2.15",
+                "mockery/mockery": "^1.2",
+                "phpunit/phpunit": "^7.5"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Yansongda\\Pay\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "yansongda",
+                    "email": "me@yansongda.cn"
+                }
+            ],
+            "description": "专注 Alipay 和 WeChat 的支付扩展包",
+            "keywords": [
+                "alipay",
+                "pay",
+                "wechat"
+            ],
+            "time": "2021-01-18T01:48:43+00:00"
+        },
+        {
+            "name": "yansongda/supports",
+            "version": "v2.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/yansongda/supports.git",
+                "reference": "de9a8d38b0461ddf9c12f27390dad9a40c9b4e3b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/yansongda/supports/zipball/de9a8d38b0461ddf9c12f27390dad9a40c9b4e3b",
+                "reference": "de9a8d38b0461ddf9c12f27390dad9a40c9b4e3b",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "guzzlehttp/guzzle": "^6.2 || ^7.0",
+                "monolog/monolog": "^1.23 || ^2.0",
+                "php": ">=7.1.3"
+            },
+            "require-dev": {
+                "friendsofphp/php-cs-fixer": "^2.15",
+                "phpunit/phpunit": "^7.5",
+                "predis/predis": "^1.1"
+            },
+            "suggest": {
+                "predis/predis": "Allows to use throttle feature"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Yansongda\\Supports\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "yansongda",
+                    "email": "me@yansongda.cn"
+                }
+            ],
+            "description": "common components",
+            "keywords": [
+                "Guzzle",
+                "array",
+                "collection",
+                "config",
+                "http",
+                "support",
+                "throttle"
+            ],
+            "time": "2020-10-14T08:17:18+00:00"
+        }
+    ],
+    "packages-dev": [],
+    "aliases": [],
+    "minimum-stability": "stable",
+    "stability-flags": {
+        "topthink/framework": 20
+    },
+    "prefer-stable": false,
+    "prefer-lowest": false,
+    "platform": {
+        "php": ">=7.1.0",
+        "ext-json": "*",
+        "ext-curl": "*",
+        "ext-pdo": "*",
+        "ext-bcmath": "*"
+    },
+    "platform-dev": []
+}

+ 1 - 1
vendor/autoload.php

@@ -2,6 +2,6 @@
 
 // autoload.php @generated by Composer
 
-require_once __DIR__ . '/composer' . '/autoload_real.php';
+require_once __DIR__ . '/composer/autoload_real.php';
 
 return ComposerAutoloaderInit73f9e72fede2c36621e52f7b610bbb65::getLoader();

+ 39 - 9
vendor/composer/ClassLoader.php

@@ -55,6 +55,7 @@ class ClassLoader
     private $classMap = array();
     private $classMapAuthoritative = false;
     private $missingClasses = array();
+    private $apcuPrefix;
 
     public function getPrefixes()
     {
@@ -272,6 +273,26 @@ class ClassLoader
     }
 
     /**
+     * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
+     *
+     * @param string|null $apcuPrefix
+     */
+    public function setApcuPrefix($apcuPrefix)
+    {
+        $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
+    }
+
+    /**
+     * The APCu prefix in use, or null if APCu caching is not enabled.
+     *
+     * @return string|null
+     */
+    public function getApcuPrefix()
+    {
+        return $this->apcuPrefix;
+    }
+
+    /**
      * Registers this instance as an autoloader.
      *
      * @param bool $prepend Whether to prepend the autoloader or not
@@ -313,11 +334,6 @@ class ClassLoader
      */
     public function findFile($class)
     {
-        // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
-        if ('\\' == $class[0]) {
-            $class = substr($class, 1);
-        }
-
         // class map lookup
         if (isset($this->classMap[$class])) {
             return $this->classMap[$class];
@@ -325,6 +341,12 @@ class ClassLoader
         if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
             return false;
         }
+        if (null !== $this->apcuPrefix) {
+            $file = apcu_fetch($this->apcuPrefix.$class, $hit);
+            if ($hit) {
+                return $file;
+            }
+        }
 
         $file = $this->findFileWithExtension($class, '.php');
 
@@ -333,6 +355,10 @@ class ClassLoader
             $file = $this->findFileWithExtension($class, '.hh');
         }
 
+        if (null !== $this->apcuPrefix) {
+            apcu_add($this->apcuPrefix.$class, $file);
+        }
+
         if (false === $file) {
             // Remember that this class does not exist.
             $this->missingClasses[$class] = true;
@@ -348,10 +374,14 @@ class ClassLoader
 
         $first = $class[0];
         if (isset($this->prefixLengthsPsr4[$first])) {
-            foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
-                if (0 === strpos($class, $prefix)) {
-                    foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
-                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
+            $subPath = $class;
+            while (false !== $lastPos = strrpos($subPath, '\\')) {
+                $subPath = substr($subPath, 0, $lastPos);
+                $search = $subPath . '\\';
+                if (isset($this->prefixDirsPsr4[$search])) {
+                    $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
+                    foreach ($this->prefixDirsPsr4[$search] as $dir) {
+                        if (file_exists($file = $dir . $pathEnd)) {
                             return $file;
                         }
                     }

+ 1 - 1
vendor/composer/LICENSE

@@ -1,5 +1,5 @@
 
-Copyright (c) 2016 Nils Adermann, Jordi Boggiano
+Copyright (c) Nils Adermann, Jordi Boggiano
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal

+ 20 - 20
vendor/composer/autoload_files.php

@@ -7,31 +7,14 @@ $baseDir = dirname($vendorDir);
 
 return array(
     '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
-    '25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
     'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
+    '25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
     'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
     'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
     '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
     'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
     'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
     '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
-    '9b552a3cc426e3287cc811caefa3cf53' => $vendorDir . '/topthink/think-helper/src/helper.php',
-    '3af723442581d6c310bf44543f9f5c60' => $vendorDir . '/markbaker/matrix/classes/src/Functions/adjoint.php',
-    'd803221834c8b57fec95debb5406a797' => $vendorDir . '/markbaker/matrix/classes/src/Functions/antidiagonal.php',
-    '4714cafbd3be4c72c274a25eae9396bb' => $vendorDir . '/markbaker/matrix/classes/src/Functions/cofactors.php',
-    '89719dc7c77436609d1c1c31f0797b8f' => $vendorDir . '/markbaker/matrix/classes/src/Functions/determinant.php',
-    'c28af79ec7730859d83f2d4310b8dd0b' => $vendorDir . '/markbaker/matrix/classes/src/Functions/diagonal.php',
-    'c5d82bf1ac485e445f911e55789ab4e6' => $vendorDir . '/markbaker/matrix/classes/src/Functions/identity.php',
-    '0d2d594de24a247f7a33499e933aa21e' => $vendorDir . '/markbaker/matrix/classes/src/Functions/inverse.php',
-    'f37c25880804a014ef40c8bffbab1b10' => $vendorDir . '/markbaker/matrix/classes/src/Functions/minors.php',
-    'd6e4e42171df0dbea253b3067fefda38' => $vendorDir . '/markbaker/matrix/classes/src/Functions/trace.php',
-    '2c9b19fa954fd3e6fcc7e7a1383caddd' => $vendorDir . '/markbaker/matrix/classes/src/Functions/transpose.php',
-    '0a538fc9b897450ec362480ebbebe94f' => $vendorDir . '/markbaker/matrix/classes/src/Operations/add.php',
-    'f0843f7f4089ec2343c7445544356385' => $vendorDir . '/markbaker/matrix/classes/src/Operations/directsum.php',
-    'ad3e8c29aa16d134661a414265677b61' => $vendorDir . '/markbaker/matrix/classes/src/Operations/subtract.php',
-    '8d37dad4703fab45bfec9dd0bbf3278e' => $vendorDir . '/markbaker/matrix/classes/src/Operations/multiply.php',
-    '4888a6f58c08148ebe17682f9ce9b2a8' => $vendorDir . '/markbaker/matrix/classes/src/Operations/divideby.php',
-    'eef6fa3879d3efa347cd24d5eb348f85' => $vendorDir . '/markbaker/matrix/classes/src/Operations/divideinto.php',
     'abede361264e2ae69ec1eee813a101af' => $vendorDir . '/markbaker/complex/classes/src/functions/abs.php',
     '21a5860fbef5be28db5ddfbc3cca67c4' => $vendorDir . '/markbaker/complex/classes/src/functions/acos.php',
     '1546e3f9d127f2a9bb2d1b6c31c26ef1' => $vendorDir . '/markbaker/complex/classes/src/functions/acosh.php',
@@ -74,9 +57,26 @@ return array(
     '883af48563631547925fa4c3b48ead07' => $vendorDir . '/markbaker/complex/classes/src/operations/multiply.php',
     'f190e3308e6ca23234a2875edc985c03' => $vendorDir . '/markbaker/complex/classes/src/operations/divideby.php',
     'ac9e33ce6841aa5bf5d16d465a2f03a7' => $vendorDir . '/markbaker/complex/classes/src/operations/divideinto.php',
-    'cc56288302d9df745d97c934d6a6e5f0' => $vendorDir . '/topthink/think-queue/src/common.php',
+    '3af723442581d6c310bf44543f9f5c60' => $vendorDir . '/markbaker/matrix/classes/src/Functions/adjoint.php',
+    'd803221834c8b57fec95debb5406a797' => $vendorDir . '/markbaker/matrix/classes/src/Functions/antidiagonal.php',
+    '4714cafbd3be4c72c274a25eae9396bb' => $vendorDir . '/markbaker/matrix/classes/src/Functions/cofactors.php',
+    '89719dc7c77436609d1c1c31f0797b8f' => $vendorDir . '/markbaker/matrix/classes/src/Functions/determinant.php',
+    'c28af79ec7730859d83f2d4310b8dd0b' => $vendorDir . '/markbaker/matrix/classes/src/Functions/diagonal.php',
+    'c5d82bf1ac485e445f911e55789ab4e6' => $vendorDir . '/markbaker/matrix/classes/src/Functions/identity.php',
+    '0d2d594de24a247f7a33499e933aa21e' => $vendorDir . '/markbaker/matrix/classes/src/Functions/inverse.php',
+    'f37c25880804a014ef40c8bffbab1b10' => $vendorDir . '/markbaker/matrix/classes/src/Functions/minors.php',
+    'd6e4e42171df0dbea253b3067fefda38' => $vendorDir . '/markbaker/matrix/classes/src/Functions/trace.php',
+    '2c9b19fa954fd3e6fcc7e7a1383caddd' => $vendorDir . '/markbaker/matrix/classes/src/Functions/transpose.php',
+    '0a538fc9b897450ec362480ebbebe94f' => $vendorDir . '/markbaker/matrix/classes/src/Operations/add.php',
+    'f0843f7f4089ec2343c7445544356385' => $vendorDir . '/markbaker/matrix/classes/src/Operations/directsum.php',
+    'ad3e8c29aa16d134661a414265677b61' => $vendorDir . '/markbaker/matrix/classes/src/Operations/subtract.php',
+    '8d37dad4703fab45bfec9dd0bbf3278e' => $vendorDir . '/markbaker/matrix/classes/src/Operations/multiply.php',
+    '4888a6f58c08148ebe17682f9ce9b2a8' => $vendorDir . '/markbaker/matrix/classes/src/Operations/divideby.php',
+    'eef6fa3879d3efa347cd24d5eb348f85' => $vendorDir . '/markbaker/matrix/classes/src/Operations/divideinto.php',
+    '9b552a3cc426e3287cc811caefa3cf53' => $vendorDir . '/topthink/think-helper/src/helper.php',
+    '488987c28e9b5e95a1ce6b6bcb94606c' => $vendorDir . '/karsonzhang/fastadmin-addons/src/common.php',
     'f0e7e63bbb278a92db02393536748c5f' => $vendorDir . '/overtrue/wechat/src/Kernel/Support/Helpers.php',
     '6747f579ad6817f318cc3a7e7a0abb93' => $vendorDir . '/overtrue/wechat/src/Kernel/Helpers.php',
     '1cfd2761b63b0a29ed23657ea394cb2d' => $vendorDir . '/topthink/think-captcha/src/helper.php',
-    '488987c28e9b5e95a1ce6b6bcb94606c' => $vendorDir . '/karsonzhang/fastadmin-addons/src/common.php',
+    'cc56288302d9df745d97c934d6a6e5f0' => $vendorDir . '/topthink/think-queue/src/common.php',
 );

+ 3 - 1
vendor/composer/autoload_psr4.php

@@ -9,7 +9,9 @@ return array(
     'think\\helper\\' => array($vendorDir . '/topthink/think-helper/src'),
     'think\\composer\\' => array($vendorDir . '/topthink/think-installer/src'),
     'think\\captcha\\' => array($vendorDir . '/topthink/think-captcha/src'),
-    'think\\' => array($vendorDir . '/topthink/think-queue/src', $vendorDir . '/karsonzhang/fastadmin-addons/src', $baseDir . '/thinkphp/library/think'),
+    'think\\' => array($vendorDir . '/karsonzhang/fastadmin-addons/src', $baseDir . '/thinkphp/library/think', $vendorDir . '/topthink/think-queue/src'),
+    'Yansongda\\Supports\\' => array($vendorDir . '/yansongda/supports/src'),
+    'Yansongda\\Pay\\' => array($vendorDir . '/yansongda/pay/src'),
     'Tx\\' => array($vendorDir . '/txthinking/mailer/src'),
     'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
     'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'),

+ 1 - 1
vendor/composer/autoload_real.php

@@ -23,7 +23,7 @@ class ComposerAutoloaderInit73f9e72fede2c36621e52f7b610bbb65
         self::$loader = $loader = new \Composer\Autoload\ClassLoader();
         spl_autoload_unregister(array('ComposerAutoloaderInit73f9e72fede2c36621e52f7b610bbb65', 'loadClassLoader'));
 
-        $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION');
+        $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
         if ($useStaticLoader) {
             require_once __DIR__ . '/autoload_static.php';
 

+ 36 - 23
vendor/composer/autoload_static.php

@@ -8,31 +8,14 @@ class ComposerStaticInit73f9e72fede2c36621e52f7b610bbb65
 {
     public static $files = array (
         '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
-        '25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
         'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
+        '25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
         'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
         'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
         '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
         'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
         'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
         '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
-        '9b552a3cc426e3287cc811caefa3cf53' => __DIR__ . '/..' . '/topthink/think-helper/src/helper.php',
-        '3af723442581d6c310bf44543f9f5c60' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/adjoint.php',
-        'd803221834c8b57fec95debb5406a797' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/antidiagonal.php',
-        '4714cafbd3be4c72c274a25eae9396bb' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/cofactors.php',
-        '89719dc7c77436609d1c1c31f0797b8f' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/determinant.php',
-        'c28af79ec7730859d83f2d4310b8dd0b' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/diagonal.php',
-        'c5d82bf1ac485e445f911e55789ab4e6' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/identity.php',
-        '0d2d594de24a247f7a33499e933aa21e' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/inverse.php',
-        'f37c25880804a014ef40c8bffbab1b10' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/minors.php',
-        'd6e4e42171df0dbea253b3067fefda38' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/trace.php',
-        '2c9b19fa954fd3e6fcc7e7a1383caddd' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/transpose.php',
-        '0a538fc9b897450ec362480ebbebe94f' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operations/add.php',
-        'f0843f7f4089ec2343c7445544356385' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operations/directsum.php',
-        'ad3e8c29aa16d134661a414265677b61' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operations/subtract.php',
-        '8d37dad4703fab45bfec9dd0bbf3278e' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operations/multiply.php',
-        '4888a6f58c08148ebe17682f9ce9b2a8' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operations/divideby.php',
-        'eef6fa3879d3efa347cd24d5eb348f85' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operations/divideinto.php',
         'abede361264e2ae69ec1eee813a101af' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/abs.php',
         '21a5860fbef5be28db5ddfbc3cca67c4' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/acos.php',
         '1546e3f9d127f2a9bb2d1b6c31c26ef1' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/acosh.php',
@@ -75,11 +58,28 @@ class ComposerStaticInit73f9e72fede2c36621e52f7b610bbb65
         '883af48563631547925fa4c3b48ead07' => __DIR__ . '/..' . '/markbaker/complex/classes/src/operations/multiply.php',
         'f190e3308e6ca23234a2875edc985c03' => __DIR__ . '/..' . '/markbaker/complex/classes/src/operations/divideby.php',
         'ac9e33ce6841aa5bf5d16d465a2f03a7' => __DIR__ . '/..' . '/markbaker/complex/classes/src/operations/divideinto.php',
-        'cc56288302d9df745d97c934d6a6e5f0' => __DIR__ . '/..' . '/topthink/think-queue/src/common.php',
+        '3af723442581d6c310bf44543f9f5c60' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/adjoint.php',
+        'd803221834c8b57fec95debb5406a797' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/antidiagonal.php',
+        '4714cafbd3be4c72c274a25eae9396bb' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/cofactors.php',
+        '89719dc7c77436609d1c1c31f0797b8f' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/determinant.php',
+        'c28af79ec7730859d83f2d4310b8dd0b' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/diagonal.php',
+        'c5d82bf1ac485e445f911e55789ab4e6' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/identity.php',
+        '0d2d594de24a247f7a33499e933aa21e' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/inverse.php',
+        'f37c25880804a014ef40c8bffbab1b10' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/minors.php',
+        'd6e4e42171df0dbea253b3067fefda38' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/trace.php',
+        '2c9b19fa954fd3e6fcc7e7a1383caddd' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/transpose.php',
+        '0a538fc9b897450ec362480ebbebe94f' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operations/add.php',
+        'f0843f7f4089ec2343c7445544356385' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operations/directsum.php',
+        'ad3e8c29aa16d134661a414265677b61' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operations/subtract.php',
+        '8d37dad4703fab45bfec9dd0bbf3278e' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operations/multiply.php',
+        '4888a6f58c08148ebe17682f9ce9b2a8' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operations/divideby.php',
+        'eef6fa3879d3efa347cd24d5eb348f85' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operations/divideinto.php',
+        '9b552a3cc426e3287cc811caefa3cf53' => __DIR__ . '/..' . '/topthink/think-helper/src/helper.php',
+        '488987c28e9b5e95a1ce6b6bcb94606c' => __DIR__ . '/..' . '/karsonzhang/fastadmin-addons/src/common.php',
         'f0e7e63bbb278a92db02393536748c5f' => __DIR__ . '/..' . '/overtrue/wechat/src/Kernel/Support/Helpers.php',
         '6747f579ad6817f318cc3a7e7a0abb93' => __DIR__ . '/..' . '/overtrue/wechat/src/Kernel/Helpers.php',
         '1cfd2761b63b0a29ed23657ea394cb2d' => __DIR__ . '/..' . '/topthink/think-captcha/src/helper.php',
-        '488987c28e9b5e95a1ce6b6bcb94606c' => __DIR__ . '/..' . '/karsonzhang/fastadmin-addons/src/common.php',
+        'cc56288302d9df745d97c934d6a6e5f0' => __DIR__ . '/..' . '/topthink/think-queue/src/common.php',
     );
 
     public static $prefixLengthsPsr4 = array (
@@ -90,6 +90,11 @@ class ComposerStaticInit73f9e72fede2c36621e52f7b610bbb65
             'think\\captcha\\' => 14,
             'think\\' => 6,
         ),
+        'Y' => 
+        array (
+            'Yansongda\\Supports\\' => 19,
+            'Yansongda\\Pay\\' => 14,
+        ),
         'T' => 
         array (
             'Tx\\' => 3,
@@ -164,9 +169,17 @@ class ComposerStaticInit73f9e72fede2c36621e52f7b610bbb65
         ),
         'think\\' => 
         array (
-            0 => __DIR__ . '/..' . '/topthink/think-queue/src',
-            1 => __DIR__ . '/..' . '/karsonzhang/fastadmin-addons/src',
-            2 => __DIR__ . '/../..' . '/thinkphp/library/think',
+            0 => __DIR__ . '/..' . '/karsonzhang/fastadmin-addons/src',
+            1 => __DIR__ . '/../..' . '/thinkphp/library/think',
+            2 => __DIR__ . '/..' . '/topthink/think-queue/src',
+        ),
+        'Yansongda\\Supports\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/yansongda/supports/src',
+        ),
+        'Yansongda\\Pay\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/yansongda/pay/src',
         ),
         'Tx\\' => 
         array (

File diff suppressed because it is too large
+ 324 - 379
vendor/composer/installed.json


+ 20 - 0
vendor/yansongda/pay/LICENSE

@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2017 yansongda <me@yansongda.cn>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 310 - 0
vendor/yansongda/pay/README.md

@@ -0,0 +1,310 @@
+<h1 align="center">Pay</h1>
+
+<p align="center">
+<a href="https://scrutinizer-ci.com/g/yansongda/pay/?branch=master"><img src="https://scrutinizer-ci.com/g/yansongda/pay/badges/quality-score.png?b=master" alt="Scrutinizer Code Quality"></a>
+<a href="https://scrutinizer-ci.com/g/yansongda/pay/build-status/master"><img src="https://scrutinizer-ci.com/g/yansongda/pay/badges/build.png?b=master" alt="Build Status"></a>
+<a href="https://packagist.org/packages/yansongda/pay"><img src="https://poser.pugx.org/yansongda/pay/v/stable" alt="Latest Stable Version"></a>
+<a href="https://packagist.org/packages/yansongda/pay"><img src="https://poser.pugx.org/yansongda/pay/downloads" alt="Total Downloads"></a>
+<a href="https://packagist.org/packages/yansongda/pay"><img src="https://poser.pugx.org/yansongda/pay/v/unstable" alt="Latest Unstable Version"></a>
+<a href="https://packagist.org/packages/yansongda/pay"><img src="https://poser.pugx.org/yansongda/pay/license" alt="License"></a>
+</p>
+
+该文档为 v2.x 版本,如果您想找 v1.x 版本文档,请点击[https://github.com/yansongda/pay/tree/v1.x](https://github.com/yansongda/pay/tree/v1.x)
+
+**注意:v1.x 与 v2.x 版本不兼容**
+
+
+开发了多次支付宝与微信支付后,很自然产生一种反感,惰性又来了,想在网上找相关的轮子,可是一直没有找到一款自己觉得逞心如意的,要么使用起来太难理解,要么文件结构太杂乱,只有自己撸起袖子干了。
+
+**!!请先熟悉 支付宝/微信 说明文档!!请具有基本的 debug 能力!!**
+
+欢迎 Star,欢迎 PR!
+
+laravel 扩展包请 [传送至这里](https://github.com/yansongda/laravel-pay)
+yii 扩展包请 [传送至这里](https://github.com/guanguans/yii-pay)
+
+QQ交流群:690027516
+
+## 特点
+- 丰富的事件系统
+- 命名不那么乱七八糟
+- 隐藏开发者不需要关注的细节
+- 根据支付宝、微信最新 API 开发而成
+- 高度抽象的类,免去各种拼json与xml的痛苦
+- 符合 PSR 标准,你可以各种方便的与你的框架集成
+- 文件结构清晰易理解,可以随心所欲添加本项目中没有的支付网关
+- 方法使用更优雅,不必再去研究那些奇怪的的方法名或者类名是做啥用的
+
+
+## 运行环境
+- PHP 7.0+ (v2.8.0 开始 >= 7.1.3)
+- composer
+
+> php5 请使用 v1.x 版本[https://github.com/yansongda/pay/tree/v1.x](https://github.com/yansongda/pay/tree/v1.x)
+
+
+## 支持的支付方法
+### 1、支付宝
+- 电脑支付
+- 手机网站支付
+- APP 支付
+- 刷卡支付
+- 扫码支付
+- 账户转账
+- 小程序支付
+
+|  method   |   描述       |
+| :-------: | :-------:   |
+|  web      | 电脑支付     |
+|  wap      | 手机网站支付 |
+|  app      | APP 支付    |
+|  pos      | 刷卡支付  |
+|  scan     | 扫码支付  |
+|  transfer | 帐户转账  |
+|  mini     | 小程序支付 |
+
+### 2、微信
+- 公众号支付
+- 小程序支付
+- H5 支付
+- 扫码支付
+- 刷卡支付
+- APP 支付
+- 企业付款
+- 普通红包
+- 分裂红包
+
+| method |   描述     |
+| :-----: | :-------: |
+| mp      | 公众号支付  |
+| miniapp | 小程序支付  |
+| wap     | H5 支付    |
+| scan    | 扫码支付    |
+| pos     | 刷卡支付    |
+| app     | APP 支付  |
+| transfer     | 企业付款 |
+| redpack      | 普通红包 |
+| groupRedpack | 分裂红包 |
+
+## 支持的方法
+所有网关均支持以下方法
+
+- find(array/string $order)  
+说明:查找订单接口  
+参数:`$order` 为 `string` 类型时,请传入系统订单号,对应支付宝或微信中的 `out_trade_no`; `array` 类型时,参数请参考支付宝或微信官方文档。  
+返回:查询成功,返回 `Yansongda\Supports\Collection` 实例,可以通过 `$colletion->xxx` 或 `$collection['xxx']` 访问服务器返回的数据。  
+异常:`GatewayException` 或 `InvalidSignException`  
+
+- refund(array $order)  
+说明:退款接口  
+参数:`$order` 数组格式,退款参数。  
+返回:退款成功,返回 `Yansongda\Supports\Collection` 实例,可以通过 `$colletion->xxx` 或 `$collection['xxx']` 访问服务器返回的数据。  
+异常:`GatewayException` 或 `InvalidSignException`
+
+- cancel(array/string $order)  
+说明:取消订单接口  
+参数:`$order` 为 `string` 类型时,请传入系统订单号,对应支付宝或微信中的 `out_trade_no`; `array` 类型时,参数请参考支付宝或微信官方文档。    
+返回:取消成功,返回 `Yansongda\Supports\Collection` 实例,可以通过 `$colletion->xxx` 或 `$collection['xxx']` 访问服务器返回的数据。  
+异常:`GatewayException` 或 `InvalidSignException`
+
+- close(array/string $order)  
+说明:关闭订单接口  
+参数:`$order` 为 `string` 类型时,请传入系统订单号,对应支付宝或微信中的 `out_trade_no`; `array` 类型时,参数请参考支付宝或微信官方文档。  
+返回:关闭成功,返回 `Yansongda\Supports\Collection` 实例,可以通过 `$colletion->xxx` 或 `$collection['xxx']` 访问服务器返回的数据。  
+异常:`GatewayException` 或 `InvalidSignException`  
+
+- verify()  
+说明:验证服务器返回消息是否合法  
+返回:验证成功,返回 `Yansongda\Supports\Collection` 实例,可以通过 `$colletion->xxx` 或 `$collection['xxx']` 访问服务器返回的数据。  
+异常:`GatewayException` 或 `InvalidSignException`  
+
+- PAYMETHOD(array $order)  
+说明:进行支付;具体支付方法名称请参考「支持的支付方法」一栏  
+返回:成功,返回 `Yansongda\Supports\Collection` 实例,可以通过 `$colletion->xxx` 或 `$collection['xxx']` 访问服务器返回的数据或 `Symfony\Component\HttpFoundation\Response` 实例,可通过 `return $response->send()`(laravel 框架中直接 `return $response`) 返回,具体请参考文档。  
+异常:`GatewayException` 或 `InvalidSignException`  
+
+## 安装
+```shell
+composer require yansongda/pay -vvv
+```
+
+## 使用说明
+
+### 支付宝
+```php
+<?php
+
+namespace App\Http\Controllers;
+
+use Yansongda\Pay\Pay;
+use Yansongda\Pay\Log;
+
+class PayController
+{
+    protected $config = [
+        'app_id' => '2016082000295641',
+        'notify_url' => 'http://yansongda.cn/notify.php',
+        'return_url' => 'http://yansongda.cn/return.php',
+        'ali_public_key' => 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuWJKrQ6SWvS6niI+4vEVZiYfjkCfLQfoFI2nCp9ZLDS42QtiL4Ccyx8scgc3nhVwmVRte8f57TFvGhvJD0upT4O5O/lRxmTjechXAorirVdAODpOu0mFfQV9y/T9o9hHnU+VmO5spoVb3umqpq6D/Pt8p25Yk852/w01VTIczrXC4QlrbOEe3sr1E9auoC7rgYjjCO6lZUIDjX/oBmNXZxhRDrYx4Yf5X7y8FRBFvygIE2FgxV4Yw+SL3QAa2m5MLcbusJpxOml9YVQfP8iSurx41PvvXUMo49JG3BDVernaCYXQCoUJv9fJwbnfZd7J5YByC+5KM4sblJTq7bXZWQIDAQAB',
+        // 加密方式: **RSA2**  
+        'private_key' => 'MIIEpAIBAAKCAQEAs6+F2leOgOrvj9jTeDhb5q46GewOjqLBlGSs/bVL4Z3fMr3p+Q1Tux/6uogeVi/eHd84xvQdfpZ87A1SfoWnEGH5z15yorccxSOwWUI+q8gz51IWqjgZxhWKe31BxNZ+prnQpyeMBtE25fXp5nQZ/pftgePyUUvUZRcAUisswntobDQKbwx28VCXw5XB2A+lvYEvxmMv/QexYjwKK4M54j435TuC3UctZbnuynSPpOmCu45ZhEYXd4YMsGMdZE5/077ZU1aU7wx/gk07PiHImEOCDkzqsFo0Buc/knGcdOiUDvm2hn2y1XvwjyFOThsqCsQYi4JmwZdRa8kvOf57nwIDAQABAoIBAQCw5QCqln4VTrTvcW+msB1ReX57nJgsNfDLbV2dG8mLYQemBa9833DqDK6iynTLNq69y88ylose33o2TVtEccGp8Dqluv6yUAED14G6LexS43KtrXPgugAtsXE253ZDGUNwUggnN1i0MW2RcMqHdQ9ORDWvJUCeZj/AEafgPN8AyiLrZeL07jJz/uaRfAuNqkImCVIarKUX3HBCjl9TpuoMjcMhz/MsOmQ0agtCatO1eoH1sqv5Odvxb1i59c8Hvq/mGEXyRuoiDo05SE6IyXYXr84/Nf2xvVNHNQA6kTckj8shSi+HGM4mO1Y4Pbb7XcnxNkT0Inn6oJMSiy56P+CpAoGBAO1O+5FE1ZuVGuLb48cY+0lHCD+nhSBd66B5FrxgPYCkFOQWR7pWyfNDBlmO3SSooQ8TQXA25blrkDxzOAEGX57EPiipXr/hy5e+WNoukpy09rsO1TMsvC+v0FXLvZ+TIAkqfnYBgaT56ku7yZ8aFGMwdCPL7WJYAwUIcZX8wZ3dAoGBAMHWplAqhe4bfkGOEEpfs6VvEQxCqYMYVyR65K0rI1LiDZn6Ij8fdVtwMjGKFSZZTspmsqnbbuCE/VTyDzF4NpAxdm3cBtZACv1Lpu2Om+aTzhK2PI6WTDVTKAJBYegXaahBCqVbSxieR62IWtmOMjggTtAKWZ1P5LQcRwdkaB2rAoGAWnAPT318Kp7YcDx8whOzMGnxqtCc24jvk2iSUZgb2Dqv+3zCOTF6JUsV0Guxu5bISoZ8GdfSFKf5gBAo97sGFeuUBMsHYPkcLehM1FmLZk1Q+ljcx3P1A/ds3kWXLolTXCrlpvNMBSN5NwOKAyhdPK/qkvnUrfX8sJ5XK2H4J8ECgYAGIZ0HIiE0Y+g9eJnpUFelXvsCEUW9YNK4065SD/BBGedmPHRC3OLgbo8X5A9BNEf6vP7fwpIiRfKhcjqqzOuk6fueA/yvYD04v+Da2MzzoS8+hkcqF3T3pta4I4tORRdRfCUzD80zTSZlRc/h286Y2eTETd+By1onnFFe2X01mwKBgQDaxo4PBcLL2OyVT5DoXiIdTCJ8KNZL9+kV1aiBuOWxnRgkDjPngslzNa1bK+klGgJNYDbQqohKNn1HeFX3mYNfCUpuSnD2Yag53Dd/1DLO+NxzwvTu4D6DCUnMMMBVaF42ig31Bs0jI3JQZVqeeFzSET8fkoFopJf3G6UXlrIEAQ==',
+        // 使用公钥证书模式,请配置下面两个参数,同时修改ali_public_key为以.crt结尾的支付宝公钥证书路径,如(./cert/alipayCertPublicKey_RSA2.crt)
+        // 'app_cert_public_key' => './cert/appCertPublicKey.crt', //应用公钥证书路径
+        // 'alipay_root_cert' => './cert/alipayRootCert.crt', //支付宝根证书路径
+        'log' => [ // optional
+            'file' => './logs/alipay.log',
+            'level' => 'info', // 建议生产环境等级调整为 info,开发环境为 debug
+            'type' => 'single', // optional, 可选 daily.
+            'max_file' => 30, // optional, 当 type 为 daily 时有效,默认 30 天
+        ],
+        'http' => [ // optional
+            'timeout' => 5.0,
+            'connect_timeout' => 5.0,
+            // 更多配置项请参考 [Guzzle](https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html)
+        ],
+        'mode' => 'dev', // optional,设置此参数,将进入沙箱模式
+    ];
+
+    public function index()
+    {
+        $order = [
+            'out_trade_no' => time(),
+            'total_amount' => '1',
+            'subject' => 'test subject - 测试',
+        ];
+
+        $alipay = Pay::alipay($this->config)->web($order);
+
+        return $alipay->send();// laravel 框架中请直接 `return $alipay`
+    }
+
+    public function return()
+    {
+        $data = Pay::alipay($this->config)->verify(); // 是的,验签就这么简单!
+
+        // 订单号:$data->out_trade_no
+        // 支付宝交易号:$data->trade_no
+        // 订单总金额:$data->total_amount
+    }
+
+    public function notify()
+    {
+        $alipay = Pay::alipay($this->config);
+    
+        try{
+            $data = $alipay->verify(); // 是的,验签就这么简单!
+
+            // 请自行对 trade_status 进行判断及其它逻辑进行判断,在支付宝的业务通知中,只有交易通知状态为 TRADE_SUCCESS 或 TRADE_FINISHED 时,支付宝才会认定为买家付款成功。
+            // 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号;
+            // 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额);
+            // 3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email);
+            // 4、验证app_id是否为该商户本身。
+            // 5、其它业务逻辑情况
+
+            Log::debug('Alipay notify', $data->all());
+        } catch (\Exception $e) {
+            // $e->getMessage();
+        }
+
+        return $alipay->success()->send();// laravel 框架中请直接 `return $alipay->success()`
+    }
+}
+```
+
+### 微信
+```php
+<?php
+
+namespace App\Http\Controllers;
+
+use Yansongda\Pay\Pay;
+use Yansongda\Pay\Log;
+
+class PayController
+{
+    protected $config = [
+        'appid' => 'wxb3fxxxxxxxxxxx', // APP APPID
+        'app_id' => 'wxb3fxxxxxxxxxxx', // 公众号 APPID
+        'miniapp_id' => 'wxb3fxxxxxxxxxxx', // 小程序 APPID
+        'mch_id' => '14577xxxx',
+        'key' => 'mF2suE9sU6Mk1Cxxxxxxxxxxx',
+        'notify_url' => 'http://yanda.net.cn/notify.php',
+        'cert_client' => './cert/apiclient_cert.pem', // optional,退款等情况时用到
+        'cert_key' => './cert/apiclient_key.pem',// optional,退款等情况时用到
+        'log' => [ // optional
+            'file' => './logs/wechat.log',
+            'level' => 'info', // 建议生产环境等级调整为 info,开发环境为 debug
+            'type' => 'single', // optional, 可选 daily.
+            'max_file' => 30, // optional, 当 type 为 daily 时有效,默认 30 天
+        ],
+        'http' => [ // optional
+            'timeout' => 5.0,
+            'connect_timeout' => 5.0,
+            // 更多配置项请参考 [Guzzle](https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html)
+        ],
+        'mode' => 'dev', // optional, dev/hk;当为 `hk` 时,为香港 gateway。
+    ];
+
+    public function index()
+    {
+        $order = [
+            'out_trade_no' => time(),
+            'total_fee' => '1', // **单位:分**
+            'body' => 'test body - 测试',
+            'openid' => 'onkVf1FjWS5SBIixxxxxxx',
+        ];
+
+        $pay = Pay::wechat($this->config)->mp($order);
+
+        // $pay->appId
+        // $pay->timeStamp
+        // $pay->nonceStr
+        // $pay->package
+        // $pay->signType
+    }
+
+    public function notify()
+    {
+        $pay = Pay::wechat($this->config);
+
+        try{
+            $data = $pay->verify(); // 是的,验签就这么简单!
+
+            Log::debug('Wechat notify', $data->all());
+        } catch (\Exception $e) {
+            // $e->getMessage();
+        }
+        
+        return $pay->success()->send();// laravel 框架中请直接 `return $pay->success()`
+    }
+}
+```
+
+## 事件系统
+[请见详细文档](http://pay.yansongda.cn)
+
+## 详细文档
+[详细说明文档](http://pay.yansongda.cn)
+
+## 错误
+如果在调用相关支付网关 API 时有错误产生,会抛出 `GatewayException`,`InvalidSignException` 错误,可以通过 `$e->getMessage()` 查看,同时,也可通过 `$e->raw` 查看调用 API 后返回的原始数据,该值为数组格式。
+
+### 所有异常
+
+* Yansongda\Pay\Exceptions\InvalidGatewayException ,表示使用了除本 SDK 支持的支付网关。
+* Yansongda\Pay\Exceptions\InvalidSignException ,表示验签失败。
+* Yansongda\Pay\Exceptions\InvalidConfigException ,表示缺少配置参数,如,`ali_public_key`, `private_key` 等。
+* Yansongda\Pay\Exceptions\GatewayException ,表示支付宝/微信服务器返回的数据非正常结果,例如,参数错误,对账单不存在等。
+
+
+## 代码贡献
+由于测试及使用环境的限制,本项目中只开发了「支付宝」和「微信支付」的相关支付网关。
+
+如果您有其它支付网关的需求,或者发现本项目中需要改进的代码,**_欢迎 Fork 并提交 PR!_**
+
+## 赏一杯咖啡吧
+
+![pay](https://pay.yanda.net.cn/images/pay.jpg)
+
+## LICENSE
+MIT

+ 43 - 0
vendor/yansongda/pay/composer.json

@@ -0,0 +1,43 @@
+{
+    "name": "yansongda/pay",
+    "description": "专注 Alipay 和 WeChat 的支付扩展包",
+    "keywords": ["alipay", "wechat", "pay"],
+    "type": "library",
+    "support": {
+        "issues": "https://github.com/yansongda/pay/issues",
+        "source": "https://github.com/yansongda/pay"
+    },
+    "authors": [
+        {
+            "name": "yansongda",
+            "email": "me@yansongda.cn"
+        }
+    ],
+    "require": {
+        "php": ">=7.1.3",
+        "ext-openssl": "*",
+        "ext-simplexml":"*",
+        "ext-libxml": "*",
+        "ext-json": "*",
+        "ext-bcmath": "*",
+        "yansongda/supports": "^2.0",
+        "symfony/http-foundation": "^4.0 || ^5.0.7",
+        "symfony/event-dispatcher": "^4.0 || ^5.0"
+    },
+    "require-dev": {
+        "phpunit/phpunit": "^7.5",
+        "mockery/mockery": "^1.2",
+        "friendsofphp/php-cs-fixer": "^2.15"
+    },
+    "autoload": {
+        "psr-4": {
+            "Yansongda\\Pay\\": "src"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "Yansongda\\Pay\\Tests\\": "tests"
+        }
+    },
+    "license": "MIT"
+}

+ 83 - 0
vendor/yansongda/pay/src/Contracts/GatewayApplicationInterface.php

@@ -0,0 +1,83 @@
+<?php
+
+namespace Yansongda\Pay\Contracts;
+
+use Symfony\Component\HttpFoundation\Response;
+use Yansongda\Supports\Collection;
+
+interface GatewayApplicationInterface
+{
+    /**
+     * To pay.
+     *
+     * @author yansongda <me@yansonga.cn>
+     *
+     * @param string $gateway
+     * @param array  $params
+     *
+     * @return Collection|Response
+     */
+    public function pay($gateway, $params);
+
+    /**
+     * Query an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string|array $order
+     *
+     * @return Collection
+     */
+    public function find($order, string $type);
+
+    /**
+     * Refund an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return Collection
+     */
+    public function refund(array $order);
+
+    /**
+     * Cancel an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string|array $order
+     *
+     * @return Collection
+     */
+    public function cancel($order);
+
+    /**
+     * Close an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string|array $order
+     *
+     * @return Collection
+     */
+    public function close($order);
+
+    /**
+     * Verify a request.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string|array|null $content
+     *
+     * @return Collection
+     */
+    public function verify($content, bool $refund);
+
+    /**
+     * Echo success to server.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return Response
+     */
+    public function success();
+}

+ 20 - 0
vendor/yansongda/pay/src/Contracts/GatewayInterface.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace Yansongda\Pay\Contracts;
+
+use Symfony\Component\HttpFoundation\Response;
+use Yansongda\Supports\Collection;
+
+interface GatewayInterface
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @return Collection|Response
+     */
+    public function pay($endpoint, array $payload);
+}

+ 98 - 0
vendor/yansongda/pay/src/Events.php

@@ -0,0 +1,98 @@
+<?php
+
+namespace Yansongda\Pay;
+
+use Exception;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Contracts\EventDispatcher\Event;
+
+/**
+ * @author yansongda <me@yansongda.cn>
+ *
+ * @method static Event dispatch(Event $event)                                Dispatches an event to all registered listeners
+ * @method static array getListeners($eventName = null)                       Gets the listeners of a specific event or all listeners sorted by descending priority.
+ * @method static int|void getListenerPriority($eventName, $listener)         Gets the listener priority for a specific event.
+ * @method static bool hasListeners($eventName = null)                        Checks whether an event has any registered listeners.
+ * @method static void addListener($eventName, $listener, $priority = 0)      Adds an event listener that listens on the specified events.
+ * @method static removeListener($eventName, $listener)                       Removes an event listener from the specified events.
+ * @method static void addSubscriber(EventSubscriberInterface $subscriber)    Adds an event subscriber.
+ * @method static void removeSubscriber(EventSubscriberInterface $subscriber)
+ */
+class Events
+{
+    /**
+     * dispatcher.
+     *
+     * @var EventDispatcher
+     */
+    protected static $dispatcher;
+
+    /**
+     * Forward call.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $method
+     * @param array  $args
+     *
+     * @throws Exception
+     *
+     * @return mixed
+     */
+    public static function __callStatic($method, $args)
+    {
+        return call_user_func_array([self::getDispatcher(), $method], $args);
+    }
+
+    /**
+     * Forward call.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $method
+     * @param array  $args
+     *
+     * @throws Exception
+     *
+     * @return mixed
+     */
+    public function __call($method, $args)
+    {
+        return call_user_func_array([self::getDispatcher(), $method], $args);
+    }
+
+    /**
+     * setDispatcher.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public static function setDispatcher(EventDispatcher $dispatcher)
+    {
+        self::$dispatcher = $dispatcher;
+    }
+
+    /**
+     * getDispatcher.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public static function getDispatcher(): EventDispatcher
+    {
+        if (self::$dispatcher) {
+            return self::$dispatcher;
+        }
+
+        return self::$dispatcher = self::createDispatcher();
+    }
+
+    /**
+     * createDispatcher.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public static function createDispatcher(): EventDispatcher
+    {
+        return new EventDispatcher();
+    }
+}

+ 31 - 0
vendor/yansongda/pay/src/Events/ApiRequested.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace Yansongda\Pay\Events;
+
+class ApiRequested extends Event
+{
+    /**
+     * Endpoint.
+     *
+     * @var string
+     */
+    public $endpoint;
+
+    /**
+     * Result.
+     *
+     * @var array
+     */
+    public $result;
+
+    /**
+     * Bootstrap.
+     */
+    public function __construct(string $driver, string $gateway, string $endpoint, array $result)
+    {
+        $this->endpoint = $endpoint;
+        $this->result = $result;
+
+        parent::__construct($driver, $gateway);
+    }
+}

+ 31 - 0
vendor/yansongda/pay/src/Events/ApiRequesting.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace Yansongda\Pay\Events;
+
+class ApiRequesting extends Event
+{
+    /**
+     * Endpoint.
+     *
+     * @var string
+     */
+    public $endpoint;
+
+    /**
+     * Payload.
+     *
+     * @var array
+     */
+    public $payload;
+
+    /**
+     * Bootstrap.
+     */
+    public function __construct(string $driver, string $gateway, string $endpoint, array $payload)
+    {
+        $this->endpoint = $endpoint;
+        $this->payload = $payload;
+
+        parent::__construct($driver, $gateway);
+    }
+}

+ 40 - 0
vendor/yansongda/pay/src/Events/Event.php

@@ -0,0 +1,40 @@
+<?php
+
+namespace Yansongda\Pay\Events;
+
+use Symfony\Contracts\EventDispatcher\Event as SymfonyEvent;
+
+class Event extends SymfonyEvent
+{
+    /**
+     * Driver.
+     *
+     * @var string
+     */
+    public $driver;
+
+    /**
+     * Method.
+     *
+     * @var string
+     */
+    public $gateway;
+
+    /**
+     * Extra attributes.
+     *
+     * @var mixed
+     */
+    public $attributes;
+
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function __construct(string $driver, string $gateway)
+    {
+        $this->driver = $driver;
+        $this->gateway = $gateway;
+    }
+}

+ 33 - 0
vendor/yansongda/pay/src/Events/MethodCalled.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace Yansongda\Pay\Events;
+
+class MethodCalled extends Event
+{
+    /**
+     * endpoint.
+     *
+     * @var string
+     */
+    public $endpoint;
+
+    /**
+     * payload.
+     *
+     * @var array
+     */
+    public $payload;
+
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function __construct(string $driver, string $gateway, string $endpoint, array $payload = [])
+    {
+        $this->endpoint = $endpoint;
+        $this->payload = $payload;
+
+        parent::__construct($driver, $gateway);
+    }
+}

+ 31 - 0
vendor/yansongda/pay/src/Events/PayStarted.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace Yansongda\Pay\Events;
+
+class PayStarted extends Event
+{
+    /**
+     * Endpoint.
+     *
+     * @var string
+     */
+    public $endpoint;
+
+    /**
+     * Payload.
+     *
+     * @var array
+     */
+    public $payload;
+
+    /**
+     * Bootstrap.
+     */
+    public function __construct(string $driver, string $gateway, string $endpoint, array $payload)
+    {
+        $this->endpoint = $endpoint;
+        $this->payload = $payload;
+
+        parent::__construct($driver, $gateway);
+    }
+}

+ 23 - 0
vendor/yansongda/pay/src/Events/PayStarting.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace Yansongda\Pay\Events;
+
+class PayStarting extends Event
+{
+    /**
+     * Params.
+     *
+     * @var array
+     */
+    public $params;
+
+    /**
+     * Bootstrap.
+     */
+    public function __construct(string $driver, string $gateway, array $params)
+    {
+        $this->params = $params;
+
+        parent::__construct($driver, $gateway);
+    }
+}

+ 25 - 0
vendor/yansongda/pay/src/Events/RequestReceived.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace Yansongda\Pay\Events;
+
+class RequestReceived extends Event
+{
+    /**
+     * Received data.
+     *
+     * @var array
+     */
+    public $data;
+
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function __construct(string $driver, string $gateway, array $data)
+    {
+        $this->data = $data;
+
+        parent::__construct($driver, $gateway);
+    }
+}

+ 25 - 0
vendor/yansongda/pay/src/Events/SignFailed.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace Yansongda\Pay\Events;
+
+class SignFailed extends Event
+{
+    /**
+     * Received data.
+     *
+     * @var array
+     */
+    public $data;
+
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function __construct(string $driver, string $gateway, array $data)
+    {
+        $this->data = $data;
+
+        parent::__construct($driver, $gateway);
+    }
+}

+ 19 - 0
vendor/yansongda/pay/src/Exceptions/BusinessException.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace Yansongda\Pay\Exceptions;
+
+class BusinessException extends GatewayException
+{
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansonga.cn>
+     *
+     * @param string       $message
+     * @param array|string $raw
+     */
+    public function __construct($message, $raw = [])
+    {
+        parent::__construct('ERROR_BUSINESS: '.$message, $raw, self::ERROR_BUSINESS);
+    }
+}

+ 44 - 0
vendor/yansongda/pay/src/Exceptions/Exception.php

@@ -0,0 +1,44 @@
+<?php
+
+namespace Yansongda\Pay\Exceptions;
+
+class Exception extends \Exception
+{
+    const UNKNOWN_ERROR = 9999;
+
+    const INVALID_GATEWAY = 1;
+
+    const INVALID_CONFIG = 2;
+
+    const INVALID_ARGUMENT = 3;
+
+    const ERROR_GATEWAY = 4;
+
+    const INVALID_SIGN = 5;
+
+    const ERROR_BUSINESS = 6;
+
+    /**
+     * Raw error info.
+     *
+     * @var array
+     */
+    public $raw;
+
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansonga.cn>
+     *
+     * @param string       $message
+     * @param array|string $raw
+     * @param int|string   $code
+     */
+    public function __construct($message = '', $raw = [], $code = self::UNKNOWN_ERROR)
+    {
+        $message = '' === $message ? 'Unknown Error' : $message;
+        $this->raw = is_array($raw) ? $raw : [$raw];
+
+        parent::__construct($message, intval($code));
+    }
+}

+ 20 - 0
vendor/yansongda/pay/src/Exceptions/GatewayException.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace Yansongda\Pay\Exceptions;
+
+class GatewayException extends Exception
+{
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansonga.cn>
+     *
+     * @param string       $message
+     * @param array|string $raw
+     * @param int          $code
+     */
+    public function __construct($message, $raw = [], $code = self::ERROR_GATEWAY)
+    {
+        parent::__construct('ERROR_GATEWAY: '.$message, $raw, $code);
+    }
+}

+ 19 - 0
vendor/yansongda/pay/src/Exceptions/InvalidArgumentException.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace Yansongda\Pay\Exceptions;
+
+class InvalidArgumentException extends Exception
+{
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansonga.cn>
+     *
+     * @param string       $message
+     * @param array|string $raw
+     */
+    public function __construct($message, $raw = [])
+    {
+        parent::__construct('INVALID_ARGUMENT: '.$message, $raw, self::INVALID_ARGUMENT);
+    }
+}

+ 19 - 0
vendor/yansongda/pay/src/Exceptions/InvalidConfigException.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace Yansongda\Pay\Exceptions;
+
+class InvalidConfigException extends Exception
+{
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansonga.cn>
+     *
+     * @param string       $message
+     * @param array|string $raw
+     */
+    public function __construct($message, $raw = [])
+    {
+        parent::__construct('INVALID_CONFIG: '.$message, $raw, self::INVALID_CONFIG);
+    }
+}

+ 19 - 0
vendor/yansongda/pay/src/Exceptions/InvalidGatewayException.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace Yansongda\Pay\Exceptions;
+
+class InvalidGatewayException extends Exception
+{
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansonga.cn>
+     *
+     * @param string       $message
+     * @param array|string $raw
+     */
+    public function __construct($message, $raw = [])
+    {
+        parent::__construct('INVALID_GATEWAY: '.$message, $raw, self::INVALID_GATEWAY);
+    }
+}

+ 19 - 0
vendor/yansongda/pay/src/Exceptions/InvalidSignException.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace Yansongda\Pay\Exceptions;
+
+class InvalidSignException extends Exception
+{
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansonga.cn>
+     *
+     * @param string       $message
+     * @param array|string $raw
+     */
+    public function __construct($message, $raw = [])
+    {
+        parent::__construct('INVALID_SIGN: '.$message, $raw, self::INVALID_SIGN);
+    }
+}

+ 423 - 0
vendor/yansongda/pay/src/Gateways/Alipay.php

@@ -0,0 +1,423 @@
+<?php
+
+namespace Yansongda\Pay\Gateways;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Yansongda\Pay\Contracts\GatewayApplicationInterface;
+use Yansongda\Pay\Contracts\GatewayInterface;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidConfigException;
+use Yansongda\Pay\Exceptions\InvalidGatewayException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Pay\Gateways\Alipay\Support;
+use Yansongda\Supports\Collection;
+use Yansongda\Supports\Config;
+use Yansongda\Supports\Str;
+
+/**
+ * @method Response   app(array $config)      APP 支付
+ * @method Collection pos(array $config)      刷卡支付
+ * @method Collection scan(array $config)     扫码支付
+ * @method Collection transfer(array $config) 帐户转账
+ * @method Response   wap(array $config)      手机网站支付
+ * @method Response   web(array $config)      电脑支付
+ * @method Collection mini(array $config)     小程序支付
+ */
+class Alipay implements GatewayApplicationInterface
+{
+    /**
+     * Const mode_normal.
+     */
+    const MODE_NORMAL = 'normal';
+
+    /**
+     * Const mode_dev.
+     */
+    const MODE_DEV = 'dev';
+
+    /**
+     * Const mode_service.
+     */
+    const MODE_SERVICE = 'service';
+
+    /**
+     * Const url.
+     */
+    const URL = [
+        self::MODE_NORMAL => 'https://openapi.alipay.com/gateway.do?charset=utf-8',
+        self::MODE_SERVICE => 'https://openapi.alipay.com/gateway.do?charset=utf-8',
+        self::MODE_DEV => 'https://openapi.alipaydev.com/gateway.do?charset=utf-8',
+    ];
+
+    /**
+     * Alipay payload.
+     *
+     * @var array
+     */
+    protected $payload;
+
+    /**
+     * Alipay gateway.
+     *
+     * @var string
+     */
+    protected $gateway;
+
+    /**
+     * extends.
+     *
+     * @var array
+     */
+    protected $extends;
+
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws \Exception
+     */
+    public function __construct(Config $config)
+    {
+        $this->gateway = Support::create($config)->getBaseUri();
+        $this->payload = [
+            'app_id' => $config->get('app_id'),
+            'method' => '',
+            'format' => 'JSON',
+            'charset' => 'utf-8',
+            'sign_type' => 'RSA2',
+            'version' => '1.0',
+            'return_url' => $config->get('return_url'),
+            'notify_url' => $config->get('notify_url'),
+            'timestamp' => date('Y-m-d H:i:s'),
+            'sign' => '',
+            'biz_content' => '',
+            'app_auth_token' => $config->get('app_auth_token'),
+        ];
+
+        if ($config->get('app_cert_public_key') && $config->get('alipay_root_cert')) {
+            $this->payload['app_cert_sn'] = Support::getCertSN($config->get('app_cert_public_key'));
+            $this->payload['alipay_root_cert_sn'] = Support::getRootCertSN($config->get('alipay_root_cert'));
+        }
+    }
+
+    /**
+     * Magic pay.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $method
+     * @param array  $params
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidConfigException
+     * @throws InvalidGatewayException
+     * @throws InvalidSignException
+     *
+     * @return Response|Collection
+     */
+    public function __call($method, $params)
+    {
+        if (isset($this->extends[$method])) {
+            return $this->makeExtend($method, ...$params);
+        }
+
+        return $this->pay($method, ...$params);
+    }
+
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $gateway
+     * @param array  $params
+     *
+     * @throws InvalidGatewayException
+     *
+     * @return Response|Collection
+     */
+    public function pay($gateway, $params = [])
+    {
+        Events::dispatch(new Events\PayStarting('Alipay', $gateway, $params));
+
+        $this->payload['return_url'] = $params['return_url'] ?? $this->payload['return_url'];
+        $this->payload['notify_url'] = $params['notify_url'] ?? $this->payload['notify_url'];
+
+        unset($params['return_url'], $params['notify_url']);
+
+        $this->payload['biz_content'] = json_encode($params);
+
+        $gateway = get_class($this).'\\'.Str::studly($gateway).'Gateway';
+
+        if (class_exists($gateway)) {
+            return $this->makePay($gateway);
+        }
+
+        throw new InvalidGatewayException("Pay Gateway [{$gateway}] not exists");
+    }
+
+    /**
+     * Verify sign.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param array|null $data
+     *
+     * @throws InvalidSignException
+     * @throws InvalidConfigException
+     */
+    public function verify($data = null, bool $refund = false): Collection
+    {
+        if (is_null($data)) {
+            $request = Request::createFromGlobals();
+
+            $data = $request->request->count() > 0 ? $request->request->all() : $request->query->all();
+        }
+
+        if (isset($data['fund_bill_list'])) {
+            $data['fund_bill_list'] = htmlspecialchars_decode($data['fund_bill_list']);
+        }
+
+        Events::dispatch(new Events\RequestReceived('Alipay', '', $data));
+
+        if (Support::verifySign($data)) {
+            return new Collection($data);
+        }
+
+        Events::dispatch(new Events\SignFailed('Alipay', '', $data));
+
+        throw new InvalidSignException('Alipay Sign Verify FAILED', $data);
+    }
+
+    /**
+     * Query an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string|array $order
+     *
+     * @throws GatewayException
+     * @throws InvalidConfigException
+     * @throws InvalidSignException
+     */
+    public function find($order, string $type = 'wap'): Collection
+    {
+        $gateway = get_class($this).'\\'.Str::studly($type).'Gateway';
+
+        if (!class_exists($gateway) || !is_callable([new $gateway(), 'find'])) {
+            throw new GatewayException("{$gateway} Done Not Exist Or Done Not Has FIND Method");
+        }
+
+        $config = call_user_func([new $gateway(), 'find'], $order);
+
+        $this->payload['method'] = $config['method'];
+        $this->payload['biz_content'] = $config['biz_content'];
+        $this->payload['sign'] = Support::generateSign($this->payload);
+
+        Events::dispatch(new Events\MethodCalled('Alipay', 'Find', $this->gateway, $this->payload));
+
+        return Support::requestApi($this->payload);
+    }
+
+    /**
+     * Refund an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws GatewayException
+     * @throws InvalidConfigException
+     * @throws InvalidSignException
+     */
+    public function refund(array $order): Collection
+    {
+        $this->payload['method'] = 'alipay.trade.refund';
+        $this->payload['biz_content'] = json_encode($order);
+        $this->payload['sign'] = Support::generateSign($this->payload);
+
+        Events::dispatch(new Events\MethodCalled('Alipay', 'Refund', $this->gateway, $this->payload));
+
+        return Support::requestApi($this->payload);
+    }
+
+    /**
+     * Cancel an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param array|string $order
+     *
+     * @throws GatewayException
+     * @throws InvalidConfigException
+     * @throws InvalidSignException
+     */
+    public function cancel($order): Collection
+    {
+        $this->payload['method'] = 'alipay.trade.cancel';
+        $this->payload['biz_content'] = json_encode(is_array($order) ? $order : ['out_trade_no' => $order]);
+        $this->payload['sign'] = Support::generateSign($this->payload);
+
+        Events::dispatch(new Events\MethodCalled('Alipay', 'Cancel', $this->gateway, $this->payload));
+
+        return Support::requestApi($this->payload);
+    }
+
+    /**
+     * Close an order.
+     *
+     * @param string|array $order
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws GatewayException
+     * @throws InvalidConfigException
+     * @throws InvalidSignException
+     */
+    public function close($order): Collection
+    {
+        $this->payload['method'] = 'alipay.trade.close';
+        $this->payload['biz_content'] = json_encode(is_array($order) ? $order : ['out_trade_no' => $order]);
+        $this->payload['sign'] = Support::generateSign($this->payload);
+
+        Events::dispatch(new Events\MethodCalled('Alipay', 'Close', $this->gateway, $this->payload));
+
+        return Support::requestApi($this->payload);
+    }
+
+    /**
+     * Download bill.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string|array $bill
+     *
+     * @throws GatewayException
+     * @throws InvalidConfigException
+     * @throws InvalidSignException
+     */
+    public function download($bill): string
+    {
+        $this->payload['method'] = 'alipay.data.dataservice.bill.downloadurl.query';
+        $this->payload['biz_content'] = json_encode(is_array($bill) ? $bill : ['bill_type' => 'trade', 'bill_date' => $bill]);
+        $this->payload['sign'] = Support::generateSign($this->payload);
+
+        Events::dispatch(new Events\MethodCalled('Alipay', 'Download', $this->gateway, $this->payload));
+
+        $result = Support::requestApi($this->payload);
+
+        return ($result instanceof Collection) ? $result->get('bill_download_url') : '';
+    }
+
+    /**
+     * Reply success to alipay.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function success(): Response
+    {
+        Events::dispatch(new Events\MethodCalled('Alipay', 'Success', $this->gateway));
+
+        return new Response('success');
+    }
+
+    /**
+     * extend.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws GatewayException
+     * @throws InvalidConfigException
+     * @throws InvalidSignException
+     * @throws InvalidArgumentException
+     */
+    public function extend(string $method, callable $function, bool $now = true, bool $response = false): ?Collection
+    {
+        if (!$now && !method_exists($this, $method)) {
+            $this->extends[$method] = $function;
+
+            return null;
+        }
+
+        $customize = $function($this->payload);
+
+        if (!is_array($customize) && !($customize instanceof Collection)) {
+            throw new InvalidArgumentException('Return Type Must Be Array Or Collection');
+        }
+
+        Events::dispatch(new Events\MethodCalled('Alipay', 'extend', $this->gateway, $customize));
+
+        if (is_array($customize)) {
+            $this->payload = $customize;
+            $this->payload['sign'] = Support::generateSign($this->payload);
+
+            return Support::requestApi($this->payload, $response);
+        }
+
+        return $customize;
+    }
+
+    /**
+     * Make pay gateway.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws InvalidGatewayException
+     *
+     * @return Response|Collection
+     */
+    protected function makePay(string $gateway)
+    {
+        $app = new $gateway();
+
+        if ($app instanceof GatewayInterface) {
+            return $app->pay($this->gateway, array_filter($this->payload, function ($value) {
+                return '' !== $value && !is_null($value);
+            }));
+        }
+
+        throw new InvalidGatewayException("Pay Gateway [{$gateway}] Must Be An Instance Of GatewayInterface");
+    }
+
+    /**
+     * makeExtend.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidConfigException
+     * @throws InvalidSignException
+     */
+    protected function makeExtend(string $method, array ...$params): Collection
+    {
+        $params = count($params) >= 1 ? $params[0] : $params;
+
+        $function = $this->extends[$method];
+
+        $customize = $function($this->payload, $params);
+
+        if (!is_array($customize) && !($customize instanceof Collection)) {
+            throw new InvalidArgumentException('Return Type Must Be Array Or Collection');
+        }
+
+        Events::dispatch(new Events\MethodCalled(
+            'Alipay',
+            'extend - '.$method,
+            $this->gateway,
+            is_array($customize) ? $customize : $customize->toArray()
+        ));
+
+        if (is_array($customize)) {
+            $this->payload = $customize;
+            $this->payload['sign'] = Support::generateSign($this->payload);
+
+            return Support::requestApi($this->payload);
+        }
+
+        return $customize;
+    }
+}

+ 38 - 0
vendor/yansongda/pay/src/Gateways/Alipay/AppGateway.php

@@ -0,0 +1,38 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Alipay;
+
+use Symfony\Component\HttpFoundation\Response;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidConfigException;
+use Yansongda\Pay\Gateways\Alipay;
+
+class AppGateway extends Gateway
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws InvalidConfigException
+     * @throws InvalidArgumentException
+     */
+    public function pay($endpoint, array $payload): Response
+    {
+        $payload['method'] = 'alipay.trade.app.pay';
+
+        $biz_array = json_decode($payload['biz_content'], true);
+        if ((Alipay::MODE_SERVICE === $this->mode) && (!empty(Support::getInstance()->pid))) {
+            $biz_array['extend_params'] = is_array($biz_array['extend_params']) ? array_merge(['sys_service_provider_id' => Support::getInstance()->pid], $biz_array['extend_params']) : ['sys_service_provider_id' => Support::getInstance()->pid];
+        }
+        $payload['biz_content'] = json_encode(array_merge($biz_array, ['product_code' => 'QUICK_MSECURITY_PAY']));
+        $payload['sign'] = Support::generateSign($payload);
+
+        Events::dispatch(new Events\PayStarted('Alipay', 'App', $endpoint, $payload));
+
+        return new Response(http_build_query($payload));
+    }
+}

+ 40 - 0
vendor/yansongda/pay/src/Gateways/Alipay/Gateway.php

@@ -0,0 +1,40 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Alipay;
+
+use Yansongda\Pay\Contracts\GatewayInterface;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Supports\Collection;
+
+abstract class Gateway implements GatewayInterface
+{
+    /**
+     * Mode.
+     *
+     * @var string
+     */
+    protected $mode;
+
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws InvalidArgumentException
+     */
+    public function __construct()
+    {
+        $this->mode = Support::getInstance()->mode;
+    }
+
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @return Collection
+     */
+    abstract public function pay($endpoint, array $payload);
+}

+ 46 - 0
vendor/yansongda/pay/src/Gateways/Alipay/MiniGateway.php

@@ -0,0 +1,46 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Alipay;
+
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidConfigException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Pay\Gateways\Alipay;
+use Yansongda\Supports\Collection;
+
+class MiniGateway extends Gateway
+{
+    /**
+     * Pay an order.
+     *
+     * @author xiaozan <i@xiaozan.me>
+     *
+     * @param string $endpoint
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidConfigException
+     * @throws InvalidSignException
+     *
+     * @see https://docs.alipay.com/mini/introduce/pay
+     */
+    public function pay($endpoint, array $payload): Collection
+    {
+        $biz_array = json_decode($payload['biz_content'], true);
+        if (empty($biz_array['buyer_id'])) {
+            throw new InvalidArgumentException('buyer_id required');
+        }
+        if ((Alipay::MODE_SERVICE === $this->mode) && (!empty(Support::getInstance()->pid))) {
+            $biz_array['extend_params'] = is_array($biz_array['extend_params']) ? array_merge(['sys_service_provider_id' => Support::getInstance()->pid], $biz_array['extend_params']) : ['sys_service_provider_id' => Support::getInstance()->pid];
+        }
+        $payload['biz_content'] = json_encode($biz_array);
+        $payload['method'] = 'alipay.trade.create';
+        $payload['sign'] = Support::generateSign($payload);
+
+        Events::dispatch(new Events\PayStarted('Alipay', 'Mini', $endpoint, $payload));
+
+        return Support::requestApi($payload);
+    }
+}

+ 47 - 0
vendor/yansongda/pay/src/Gateways/Alipay/PosGateway.php

@@ -0,0 +1,47 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Alipay;
+
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidConfigException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Pay\Gateways\Alipay;
+use Yansongda\Supports\Collection;
+
+class PosGateway extends Gateway
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws InvalidArgumentException
+     * @throws GatewayException
+     * @throws InvalidConfigException
+     * @throws InvalidSignException
+     */
+    public function pay($endpoint, array $payload): Collection
+    {
+        $payload['method'] = 'alipay.trade.pay';
+        $biz_array = json_decode($payload['biz_content'], true);
+        if ((Alipay::MODE_SERVICE === $this->mode) && (!empty(Support::getInstance()->pid))) {
+            $biz_array['extend_params'] = is_array($biz_array['extend_params']) ? array_merge(['sys_service_provider_id' => Support::getInstance()->pid], $biz_array['extend_params']) : ['sys_service_provider_id' => Support::getInstance()->pid];
+        }
+        $payload['biz_content'] = json_encode(array_merge(
+            $biz_array,
+            [
+                'product_code' => 'FACE_TO_FACE_PAYMENT',
+                'scene' => 'bar_code',
+            ]
+        ));
+        $payload['sign'] = Support::generateSign($payload);
+
+        Events::dispatch(new Events\PayStarted('Alipay', 'Pos', $endpoint, $payload));
+
+        return Support::requestApi($payload);
+    }
+}

+ 21 - 0
vendor/yansongda/pay/src/Gateways/Alipay/RefundGateway.php

@@ -0,0 +1,21 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Alipay;
+
+class RefundGateway
+{
+    /**
+     * Find.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param $order
+     */
+    public function find($order): array
+    {
+        return [
+            'method' => 'alipay.trade.fastpay.refund.query',
+            'biz_content' => json_encode(is_array($order) ? $order : ['out_trade_no' => $order]),
+        ];
+    }
+}

+ 41 - 0
vendor/yansongda/pay/src/Gateways/Alipay/ScanGateway.php

@@ -0,0 +1,41 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Alipay;
+
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidConfigException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Pay\Gateways\Alipay;
+use Yansongda\Supports\Collection;
+
+class ScanGateway extends Gateway
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidConfigException
+     * @throws InvalidSignException
+     */
+    public function pay($endpoint, array $payload): Collection
+    {
+        $payload['method'] = 'alipay.trade.precreate';
+        $biz_array = json_decode($payload['biz_content'], true);
+        if ((Alipay::MODE_SERVICE === $this->mode) && (!empty(Support::getInstance()->pid))) {
+            $biz_array['extend_params'] = is_array($biz_array['extend_params']) ? array_merge(['sys_service_provider_id' => Support::getInstance()->pid], $biz_array['extend_params']) : ['sys_service_provider_id' => Support::getInstance()->pid];
+        }
+        $payload['biz_content'] = json_encode(array_merge($biz_array, ['product_code' => '']));
+        $payload['sign'] = Support::generateSign($payload);
+
+        Events::dispatch(new Events\PayStarted('Alipay', 'Scan', $endpoint, $payload));
+
+        return Support::requestApi($payload);
+    }
+}

+ 456 - 0
vendor/yansongda/pay/src/Gateways/Alipay/Support.php

@@ -0,0 +1,456 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Alipay;
+
+use Exception;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidConfigException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Pay\Gateways\Alipay;
+use Yansongda\Pay\Log;
+use Yansongda\Supports\Arr;
+use Yansongda\Supports\Collection;
+use Yansongda\Supports\Config;
+use Yansongda\Supports\Str;
+use Yansongda\Supports\Traits\HasHttpRequest;
+
+/**
+ * @author yansongda <me@yansongda.cn>
+ *
+ * @property string app_id alipay app_id
+ * @property string ali_public_key
+ * @property string private_key
+ * @property array http http options
+ * @property string mode current mode
+ * @property array log log options
+ * @property string pid ali pid
+ */
+class Support
+{
+    use HasHttpRequest;
+
+    /**
+     * Alipay gateway.
+     *
+     * @var string
+     */
+    protected $baseUri;
+
+    /**
+     * Config.
+     *
+     * @var Config
+     */
+    protected $config;
+
+    /**
+     * Instance.
+     *
+     * @var Support
+     */
+    private static $instance;
+
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    private function __construct(Config $config)
+    {
+        $this->baseUri = Alipay::URL[$config->get('mode', Alipay::MODE_NORMAL)];
+        $this->config = $config;
+
+        $this->setHttpOptions();
+    }
+
+    /**
+     * __get.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param $key
+     *
+     * @return mixed|Config|null
+     */
+    public function __get($key)
+    {
+        return $this->getConfig($key);
+    }
+
+    /**
+     * create.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return Support
+     */
+    public static function create(Config $config)
+    {
+        if ('cli' === php_sapi_name() || !(self::$instance instanceof self)) {
+            self::$instance = new self($config);
+        }
+
+        return self::$instance;
+    }
+
+    /**
+     * getInstance.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws InvalidArgumentException
+     *
+     * @return Support
+     */
+    public static function getInstance()
+    {
+        if (is_null(self::$instance)) {
+            throw new InvalidArgumentException('You Should [Create] First Before Using');
+        }
+
+        return self::$instance;
+    }
+
+    /**
+     * clear.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function clear()
+    {
+        self::$instance = null;
+    }
+
+    /**
+     * Get Alipay API result.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws GatewayException
+     * @throws InvalidConfigException
+     * @throws InvalidSignException
+     */
+    public static function requestApi(array $data, bool $response = false): Collection
+    {
+        Events::dispatch(new Events\ApiRequesting('Alipay', '', self::$instance->getBaseUri(), $data));
+
+        $data = array_filter($data, function ($value) {
+            return ('' == $value || is_null($value)) ? false : true;
+        });
+
+        $result = json_decode(self::$instance->post('', $data), true);
+
+        Events::dispatch(new Events\ApiRequested('Alipay', '', self::$instance->getBaseUri(), $result));
+
+        return self::processingApiResult($data, $result, $response);
+    }
+
+    /**
+     * Generate sign.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws InvalidConfigException
+     */
+    public static function generateSign(array $params): string
+    {
+        $privateKey = self::$instance->private_key;
+
+        if (is_null($privateKey)) {
+            throw new InvalidConfigException('Missing Alipay Config -- [private_key]');
+        }
+
+        if (Str::endsWith($privateKey, '.pem')) {
+            $privateKey = openssl_pkey_get_private(
+                Str::startsWith($privateKey, 'file://') ? $privateKey : 'file://'.$privateKey
+            );
+        } else {
+            $privateKey = "-----BEGIN RSA PRIVATE KEY-----\n".
+                wordwrap($privateKey, 64, "\n", true).
+                "\n-----END RSA PRIVATE KEY-----";
+        }
+
+        openssl_sign(self::getSignContent($params), $sign, $privateKey, OPENSSL_ALGO_SHA256);
+
+        $sign = base64_encode($sign);
+
+        Log::debug('Alipay Generate Sign', [$params, $sign]);
+
+        if (is_resource($privateKey)) {
+            openssl_free_key($privateKey);
+        }
+
+        return $sign;
+    }
+
+    /**
+     * Verify sign.
+     *
+     * @author yansongda <me@yansonga.cn>
+     *
+     * @param bool        $sync
+     * @param string|null $sign
+     *
+     * @throws InvalidConfigException
+     */
+    public static function verifySign(array $data, $sync = false, $sign = null): bool
+    {
+        $publicKey = self::$instance->ali_public_key;
+
+        if (is_null($publicKey)) {
+            throw new InvalidConfigException('Missing Alipay Config -- [ali_public_key]');
+        }
+
+        if (Str::endsWith($publicKey, '.crt')) {
+            $publicKey = file_get_contents($publicKey);
+        } elseif (Str::endsWith($publicKey, '.pem')) {
+            $publicKey = openssl_pkey_get_public(
+                Str::startsWith($publicKey, 'file://') ? $publicKey : 'file://'.$publicKey
+            );
+        } else {
+            $publicKey = "-----BEGIN PUBLIC KEY-----\n".
+                wordwrap($publicKey, 64, "\n", true).
+                "\n-----END PUBLIC KEY-----";
+        }
+
+        $sign = $sign ?? $data['sign'];
+
+        $toVerify = $sync ? json_encode($data, JSON_UNESCAPED_UNICODE) : self::getSignContent($data, true);
+
+        $isVerify = 1 === openssl_verify($toVerify, base64_decode($sign), $publicKey, OPENSSL_ALGO_SHA256);
+
+        if (is_resource($publicKey)) {
+            openssl_free_key($publicKey);
+        }
+
+        return $isVerify;
+    }
+
+    /**
+     * Get signContent that is to be signed.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param bool $verify
+     */
+    public static function getSignContent(array $data, $verify = false): string
+    {
+        ksort($data);
+
+        $stringToBeSigned = '';
+        foreach ($data as $k => $v) {
+            if ($verify && 'sign' != $k && 'sign_type' != $k) {
+                $stringToBeSigned .= $k.'='.$v.'&';
+            }
+            if (!$verify && '' !== $v && !is_null($v) && 'sign' != $k && '@' != substr($v, 0, 1)) {
+                $stringToBeSigned .= $k.'='.$v.'&';
+            }
+        }
+
+        Log::debug('Alipay Generate Sign Content Before Trim', [$data, $stringToBeSigned]);
+
+        return trim($stringToBeSigned, '&');
+    }
+
+    /**
+     * Convert encoding.
+     *
+     * @author yansongda <me@yansonga.cn>
+     *
+     * @param string|array $data
+     * @param string       $to
+     * @param string       $from
+     */
+    public static function encoding($data, $to = 'utf-8', $from = 'gb2312'): array
+    {
+        return Arr::encoding((array) $data, $to, $from);
+    }
+
+    /**
+     * Get service config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string|null $key
+     * @param mixed|null  $default
+     *
+     * @return mixed|null
+     */
+    public function getConfig($key = null, $default = null)
+    {
+        if (is_null($key)) {
+            return $this->config->all();
+        }
+
+        if ($this->config->has($key)) {
+            return $this->config[$key];
+        }
+
+        return $default;
+    }
+
+    /**
+     * Get Base Uri.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return string
+     */
+    public function getBaseUri()
+    {
+        return $this->baseUri;
+    }
+
+    /**
+     * 生成应用证书SN.
+     *
+     * @author 大冰 https://sbing.vip/archives/2019-new-alipay-php-docking.html
+     *
+     * @param $certPath
+     *
+     * @throws /Exception
+     */
+    public static function getCertSN($certPath): string
+    {
+        if (!is_file($certPath)) {
+            throw new Exception('unknown certPath -- [getCertSN]');
+        }
+        $x509data = file_get_contents($certPath);
+        if (false === $x509data) {
+            throw new Exception('Alipay CertSN Error -- [getCertSN]');
+        }
+        openssl_x509_read($x509data);
+        $certdata = openssl_x509_parse($x509data);
+        if (empty($certdata)) {
+            throw new Exception('Alipay openssl_x509_parse Error -- [getCertSN]');
+        }
+        $issuer_arr = [];
+        foreach ($certdata['issuer'] as $key => $val) {
+            $issuer_arr[] = $key.'='.$val;
+        }
+        $issuer = implode(',', array_reverse($issuer_arr));
+        Log::debug('getCertSN:', [$certPath, $issuer, $certdata['serialNumber']]);
+
+        return md5($issuer.$certdata['serialNumber']);
+    }
+
+    /**
+     * 生成支付宝根证书SN.
+     *
+     * @author 大冰 https://sbing.vip/archives/2019-new-alipay-php-docking.html
+     *
+     * @param $certPath
+     *
+     * @return string
+     *
+     * @throws /Exception
+     */
+    public static function getRootCertSN($certPath)
+    {
+        if (!is_file($certPath)) {
+            throw new Exception('unknown certPath -- [getRootCertSN]');
+        }
+        $x509data = file_get_contents($certPath);
+        if (false === $x509data) {
+            throw new Exception('Alipay CertSN Error -- [getRootCertSN]');
+        }
+        $kCertificateEnd = '-----END CERTIFICATE-----';
+        $certStrList = explode($kCertificateEnd, $x509data);
+        $md5_arr = [];
+        foreach ($certStrList as $one) {
+            if (!empty(trim($one))) {
+                $_x509data = $one.$kCertificateEnd;
+                openssl_x509_read($_x509data);
+                $_certdata = openssl_x509_parse($_x509data);
+                if (in_array($_certdata['signatureTypeSN'], ['RSA-SHA256', 'RSA-SHA1'])) {
+                    $issuer_arr = [];
+                    foreach ($_certdata['issuer'] as $key => $val) {
+                        $issuer_arr[] = $key.'='.$val;
+                    }
+                    $_issuer = implode(',', array_reverse($issuer_arr));
+                    if (0 === strpos($_certdata['serialNumber'], '0x')) {
+                        $serialNumber = self::bchexdec($_certdata['serialNumber']);
+                    } else {
+                        $serialNumber = $_certdata['serialNumber'];
+                    }
+                    $md5_arr[] = md5($_issuer.$serialNumber);
+                    Log::debug('getRootCertSN Sub:', [$certPath, $_issuer, $serialNumber]);
+                }
+            }
+        }
+
+        return implode('_', $md5_arr);
+    }
+
+    /**
+     * processingApiResult.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param $data
+     * @param $result
+     *
+     * @throws GatewayException
+     * @throws InvalidConfigException
+     * @throws InvalidSignException
+     */
+    protected static function processingApiResult($data, $result, $response = false): Collection
+    {
+        if ($response) {
+            return new Collection($result);
+        }
+
+        $method = str_replace('.', '_', $data['method']).'_response';
+
+        if (!isset($result['sign']) || '10000' != $result[$method]['code']) {
+            throw new GatewayException('Get Alipay API Error:'.$result[$method]['msg'].(isset($result[$method]['sub_code']) ? (' - '.$result[$method]['sub_code']) : ''), $result);
+        }
+
+        if (self::verifySign($result[$method], true, $result['sign'])) {
+            return new Collection($result[$method]);
+        }
+
+        Events::dispatch(new Events\SignFailed('Alipay', '', $result));
+
+        throw new InvalidSignException('Alipay Sign Verify FAILED', $result);
+    }
+
+    /**
+     * Set Http options.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function setHttpOptions(): self
+    {
+        if ($this->config->has('http') && is_array($this->config->get('http'))) {
+            $this->config->forget('http.base_uri');
+            $this->httpOptions = $this->config->get('http');
+        }
+
+        return $this;
+    }
+
+    /**
+     * 0x转高精度数字.
+     *
+     * @author 大冰 https://sbing.vip/archives/2019-new-alipay-php-docking.html
+     *
+     * @param $hex
+     *
+     * @return int|string
+     */
+    private static function bchexdec($hex)
+    {
+        $dec = 0;
+        $len = strlen($hex);
+        for ($i = 1; $i <= $len; ++$i) {
+            if (ctype_xdigit($hex[$i - 1])) {
+                $dec = bcadd($dec, bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i))));
+            }
+        }
+
+        return str_replace('.00', '', $dec);
+    }
+}

+ 49 - 0
vendor/yansongda/pay/src/Gateways/Alipay/TransferGateway.php

@@ -0,0 +1,49 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Alipay;
+
+use Yansongda\Pay\Contracts\GatewayInterface;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidConfigException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Supports\Collection;
+
+class TransferGateway implements GatewayInterface
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws GatewayException
+     * @throws InvalidConfigException
+     * @throws InvalidSignException
+     */
+    public function pay($endpoint, array $payload): Collection
+    {
+        $payload['method'] = 'alipay.fund.trans.uni.transfer';
+        $payload['sign'] = Support::generateSign($payload);
+
+        Events::dispatch(new Events\PayStarted('Alipay', 'Transfer', $endpoint, $payload));
+
+        return Support::requestApi($payload);
+    }
+
+    /**
+     * Find.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param $order
+     */
+    public function find($order): array
+    {
+        return [
+            'method' => 'alipay.fund.trans.order.query',
+            'biz_content' => json_encode(is_array($order) ? $order : ['out_biz_no' => $order]),
+        ];
+    }
+}

+ 26 - 0
vendor/yansongda/pay/src/Gateways/Alipay/WapGateway.php

@@ -0,0 +1,26 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Alipay;
+
+class WapGateway extends WebGateway
+{
+    /**
+     * Get method config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function getMethod(): string
+    {
+        return 'alipay.trade.wap.pay';
+    }
+
+    /**
+     * Get productCode config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function getProductCode(): string
+    {
+        return 'QUICK_WAP_WAY';
+    }
+}

+ 104 - 0
vendor/yansongda/pay/src/Gateways/Alipay/WebGateway.php

@@ -0,0 +1,104 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Alipay;
+
+use Symfony\Component\HttpFoundation\RedirectResponse;
+use Symfony\Component\HttpFoundation\Response;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidConfigException;
+use Yansongda\Pay\Gateways\Alipay;
+
+class WebGateway extends Gateway
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws InvalidConfigException
+     * @throws InvalidArgumentException
+     */
+    public function pay($endpoint, array $payload): Response
+    {
+        $biz_array = json_decode($payload['biz_content'], true);
+        $biz_array['product_code'] = $this->getProductCode();
+
+        $method = $biz_array['http_method'] ?? 'POST';
+
+        unset($biz_array['http_method']);
+        if ((Alipay::MODE_SERVICE === $this->mode) && (!empty(Support::getInstance()->pid))) {
+            $biz_array['extend_params'] = is_array($biz_array['extend_params']) ? array_merge(['sys_service_provider_id' => Support::getInstance()->pid], $biz_array['extend_params']) : ['sys_service_provider_id' => Support::getInstance()->pid];
+        }
+        $payload['method'] = $this->getMethod();
+        $payload['biz_content'] = json_encode($biz_array);
+        $payload['sign'] = Support::generateSign($payload);
+
+        Events::dispatch(new Events\PayStarted('Alipay', 'Web/Wap', $endpoint, $payload));
+
+        return $this->buildPayHtml($endpoint, $payload, $method);
+    }
+
+    /**
+     * Find.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param $order
+     */
+    public function find($order): array
+    {
+        return [
+            'method' => 'alipay.trade.query',
+            'biz_content' => json_encode(is_array($order) ? $order : ['out_trade_no' => $order]),
+        ];
+    }
+
+    /**
+     * Build Html response.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     * @param array  $payload
+     * @param string $method
+     */
+    protected function buildPayHtml($endpoint, $payload, $method = 'POST'): Response
+    {
+        if ('GET' === strtoupper($method)) {
+            return new RedirectResponse($endpoint.'&'.http_build_query($payload));
+        }
+
+        $sHtml = "<form id='alipay_submit' name='alipay_submit' action='".$endpoint."' method='".$method."'>";
+        foreach ($payload as $key => $val) {
+            $val = str_replace("'", '&apos;', $val);
+            $sHtml .= "<input type='hidden' name='".$key."' value='".$val."'/>";
+        }
+        $sHtml .= "<input type='submit' value='ok' style='display:none;'></form>";
+        $sHtml .= "<script>document.forms['alipay_submit'].submit();</script>";
+
+        return new Response($sHtml);
+    }
+
+    /**
+     * Get method config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function getMethod(): string
+    {
+        return 'alipay.trade.page.pay';
+    }
+
+    /**
+     * Get productCode config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function getProductCode(): string
+    {
+        return 'FAST_INSTANT_TRADE_PAY';
+    }
+}

+ 365 - 0
vendor/yansongda/pay/src/Gateways/Wechat.php

@@ -0,0 +1,365 @@
+<?php
+
+namespace Yansongda\Pay\Gateways;
+
+use Exception;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Yansongda\Pay\Contracts\GatewayApplicationInterface;
+use Yansongda\Pay\Contracts\GatewayInterface;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidGatewayException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Pay\Gateways\Wechat\Support;
+use Yansongda\Pay\Log;
+use Yansongda\Supports\Collection;
+use Yansongda\Supports\Config;
+use Yansongda\Supports\Str;
+
+/**
+ * @method Response         app(array $config)          APP 支付
+ * @method Collection       groupRedpack(array $config) 分裂红包
+ * @method Collection       miniapp(array $config)      小程序支付
+ * @method Collection       mp(array $config)           公众号支付
+ * @method Collection       pos(array $config)          刷卡支付
+ * @method Collection       redpack(array $config)      普通红包
+ * @method Collection       scan(array $config)         扫码支付
+ * @method Collection       transfer(array $config)     企业付款
+ * @method RedirectResponse wap(array $config)          H5 支付
+ */
+class Wechat implements GatewayApplicationInterface
+{
+    /**
+     * 普通模式.
+     */
+    const MODE_NORMAL = 'normal';
+
+    /**
+     * 沙箱模式.
+     */
+    const MODE_DEV = 'dev';
+
+    /**
+     * 香港钱包 API.
+     */
+    const MODE_HK = 'hk';
+
+    /**
+     * 境外 API.
+     */
+    const MODE_US = 'us';
+
+    /**
+     * 服务商模式.
+     */
+    const MODE_SERVICE = 'service';
+
+    /**
+     * Const url.
+     */
+    const URL = [
+        self::MODE_NORMAL => 'https://api.mch.weixin.qq.com/',
+        self::MODE_DEV => 'https://api.mch.weixin.qq.com/sandboxnew/',
+        self::MODE_HK => 'https://apihk.mch.weixin.qq.com/',
+        self::MODE_SERVICE => 'https://api.mch.weixin.qq.com/',
+        self::MODE_US => 'https://apius.mch.weixin.qq.com/',
+    ];
+
+    /**
+     * Wechat payload.
+     *
+     * @var array
+     */
+    protected $payload;
+
+    /**
+     * Wechat gateway.
+     *
+     * @var string
+     */
+    protected $gateway;
+
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws Exception
+     */
+    public function __construct(Config $config)
+    {
+        $this->gateway = Support::create($config)->getBaseUri();
+        $this->payload = [
+            'appid' => $config->get('app_id', ''),
+            'mch_id' => $config->get('mch_id', ''),
+            'nonce_str' => Str::random(),
+            'notify_url' => $config->get('notify_url', ''),
+            'sign' => '',
+            'trade_type' => '',
+            'spbill_create_ip' => Request::createFromGlobals()->getClientIp(),
+        ];
+
+        if ($config->get('mode', self::MODE_NORMAL) === static::MODE_SERVICE) {
+            $this->payload = array_merge($this->payload, [
+                'sub_mch_id' => $config->get('sub_mch_id'),
+                'sub_appid' => $config->get('sub_app_id', ''),
+            ]);
+        }
+    }
+
+    /**
+     * Magic pay.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $method
+     * @param string $params
+     *
+     * @throws InvalidGatewayException
+     *
+     * @return Response|Collection
+     */
+    public function __call($method, $params)
+    {
+        return self::pay($method, ...$params);
+    }
+
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $gateway
+     * @param array  $params
+     *
+     * @throws InvalidGatewayException
+     *
+     * @return Response|Collection
+     */
+    public function pay($gateway, $params = [])
+    {
+        Events::dispatch(new Events\PayStarting('Wechat', $gateway, $params));
+
+        $this->payload = array_merge($this->payload, $params);
+
+        $gateway = get_class($this).'\\'.Str::studly($gateway).'Gateway';
+
+        if (class_exists($gateway)) {
+            return $this->makePay($gateway);
+        }
+
+        throw new InvalidGatewayException("Pay Gateway [{$gateway}] Not Exists");
+    }
+
+    /**
+     * Verify data.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string|null $content
+     *
+     * @throws InvalidSignException
+     * @throws InvalidArgumentException
+     */
+    public function verify($content = null, bool $refund = false): Collection
+    {
+        $content = $content ?? Request::createFromGlobals()->getContent();
+
+        Events::dispatch(new Events\RequestReceived('Wechat', '', [$content]));
+
+        $data = Support::fromXml($content);
+        if ($refund) {
+            $decrypt_data = Support::decryptRefundContents($data['req_info']);
+            $data = array_merge(Support::fromXml($decrypt_data), $data);
+        }
+
+        Log::debug('Resolved The Received Wechat Request Data', $data);
+
+        if ($refund || Support::generateSign($data) === $data['sign']) {
+            return new Collection($data);
+        }
+
+        Events::dispatch(new Events\SignFailed('Wechat', '', $data));
+
+        throw new InvalidSignException('Wechat Sign Verify FAILED', $data);
+    }
+
+    /**
+     * Query an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string|array $order
+     *
+     * @throws GatewayException
+     * @throws InvalidSignException
+     * @throws InvalidArgumentException
+     */
+    public function find($order, string $type = 'wap'): Collection
+    {
+        if ('wap' != $type) {
+            unset($this->payload['spbill_create_ip']);
+        }
+
+        $gateway = get_class($this).'\\'.Str::studly($type).'Gateway';
+
+        if (!class_exists($gateway) || !is_callable([new $gateway(), 'find'])) {
+            throw new GatewayException("{$gateway} Done Not Exist Or Done Not Has FIND Method");
+        }
+
+        $config = call_user_func([new $gateway(), 'find'], $order);
+
+        $this->payload = Support::filterPayload($this->payload, $config['order']);
+
+        Events::dispatch(new Events\MethodCalled('Wechat', 'Find', $this->gateway, $this->payload));
+
+        return Support::requestApi(
+            $config['endpoint'],
+            $this->payload,
+            $config['cert']
+        );
+    }
+
+    /**
+     * Refund an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws GatewayException
+     * @throws InvalidSignException
+     * @throws InvalidArgumentException
+     */
+    public function refund(array $order): Collection
+    {
+        $this->payload = Support::filterPayload($this->payload, $order, true);
+
+        Events::dispatch(new Events\MethodCalled('Wechat', 'Refund', $this->gateway, $this->payload));
+
+        return Support::requestApi(
+            'secapi/pay/refund',
+            $this->payload,
+            true
+        );
+    }
+
+    /**
+     * Cancel an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param array $order
+     *
+     * @throws GatewayException
+     * @throws InvalidSignException
+     * @throws InvalidArgumentException
+     */
+    public function cancel($order): Collection
+    {
+        unset($this->payload['spbill_create_ip']);
+
+        $this->payload = Support::filterPayload($this->payload, $order);
+
+        Events::dispatch(new Events\MethodCalled('Wechat', 'Cancel', $this->gateway, $this->payload));
+
+        return Support::requestApi(
+            'secapi/pay/reverse',
+            $this->payload,
+            true
+        );
+    }
+
+    /**
+     * Close an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string|array $order
+     *
+     * @throws GatewayException
+     * @throws InvalidSignException
+     * @throws InvalidArgumentException
+     */
+    public function close($order): Collection
+    {
+        unset($this->payload['spbill_create_ip']);
+
+        $this->payload = Support::filterPayload($this->payload, $order);
+
+        Events::dispatch(new Events\MethodCalled('Wechat', 'Close', $this->gateway, $this->payload));
+
+        return Support::requestApi('pay/closeorder', $this->payload);
+    }
+
+    /**
+     * Echo success to server.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws InvalidArgumentException
+     */
+    public function success(): Response
+    {
+        Events::dispatch(new Events\MethodCalled('Wechat', 'Success', $this->gateway));
+
+        return new Response(
+            Support::toXml(['return_code' => 'SUCCESS', 'return_msg' => 'OK']),
+            200,
+            ['Content-Type' => 'application/xml']
+        );
+    }
+
+    /**
+     * Download the bill.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     */
+    public function download(array $params): string
+    {
+        unset($this->payload['spbill_create_ip']);
+
+        $this->payload = Support::filterPayload($this->payload, $params, true);
+
+        Events::dispatch(new Events\MethodCalled('Wechat', 'Download', $this->gateway, $this->payload));
+
+        $result = Support::getInstance()->post(
+            'pay/downloadbill',
+            Support::getInstance()->toXml($this->payload)
+        );
+
+        if (is_array($result)) {
+            throw new GatewayException('Get Wechat API Error: '.$result['return_msg'], $result);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Make pay gateway.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $gateway
+     *
+     * @throws InvalidGatewayException
+     *
+     * @return Response|Collection
+     */
+    protected function makePay($gateway)
+    {
+        $app = new $gateway();
+
+        if ($app instanceof GatewayInterface) {
+            return $app->pay($this->gateway, array_filter($this->payload, function ($value) {
+                return '' !== $value && !is_null($value);
+            }));
+        }
+
+        throw new InvalidGatewayException("Pay Gateway [{$gateway}] Must Be An Instance Of GatewayInterface");
+    }
+}

+ 62 - 0
vendor/yansongda/pay/src/Gateways/Wechat/AppGateway.php

@@ -0,0 +1,62 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Wechat;
+
+use Exception;
+use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\HttpFoundation\Response;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Pay\Gateways\Wechat;
+use Yansongda\Supports\Str;
+
+class AppGateway extends Gateway
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     * @throws Exception
+     */
+    public function pay($endpoint, array $payload): Response
+    {
+        $payload['appid'] = Support::getInstance()->appid;
+        $payload['trade_type'] = $this->getTradeType();
+
+        if (Wechat::MODE_SERVICE === $this->mode) {
+            $payload['sub_appid'] = Support::getInstance()->sub_appid;
+        }
+
+        $pay_request = [
+            'appid' => Wechat::MODE_SERVICE === $this->mode ? $payload['sub_appid'] : $payload['appid'],
+            'partnerid' => Wechat::MODE_SERVICE === $this->mode ? $payload['sub_mch_id'] : $payload['mch_id'],
+            'prepayid' => $this->preOrder($payload)->get('prepay_id'),
+            'timestamp' => strval(time()),
+            'noncestr' => Str::random(),
+            'package' => 'Sign=WXPay',
+        ];
+        $pay_request['sign'] = Support::generateSign($pay_request);
+
+        Events::dispatch(new Events\PayStarted('Wechat', 'App', $endpoint, $pay_request));
+
+        return new JsonResponse($pay_request);
+    }
+
+    /**
+     * Get trade type config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function getTradeType(): string
+    {
+        return 'APP';
+    }
+}

+ 88 - 0
vendor/yansongda/pay/src/Gateways/Wechat/Gateway.php

@@ -0,0 +1,88 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Wechat;
+
+use Yansongda\Pay\Contracts\GatewayInterface;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Supports\Collection;
+
+abstract class Gateway implements GatewayInterface
+{
+    /**
+     * Mode.
+     *
+     * @var string
+     */
+    protected $mode;
+
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws InvalidArgumentException
+     */
+    public function __construct()
+    {
+        $this->mode = Support::getInstance()->mode;
+    }
+
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @return Collection
+     */
+    abstract public function pay($endpoint, array $payload);
+
+    /**
+     * Find.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string|array $order
+     */
+    public function find($order): array
+    {
+        return [
+            'endpoint' => 'pay/orderquery',
+            'order' => is_array($order) ? $order : ['out_trade_no' => $order],
+            'cert' => false,
+        ];
+    }
+
+    /**
+     * Get trade type config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return string
+     */
+    abstract protected function getTradeType();
+
+    /**
+     * Schedule an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param array $payload
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     */
+    protected function preOrder($payload): Collection
+    {
+        $payload['sign'] = Support::generateSign($payload);
+
+        Events::dispatch(new Events\MethodCalled('Wechat', 'PreOrder', '', $payload));
+
+        return Support::requestApi('pay/unifiedorder', $payload);
+    }
+}

+ 73 - 0
vendor/yansongda/pay/src/Gateways/Wechat/GroupRedpackGateway.php

@@ -0,0 +1,73 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Wechat;
+
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Pay\Gateways\Wechat;
+use Yansongda\Supports\Collection;
+
+class GroupRedpackGateway extends Gateway
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     */
+    public function pay($endpoint, array $payload): Collection
+    {
+        $payload['wxappid'] = $payload['appid'];
+        $payload['amt_type'] = 'ALL_RAND';
+
+        if (Wechat::MODE_SERVICE === $this->mode) {
+            $payload['msgappid'] = $payload['appid'];
+        }
+
+        unset($payload['appid'], $payload['trade_type'],
+              $payload['notify_url'], $payload['spbill_create_ip']);
+
+        $payload['sign'] = Support::generateSign($payload);
+
+        Events::dispatch(new Events\PayStarted('Wechat', 'Group Redpack', $endpoint, $payload));
+
+        return Support::requestApi(
+            'mmpaymkttransfers/sendgroupredpack',
+            $payload,
+            true
+        );
+    }
+
+    /**
+     * Find.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param $billno
+     */
+    public function find($billno): array
+    {
+        return [
+            'endpoint' => 'mmpaymkttransfers/gethbinfo',
+            'order' => ['mch_billno' => $billno, 'bill_type' => 'MCHT'],
+            'cert' => true,
+        ];
+    }
+
+    /**
+     * Get trade type config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function getTradeType(): string
+    {
+        return '';
+    }
+}

+ 35 - 0
vendor/yansongda/pay/src/Gateways/Wechat/MiniappGateway.php

@@ -0,0 +1,35 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Wechat;
+
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Pay\Gateways\Wechat;
+use Yansongda\Supports\Collection;
+
+class MiniappGateway extends MpGateway
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     */
+    public function pay($endpoint, array $payload): Collection
+    {
+        $payload['appid'] = Support::getInstance()->miniapp_id;
+
+        if (Wechat::MODE_SERVICE === $this->mode) {
+            $payload['sub_appid'] = Support::getInstance()->sub_miniapp_id;
+            $this->payRequestUseSubAppId = true;
+        }
+
+        return parent::pay($endpoint, $payload);
+    }
+}

+ 59 - 0
vendor/yansongda/pay/src/Gateways/Wechat/MpGateway.php

@@ -0,0 +1,59 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Wechat;
+
+use Exception;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Supports\Collection;
+use Yansongda\Supports\Str;
+
+class MpGateway extends Gateway
+{
+    /**
+     * @var bool
+     */
+    protected $payRequestUseSubAppId = false;
+
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     * @throws Exception
+     */
+    public function pay($endpoint, array $payload): Collection
+    {
+        $payload['trade_type'] = $this->getTradeType();
+
+        $pay_request = [
+            'appId' => !$this->payRequestUseSubAppId ? $payload['appid'] : $payload['sub_appid'],
+            'timeStamp' => strval(time()),
+            'nonceStr' => Str::random(),
+            'package' => 'prepay_id='.$this->preOrder($payload)->get('prepay_id'),
+            'signType' => 'MD5',
+        ];
+        $pay_request['paySign'] = Support::generateSign($pay_request);
+
+        Events::dispatch(new Events\PayStarted('Wechat', 'JSAPI', $endpoint, $pay_request));
+
+        return new Collection($pay_request);
+    }
+
+    /**
+     * Get trade type config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function getTradeType(): string
+    {
+        return 'JSAPI';
+    }
+}

+ 44 - 0
vendor/yansongda/pay/src/Gateways/Wechat/PosGateway.php

@@ -0,0 +1,44 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Wechat;
+
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Supports\Collection;
+
+class PosGateway extends Gateway
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     */
+    public function pay($endpoint, array $payload): Collection
+    {
+        unset($payload['trade_type'], $payload['notify_url']);
+
+        $payload['sign'] = Support::generateSign($payload);
+
+        Events::dispatch(new Events\PayStarted('Wechat', 'Pos', $endpoint, $payload));
+
+        return Support::requestApi('pay/micropay', $payload);
+    }
+
+    /**
+     * Get trade type config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function getTradeType(): string
+    {
+        return 'MICROPAY';
+    }
+}

+ 77 - 0
vendor/yansongda/pay/src/Gateways/Wechat/RedpackGateway.php

@@ -0,0 +1,77 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Wechat;
+
+use Symfony\Component\HttpFoundation\Request;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Pay\Gateways\Wechat;
+use Yansongda\Supports\Collection;
+
+class RedpackGateway extends Gateway
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     */
+    public function pay($endpoint, array $payload): Collection
+    {
+        $payload['wxappid'] = $payload['appid'];
+
+        if ('cli' !== php_sapi_name()) {
+            $payload['client_ip'] = Request::createFromGlobals()->server->get('SERVER_ADDR');
+        }
+
+        if (Wechat::MODE_SERVICE === $this->mode) {
+            $payload['msgappid'] = $payload['appid'];
+        }
+
+        unset($payload['appid'], $payload['trade_type'],
+              $payload['notify_url'], $payload['spbill_create_ip']);
+
+        $payload['sign'] = Support::generateSign($payload);
+
+        Events::dispatch(new Events\PayStarted('Wechat', 'Redpack', $endpoint, $payload));
+
+        return Support::requestApi(
+            'mmpaymkttransfers/sendredpack',
+            $payload,
+            true
+        );
+    }
+
+    /**
+     * Find.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param $billno
+     */
+    public function find($billno): array
+    {
+        return [
+            'endpoint' => 'mmpaymkttransfers/gethbinfo',
+            'order' => ['mch_billno' => $billno, 'bill_type' => 'MCHT'],
+            'cert' => true,
+        ];
+    }
+
+    /**
+     * Get trade type config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function getTradeType(): string
+    {
+        return '';
+    }
+}

+ 50 - 0
vendor/yansongda/pay/src/Gateways/Wechat/RefundGateway.php

@@ -0,0 +1,50 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Wechat;
+
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+
+class RefundGateway extends Gateway
+{
+    /**
+     * Find.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param $order
+     */
+    public function find($order): array
+    {
+        return [
+            'endpoint' => 'pay/refundquery',
+            'order' => is_array($order) ? $order : ['out_trade_no' => $order],
+            'cert' => false,
+        ];
+    }
+
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws InvalidArgumentException
+     */
+    public function pay($endpoint, array $payload)
+    {
+        throw new InvalidArgumentException('Not Support Refund In Pay');
+    }
+
+    /**
+     * Get trade type config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws InvalidArgumentException
+     */
+    protected function getTradeType()
+    {
+        throw new InvalidArgumentException('Not Support Refund In Pay');
+    }
+}

+ 44 - 0
vendor/yansongda/pay/src/Gateways/Wechat/ScanGateway.php

@@ -0,0 +1,44 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Wechat;
+
+use Symfony\Component\HttpFoundation\Request;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Supports\Collection;
+
+class ScanGateway extends Gateway
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     */
+    public function pay($endpoint, array $payload): Collection
+    {
+        $payload['spbill_create_ip'] = Request::createFromGlobals()->server->get('SERVER_ADDR');
+        $payload['trade_type'] = $this->getTradeType();
+
+        Events::dispatch(new Events\PayStarted('Wechat', 'Scan', $endpoint, $payload));
+
+        return $this->preOrder($payload);
+    }
+
+    /**
+     * Get trade type config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function getTradeType(): string
+    {
+        return 'NATIVE';
+    }
+}

+ 451 - 0
vendor/yansongda/pay/src/Gateways/Wechat/Support.php

@@ -0,0 +1,451 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Wechat;
+
+use Exception;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\BusinessException;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Pay\Gateways\Wechat;
+use Yansongda\Pay\Log;
+use Yansongda\Supports\Collection;
+use Yansongda\Supports\Config;
+use Yansongda\Supports\Str;
+use Yansongda\Supports\Traits\HasHttpRequest;
+
+/**
+ * @author yansongda <me@yansongda.cn>
+ *
+ * @property string appid
+ * @property string app_id
+ * @property string miniapp_id
+ * @property string sub_appid
+ * @property string sub_app_id
+ * @property string sub_miniapp_id
+ * @property string mch_id
+ * @property string sub_mch_id
+ * @property string key
+ * @property string return_url
+ * @property string cert_client
+ * @property string cert_key
+ * @property array log
+ * @property array http
+ * @property string mode
+ */
+class Support
+{
+    use HasHttpRequest;
+
+    /**
+     * Wechat gateway.
+     *
+     * @var string
+     */
+    protected $baseUri;
+
+    /**
+     * Config.
+     *
+     * @var Config
+     */
+    protected $config;
+
+    /**
+     * Instance.
+     *
+     * @var Support
+     */
+    private static $instance;
+
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    private function __construct(Config $config)
+    {
+        $this->baseUri = Wechat::URL[$config->get('mode', Wechat::MODE_NORMAL)];
+        $this->config = $config;
+
+        $this->setHttpOptions();
+    }
+
+    /**
+     * __get.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param $key
+     *
+     * @return mixed|Config|null
+     */
+    public function __get($key)
+    {
+        return $this->getConfig($key);
+    }
+
+    /**
+     * create.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     *
+     * @return Support
+     */
+    public static function create(Config $config)
+    {
+        if ('cli' === php_sapi_name() || !(self::$instance instanceof self)) {
+            self::$instance = new self($config);
+
+            self::setDevKey();
+        }
+
+        return self::$instance;
+    }
+
+    /**
+     * getInstance.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws InvalidArgumentException
+     *
+     * @return Support
+     */
+    public static function getInstance()
+    {
+        if (is_null(self::$instance)) {
+            throw new InvalidArgumentException('You Should [Create] First Before Using');
+        }
+
+        return self::$instance;
+    }
+
+    /**
+     * clear.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public static function clear()
+    {
+        self::$instance = null;
+    }
+
+    /**
+     * Request wechat api.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     * @param array  $data
+     * @param bool   $cert
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     */
+    public static function requestApi($endpoint, $data, $cert = false): Collection
+    {
+        Events::dispatch(new Events\ApiRequesting('Wechat', '', self::$instance->getBaseUri().$endpoint, $data));
+
+        $result = self::$instance->post(
+            $endpoint,
+            self::toXml($data),
+            $cert ? [
+                'cert' => self::$instance->cert_client,
+                'ssl_key' => self::$instance->cert_key,
+            ] : []
+        );
+        $result = is_array($result) ? $result : self::fromXml($result);
+
+        Events::dispatch(new Events\ApiRequested('Wechat', '', self::$instance->getBaseUri().$endpoint, $result));
+
+        return self::processingApiResult($endpoint, $result);
+    }
+
+    /**
+     * Filter payload.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param array        $payload
+     * @param array|string $params
+     * @param bool         $preserve_notify_url
+     *
+     * @throws InvalidArgumentException
+     */
+    public static function filterPayload($payload, $params, $preserve_notify_url = false): array
+    {
+        $type = self::getTypeName($params['type'] ?? '');
+
+        $payload = array_merge(
+            $payload,
+            is_array($params) ? $params : ['out_trade_no' => $params]
+        );
+        $payload['appid'] = self::$instance->getConfig($type, '');
+
+        if (Wechat::MODE_SERVICE === self::$instance->getConfig('mode', Wechat::MODE_NORMAL)) {
+            $payload['sub_appid'] = self::$instance->getConfig('sub_'.$type, '');
+        }
+
+        unset($payload['trade_type'], $payload['type']);
+        if (!$preserve_notify_url) {
+            unset($payload['notify_url']);
+        }
+
+        $payload['sign'] = self::generateSign($payload);
+
+        return $payload;
+    }
+
+    /**
+     * Generate wechat sign.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param array $data
+     *
+     * @throws InvalidArgumentException
+     */
+    public static function generateSign($data): string
+    {
+        $key = self::$instance->key;
+
+        if (is_null($key)) {
+            throw new InvalidArgumentException('Missing Wechat Config -- [key]');
+        }
+
+        ksort($data);
+
+        $string = md5(self::getSignContent($data).'&key='.$key);
+
+        Log::debug('Wechat Generate Sign Before UPPER', [$data, $string]);
+
+        return strtoupper($string);
+    }
+
+    /**
+     * Generate sign content.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param array $data
+     */
+    public static function getSignContent($data): string
+    {
+        $buff = '';
+
+        foreach ($data as $k => $v) {
+            $buff .= ('sign' != $k && '' != $v && !is_array($v)) ? $k.'='.$v.'&' : '';
+        }
+
+        Log::debug('Wechat Generate Sign Content Before Trim', [$data, $buff]);
+
+        return trim($buff, '&');
+    }
+
+    /**
+     * Decrypt refund contents.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $contents
+     */
+    public static function decryptRefundContents($contents): string
+    {
+        return openssl_decrypt(
+            base64_decode($contents),
+            'AES-256-ECB',
+            md5(self::$instance->key),
+            OPENSSL_RAW_DATA
+        );
+    }
+
+    /**
+     * Convert array to xml.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param array $data
+     *
+     * @throws InvalidArgumentException
+     */
+    public static function toXml($data): string
+    {
+        if (!is_array($data) || count($data) <= 0) {
+            throw new InvalidArgumentException('Convert To Xml Error! Invalid Array!');
+        }
+
+        $xml = '<xml>';
+        foreach ($data as $key => $val) {
+            $xml .= is_numeric($val) ? '<'.$key.'>'.$val.'</'.$key.'>' :
+                                       '<'.$key.'><![CDATA['.$val.']]></'.$key.'>';
+        }
+        $xml .= '</xml>';
+
+        return $xml;
+    }
+
+    /**
+     * Convert xml to array.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $xml
+     *
+     * @throws InvalidArgumentException
+     */
+    public static function fromXml($xml): array
+    {
+        if (!$xml) {
+            throw new InvalidArgumentException('Convert To Array Error! Invalid Xml!');
+        }
+
+        if (\PHP_VERSION_ID < 80000) {
+            libxml_disable_entity_loader(true);
+        }
+
+        return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true);
+    }
+
+    /**
+     * Get service config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string|null $key
+     * @param mixed|null  $default
+     *
+     * @return mixed|null
+     */
+    public function getConfig($key = null, $default = null)
+    {
+        if (is_null($key)) {
+            return $this->config->all();
+        }
+
+        if ($this->config->has($key)) {
+            return $this->config[$key];
+        }
+
+        return $default;
+    }
+
+    /**
+     * Get app id according to param type.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $type
+     */
+    public static function getTypeName($type = ''): string
+    {
+        switch ($type) {
+            case '':
+                $type = 'app_id';
+                break;
+            case 'app':
+                $type = 'appid';
+                break;
+            default:
+                $type = $type.'_id';
+        }
+
+        return $type;
+    }
+
+    /**
+     * Get Base Uri.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return string
+     */
+    public function getBaseUri()
+    {
+        return $this->baseUri;
+    }
+
+    /**
+     * processingApiResult.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param $endpoint
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     *
+     * @return Collection
+     */
+    protected static function processingApiResult($endpoint, array $result)
+    {
+        if (!isset($result['return_code']) || 'SUCCESS' != $result['return_code']) {
+            throw new GatewayException('Get Wechat API Error:'.($result['return_msg'] ?? $result['retmsg'] ?? ''), $result);
+        }
+
+        if (isset($result['result_code']) && 'SUCCESS' != $result['result_code']) {
+            throw new BusinessException('Wechat Business Error: '.$result['err_code'].' - '.$result['err_code_des'], $result);
+        }
+
+        if ('pay/getsignkey' === $endpoint ||
+            false !== strpos($endpoint, 'mmpaymkttransfers') ||
+            self::generateSign($result) === $result['sign']) {
+            return new Collection($result);
+        }
+
+        Events::dispatch(new Events\SignFailed('Wechat', '', $result));
+
+        throw new InvalidSignException('Wechat Sign Verify FAILED', $result);
+    }
+
+    /**
+     * setDevKey.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     * @throws Exception
+     *
+     * @return Support
+     */
+    private static function setDevKey()
+    {
+        if (Wechat::MODE_DEV == self::$instance->mode) {
+            $data = [
+                'mch_id' => self::$instance->mch_id,
+                'nonce_str' => Str::random(),
+            ];
+            $data['sign'] = self::generateSign($data);
+
+            $result = self::requestApi('pay/getsignkey', $data);
+
+            self::$instance->config->set('key', $result['sandbox_signkey']);
+        }
+
+        return self::$instance;
+    }
+
+    /**
+     * Set Http options.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    private function setHttpOptions(): self
+    {
+        if ($this->config->has('http') && is_array($this->config->get('http'))) {
+            $this->config->forget('http.base_uri');
+            $this->httpOptions = $this->config->get('http');
+        }
+
+        return $this;
+    }
+}

+ 80 - 0
vendor/yansongda/pay/src/Gateways/Wechat/TransferGateway.php

@@ -0,0 +1,80 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Wechat;
+
+use Symfony\Component\HttpFoundation\Request;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+use Yansongda\Pay\Gateways\Wechat;
+use Yansongda\Supports\Collection;
+
+class TransferGateway extends Gateway
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     */
+    public function pay($endpoint, array $payload): Collection
+    {
+        if (Wechat::MODE_SERVICE === $this->mode) {
+            unset($payload['sub_mch_id'], $payload['sub_appid']);
+        }
+
+        $type = Support::getTypeName($payload['type'] ?? '');
+
+        $payload['mch_appid'] = Support::getInstance()->getConfig($type, '');
+        $payload['mchid'] = $payload['mch_id'];
+
+        if ('cli' !== php_sapi_name() && !isset($payload['spbill_create_ip'])) {
+            $payload['spbill_create_ip'] = Request::createFromGlobals()->server->get('SERVER_ADDR');
+        }
+
+        unset($payload['appid'], $payload['mch_id'], $payload['trade_type'],
+            $payload['notify_url'], $payload['type']);
+
+        $payload['sign'] = Support::generateSign($payload);
+
+        Events::dispatch(new Events\PayStarted('Wechat', 'Transfer', $endpoint, $payload));
+
+        return Support::requestApi(
+            'mmpaymkttransfers/promotion/transfers',
+            $payload,
+            true
+        );
+    }
+
+    /**
+     * Find.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param $order
+     */
+    public function find($order): array
+    {
+        return [
+            'endpoint' => 'mmpaymkttransfers/gettransferinfo',
+            'order' => is_array($order) ? $order : ['partner_trade_no' => $order],
+            'cert' => true,
+        ];
+    }
+
+    /**
+     * Get trade type config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function getTradeType(): string
+    {
+        return '';
+    }
+}

+ 47 - 0
vendor/yansongda/pay/src/Gateways/Wechat/WapGateway.php

@@ -0,0 +1,47 @@
+<?php
+
+namespace Yansongda\Pay\Gateways\Wechat;
+
+use Symfony\Component\HttpFoundation\RedirectResponse;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Exceptions\GatewayException;
+use Yansongda\Pay\Exceptions\InvalidArgumentException;
+use Yansongda\Pay\Exceptions\InvalidSignException;
+
+class WapGateway extends Gateway
+{
+    /**
+     * Pay an order.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $endpoint
+     *
+     * @throws GatewayException
+     * @throws InvalidArgumentException
+     * @throws InvalidSignException
+     */
+    public function pay($endpoint, array $payload): RedirectResponse
+    {
+        $payload['trade_type'] = $this->getTradeType();
+
+        Events::dispatch(new Events\PayStarted('Wechat', 'Wap', $endpoint, $payload));
+
+        $mweb_url = $this->preOrder($payload)->get('mweb_url');
+
+        $url = is_null(Support::getInstance()->return_url) ? $mweb_url : $mweb_url.
+                        '&redirect_url='.urlencode(Support::getInstance()->return_url);
+
+        return new RedirectResponse($url);
+    }
+
+    /**
+     * Get trade type config.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function getTradeType(): string
+    {
+        return 'MWEB';
+    }
+}

+ 114 - 0
vendor/yansongda/pay/src/Listeners/KernelLogSubscriber.php

@@ -0,0 +1,114 @@
+<?php
+
+namespace Yansongda\Pay\Listeners;
+
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Yansongda\Pay\Events;
+use Yansongda\Pay\Log;
+
+class KernelLogSubscriber implements EventSubscriberInterface
+{
+    /**
+     * Returns an array of event names this subscriber wants to listen to.
+     *
+     * The array keys are event names and the value can be:
+     *
+     *  * The method name to call (priority defaults to 0)
+     *  * An array composed of the method name to call and the priority
+     *  * An array of arrays composed of the method names to call and respective
+     *    priorities, or 0 if unset
+     *
+     * For instance:
+     *
+     *  * array('eventName' => 'methodName')
+     *  * array('eventName' => array('methodName', $priority))
+     *  * array('eventName' => array(array('methodName1', $priority), array('methodName2')))
+     *
+     * @return array The event names to listen to
+     */
+    public static function getSubscribedEvents()
+    {
+        return [
+            Events\PayStarting::class => ['writePayStartingLog', 256],
+            Events\PayStarted::class => ['writePayStartedLog', 256],
+            Events\ApiRequesting::class => ['writeApiRequestingLog', 256],
+            Events\ApiRequested::class => ['writeApiRequestedLog', 256],
+            Events\SignFailed::class => ['writeSignFailedLog', 256],
+            Events\RequestReceived::class => ['writeRequestReceivedLog', 256],
+            Events\MethodCalled::class => ['writeMethodCalledLog', 256],
+        ];
+    }
+
+    /**
+     * writePayStartingLog.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function writePayStartingLog(Events\PayStarting $event)
+    {
+        Log::debug("Starting To {$event->driver}", [$event->gateway, $event->params]);
+    }
+
+    /**
+     * writePayStartedLog.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function writePayStartedLog(Events\PayStarted $event)
+    {
+        Log::info(
+            "{$event->driver} {$event->gateway} Has Started",
+            [$event->endpoint, $event->payload]
+        );
+    }
+
+    /**
+     * writeApiRequestingLog.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function writeApiRequestingLog(Events\ApiRequesting $event)
+    {
+        Log::debug("Requesting To {$event->driver} Api", [$event->endpoint, $event->payload]);
+    }
+
+    /**
+     * writeApiRequestedLog.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function writeApiRequestedLog(Events\ApiRequested $event)
+    {
+        Log::debug("Result Of {$event->driver} Api", $event->result);
+    }
+
+    /**
+     * writeSignFailedLog.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function writeSignFailedLog(Events\SignFailed $event)
+    {
+        Log::warning("{$event->driver} Sign Verify FAILED", $event->data);
+    }
+
+    /**
+     * writeRequestReceivedLog.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function writeRequestReceivedLog(Events\RequestReceived $event)
+    {
+        Log::info("Received {$event->driver} Request", $event->data);
+    }
+
+    /**
+     * writeMethodCalledLog.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function writeMethodCalledLog(Events\MethodCalled $event)
+    {
+        Log::info("{$event->driver} {$event->gateway} Method Has Called", [$event->endpoint, $event->payload]);
+    }
+}

+ 49 - 0
vendor/yansongda/pay/src/Log.php

@@ -0,0 +1,49 @@
+<?php
+
+namespace Yansongda\Pay;
+
+use Yansongda\Supports\Log as BaseLog;
+
+/**
+ * @method static void emergency($message, array $context = array())
+ * @method static void alert($message, array $context = array())
+ * @method static void critical($message, array $context = array())
+ * @method static void error($message, array $context = array())
+ * @method static void warning($message, array $context = array())
+ * @method static void notice($message, array $context = array())
+ * @method static void info($message, array $context = array())
+ * @method static void debug($message, array $context = array())
+ * @method static void log($message, array $context = array())
+ */
+class Log
+{
+    /**
+     * Forward call.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $method
+     * @param array  $args
+     *
+     * @return mixed
+     */
+    public static function __callStatic($method, $args)
+    {
+        return forward_static_call_array([BaseLog::class, $method], $args);
+    }
+
+    /**
+     * Forward call.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $method
+     * @param array  $args
+     *
+     * @return mixed
+     */
+    public function __call($method, $args)
+    {
+        return call_user_func_array([BaseLog::class, $method], $args);
+    }
+}

+ 131 - 0
vendor/yansongda/pay/src/Pay.php

@@ -0,0 +1,131 @@
+<?php
+
+namespace Yansongda\Pay;
+
+use Exception;
+use Yansongda\Pay\Contracts\GatewayApplicationInterface;
+use Yansongda\Pay\Exceptions\InvalidGatewayException;
+use Yansongda\Pay\Gateways\Alipay;
+use Yansongda\Pay\Gateways\Wechat;
+use Yansongda\Pay\Listeners\KernelLogSubscriber;
+use Yansongda\Supports\Config;
+use Yansongda\Supports\Log;
+use Yansongda\Supports\Logger;
+use Yansongda\Supports\Str;
+
+/**
+ * @method static Alipay alipay(array $config) 支付宝
+ * @method static Wechat wechat(array $config) 微信
+ */
+class Pay
+{
+    /**
+     * Config.
+     *
+     * @var Config
+     */
+    protected $config;
+
+    /**
+     * Bootstrap.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws Exception
+     */
+    public function __construct(array $config)
+    {
+        $this->config = new Config($config);
+
+        $this->registerLogService();
+        $this->registerEventService();
+    }
+
+    /**
+     * Magic static call.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $method
+     * @param array  $params
+     *
+     * @throws InvalidGatewayException
+     * @throws Exception
+     */
+    public static function __callStatic($method, $params): GatewayApplicationInterface
+    {
+        $app = new self(...$params);
+
+        return $app->create($method);
+    }
+
+    /**
+     * Create a instance.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $method
+     *
+     * @throws InvalidGatewayException
+     */
+    protected function create($method): GatewayApplicationInterface
+    {
+        $gateway = __NAMESPACE__.'\\Gateways\\'.Str::studly($method);
+
+        if (class_exists($gateway)) {
+            return self::make($gateway);
+        }
+
+        throw new InvalidGatewayException("Gateway [{$method}] Not Exists");
+    }
+
+    /**
+     * Make a gateway.
+     *
+     * @author yansongda <me@yansonga.cn>
+     *
+     * @param string $gateway
+     *
+     * @throws InvalidGatewayException
+     */
+    protected function make($gateway): GatewayApplicationInterface
+    {
+        $app = new $gateway($this->config);
+
+        if ($app instanceof GatewayApplicationInterface) {
+            return $app;
+        }
+
+        throw new InvalidGatewayException("Gateway [{$gateway}] Must Be An Instance Of GatewayApplicationInterface");
+    }
+
+    /**
+     * Register log service.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws Exception
+     */
+    protected function registerLogService()
+    {
+        $config = $this->config->get('log');
+        $config['identify'] = 'yansongda.pay';
+
+        $logger = new Logger();
+        $logger->setConfig($config);
+
+        Log::setInstance($logger);
+    }
+
+    /**
+     * Register event service.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    protected function registerEventService()
+    {
+        Events::setDispatcher(Events::createDispatcher());
+
+        Events::addSubscriber(new KernelLogSubscriber());
+    }
+}

+ 6 - 0
vendor/yansongda/supports/.scrutinizer.yml

@@ -0,0 +1,6 @@
+filter:
+  excluded_paths:
+    - tests/*
+
+checks:
+  php: true

+ 20 - 0
vendor/yansongda/supports/LICENSE

@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2017 yansongda <me@yansongda.cn>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 70 - 0
vendor/yansongda/supports/README.md

@@ -0,0 +1,70 @@
+<h1 align="center">Supports</h1>
+
+[![Linter Status](https://github.com/yansongda/supports/workflows/Linter/badge.svg)](https://github.com/yansongda/supports/actions) 
+[![Tester Status](https://github.com/yansongda/supports/workflows/Tester/badge.svg)](https://github.com/yansongda/supports/actions) 
+[![Latest Stable Version](https://poser.pugx.org/yansongda/supports/v/stable)](https://packagist.org/packages/yansongda/supports)
+[![Total Downloads](https://poser.pugx.org/yansongda/supports/downloads)](https://packagist.org/packages/yansongda/supports)
+[![Latest Unstable Version](https://poser.pugx.org/yansongda/supports/v/unstable)](https://packagist.org/packages/yansongda/supports)
+[![License](https://poser.pugx.org/yansongda/supports/license)](https://packagist.org/packages/yansongda/supports)
+
+
+handle with array/config/log/guzzle etc.
+
+## About log
+
+### Register
+
+#### Method 1
+
+A application logger can extends `Yansongda\Supports\Log` and modify `createLogger` method, the method must return instance of `Monolog\Logger`.
+
+```PHP
+use Yansongda\Supports\Log;
+use Monolog\Logger;
+
+class APPLICATIONLOG extends Log
+{
+    /**
+     * Make a default log instance.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return Logger
+     */
+    public static function createLogger()
+    {
+        $handler = new StreamHandler('./log.log');
+        $handler->setFormatter(new LineFormatter("%datetime% > %level_name% > %message% %context% %extra%\n\n"));
+
+        $logger = new Logger('yansongda.private_number');
+        $logger->pushHandler($handler);
+
+        return $logger;
+    }
+}
+```
+
+#### Method 2
+
+Or, just init the log service with:
+
+```PHP
+use Yansongda\Supports\Log;
+
+protected function registerLog()
+{
+    $logger = Log::createLogger($file, $identify, $level);
+
+    Log::setLogger($logger);
+}
+```
+
+### Usage
+
+After registerLog, you can use Log service:
+
+```PHP
+use Yansongda\Supports\Log;
+
+Log::debug('test', ['test log']);
+```

+ 39 - 0
vendor/yansongda/supports/composer.json

@@ -0,0 +1,39 @@
+{
+    "name": "yansongda/supports",
+    "description": "common components",
+    "keywords": ["support", "array", "collection", "config", "http", "guzzle", "throttle"],
+    "support": {
+        "issues": "https://github.com/yansongda/supports/issues",
+        "source": "https://github.com/yansongda/supports"
+    },
+    "authors": [
+        {
+            "name": "yansongda",
+            "email": "me@yansongda.cn"
+        }
+    ],
+    "require": {
+        "php": ">=7.1.3",
+        "monolog/monolog": "^1.23 || ^2.0",
+        "guzzlehttp/guzzle": "^6.2 || ^7.0"
+    },
+    "require-dev": {
+        "predis/predis": "^1.1",
+        "phpunit/phpunit": "^7.5",
+        "friendsofphp/php-cs-fixer": "^2.15"
+    },
+    "autoload": {
+        "psr-4": {
+            "Yansongda\\Supports\\": "src/"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "Yansongda\\Supports\\Tests\\": "tests/"
+        }
+    },
+    "suggest": {
+        "predis/predis": "Allows to use throttle feature"
+    },
+    "license": "MIT"
+}

+ 605 - 0
vendor/yansongda/supports/src/Arr.php

@@ -0,0 +1,605 @@
+<?php
+
+namespace Yansongda\Supports;
+
+use ArrayAccess;
+
+/**
+ * Array helper from Illuminate\Support\Arr.
+ */
+class Arr
+{
+    /**
+     * Determine whether the given value is array accessible.
+     *
+     * @param mixed $value
+     */
+    public static function accessible($value): bool
+    {
+        return is_array($value) || $value instanceof ArrayAccess;
+    }
+
+    /**
+     * Add an element to an array using "dot" notation if it doesn't exist.
+     *
+     * @param mixed $value
+     */
+    public static function add(array $array, string $key, $value): array
+    {
+        if (is_null(static::get($array, $key))) {
+            static::set($array, $key, $value);
+        }
+
+        return $array;
+    }
+
+    /**
+     * Build a new array using a callback.
+     */
+    public static function build(array $array, callable $callback): array
+    {
+        $results = [];
+
+        foreach ($array as $key => $value) {
+            [$innerKey, $innerValue] = call_user_func($callback, $key, $value);
+            $results[$innerKey] = $innerValue;
+        }
+
+        return $results;
+    }
+
+    /**
+     * Divide an array into two arrays. One with keys and the other with values.
+     */
+    public static function divide(array $array): array
+    {
+        return [
+                array_keys($array),
+                array_values($array),
+               ];
+    }
+
+    /**
+     * Flatten a multi-dimensional associative array with dots.
+     */
+    public static function dot(array $array, string $prepend = ''): array
+    {
+        $results = [];
+
+        foreach ($array as $key => $value) {
+            if (is_array($value)) {
+                $results = array_merge($results, static::dot($value, $prepend.$key.'.'));
+            } else {
+                $results[$prepend.$key] = $value;
+            }
+        }
+
+        return $results;
+    }
+
+    /**
+     * Get all of the given array except for a specified array of items.
+     *
+     * @param array|string $keys
+     */
+    public static function except(array $array, $keys): array
+    {
+        return array_diff_key($array, array_flip((array) $keys));
+    }
+
+    /**
+     * access array.
+     *
+     * if not array access, return original.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param mixed $data
+     *
+     * @return mixed
+     */
+    public static function access($data)
+    {
+        if (!self::accessible($data) &&
+            !(is_object($data) && method_exists($data, 'toArray'))) {
+            return $data;
+        }
+
+        return is_object($data) ? $data->toArray() : $data;
+    }
+
+    /**
+     * Determine if the given key exists in the provided array.
+     *
+     * @param \ArrayAccess|array $array
+     * @param string|int         $key
+     *
+     * @return bool
+     */
+    public static function exists($array, $key)
+    {
+        $array = self::access($array);
+
+        if ($array instanceof ArrayAccess) {
+            return $array->offsetExists($key);
+        }
+
+        return array_key_exists($key, $array);
+    }
+
+    /**
+     * Check if an item or items exist in an array using "dot" notation.
+     *
+     * @param \ArrayAccess|array $array
+     * @param string|array       $keys
+     *
+     * @return bool
+     */
+    public static function has($array, $keys)
+    {
+        $array = self::access($array);
+
+        $keys = (array) $keys;
+
+        if (!$array || $keys === []) {
+            return false;
+        }
+
+        foreach ($keys as $key) {
+            $subKeyArray = $array;
+
+            if (static::exists($array, $key)) {
+                continue;
+            }
+
+            foreach (explode('.', $key) as $segment) {
+                if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) {
+                    $subKeyArray = $subKeyArray[$segment];
+                } else {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Determine if any of the keys exist in an array using "dot" notation.
+     *
+     * @param \ArrayAccess|array $array
+     * @param string|array       $keys
+     *
+     * @return bool
+     */
+    public static function hasAny($array, $keys)
+    {
+        $array = self::access($array);
+
+        if (is_null($keys)) {
+            return false;
+        }
+
+        $keys = (array) $keys;
+
+        if (!$array) {
+            return false;
+        }
+
+        if ($keys === []) {
+            return false;
+        }
+
+        foreach ($keys as $key) {
+            if (static::has($array, $key)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Fetch a flattened array of a nested array element.
+     */
+    public static function fetch(array $array, string $key): array
+    {
+        $results = [];
+
+        foreach (explode('.', $key) as $segment) {
+            $results = [];
+            foreach ($array as $value) {
+                $value = (array) $value;
+                $results[] = $value[$segment];
+            }
+            $array = array_values($results);
+        }
+
+        return array_values($results);
+    }
+
+    /**
+     * Return the first element in an array passing a given truth test.
+     *
+     * @param mixed $default
+     *
+     * @return mixed
+     */
+    public static function first(array $array, callable $callback, $default = null)
+    {
+        foreach ($array as $key => $value) {
+            if (call_user_func($callback, $key, $value)) {
+                return $value;
+            }
+        }
+
+        return $default;
+    }
+
+    /**
+     * Return the last element in an array passing a given truth test.
+     *
+     * @param mixed $default
+     *
+     * @return mixed
+     */
+    public static function last(array $array, callable $callback, $default = null)
+    {
+        return static::first(array_reverse($array), $callback, $default);
+    }
+
+    /**
+     * Flatten a multi-dimensional array into a single level.
+     */
+    public static function flatten(array $array): array
+    {
+        $return = [];
+        array_walk_recursive(
+            $array,
+            function ($x) use (&$return) {
+                $return[] = $x;
+            }
+        );
+
+        return $return;
+    }
+
+    /**
+     * Remove one or many array items from a given array using "dot" notation.
+     *
+     * @param array        $array
+     * @param array|string $keys
+     */
+    public static function forget(&$array, $keys)
+    {
+        $original = &$array;
+
+        $keys = (array) $keys;
+
+        if (0 === count($keys)) {
+            return;
+        }
+
+        foreach ($keys as $key) {
+            // if the exact key exists in the top-level, remove it
+            if (static::exists($array, $key)) {
+                unset($array[$key]);
+
+                continue;
+            }
+
+            $parts = explode('.', $key);
+
+            // clean up before each pass
+            $array = &$original;
+
+            while (count($parts) > 1) {
+                $part = array_shift($parts);
+
+                if (isset($array[$part]) && is_array($array[$part])) {
+                    $array = &$array[$part];
+                } else {
+                    continue 2;
+                }
+            }
+
+            unset($array[array_shift($parts)]);
+        }
+    }
+
+    /**
+     * Get an item from an array using "dot" notation.
+     *
+     * @param mixed $default
+     *
+     * @return mixed
+     */
+    public static function get(array $array, string $key, $default = null)
+    {
+        if (is_null($key)) {
+            return $array;
+        }
+
+        if (isset($array[$key])) {
+            return $array[$key];
+        }
+
+        foreach (explode('.', $key) as $segment) {
+            if (!is_array($array) || !array_key_exists($segment, $array)) {
+                return $default;
+            }
+            $array = $array[$segment];
+        }
+
+        return $array;
+    }
+
+    /**
+     * Get a subset of the items from the given array.
+     *
+     * @param array|string $keys
+     */
+    public static function only(array $array, $keys): array
+    {
+        return array_intersect_key($array, array_flip((array) $keys));
+    }
+
+    /**
+     * Pluck an array of values from an array.
+     *
+     * @param string $key
+     */
+    public static function pluck(array $array, string $value, string $key = null): array
+    {
+        $results = [];
+
+        foreach ($array as $item) {
+            $itemValue = is_object($item) ? $item->{$value} : $item[$value];
+            // If the key is "null", we will just append the value to the array and keep
+            // looping. Otherwise we will key the array using the value of the key we
+            // received from the developer. Then we'll return the final array form.
+            if (is_null($key)) {
+                $results[] = $itemValue;
+            } else {
+                $itemKey = is_object($item) ? $item->{$key} : $item[$key];
+                $results[$itemKey] = $itemValue;
+            }
+        }
+
+        return $results;
+    }
+
+    /**
+     * Push an item onto the beginning of an array.
+     *
+     * @param mixed $value
+     * @param mixed $key
+     *
+     * @return array
+     */
+    public static function prepend(array $array, $value, $key = null)
+    {
+        if (is_null($key)) {
+            array_unshift($array, $value);
+        } else {
+            $array = [$key => $value] + $array;
+        }
+
+        return $array;
+    }
+
+    /**
+     * Get a value from the array, and remove it.
+     *
+     * @param mixed $default
+     *
+     * @return mixed
+     */
+    public static function pull(array &$array, string $key, $default = null)
+    {
+        $value = static::get($array, $key, $default);
+
+        static::forget($array, $key);
+
+        return $value;
+    }
+
+    /**
+     * Get one or a specified number of random values from an array.
+     *
+     * @param array    $array
+     * @param int|null $number
+     *
+     * @return mixed
+     *
+     * @throws \InvalidArgumentException
+     */
+    public static function random(array $array, $number = null)
+    {
+        $requested = is_null($number) ? 1 : $number;
+
+        $count = count($array);
+
+        $number = $requested > $count ? $count : $requested;
+
+        if (is_null($number)) {
+            return $array[array_rand($array)];
+        }
+
+        if (0 === (int) $number) {
+            return [];
+        }
+
+        $keys = array_rand($array, $number);
+
+        $results = [];
+
+        foreach ((array) $keys as $key) {
+            $results[] = $array[$key];
+        }
+
+        return $results;
+    }
+
+    /**
+     * Set an array item to a given value using "dot" notation.
+     *
+     * If no key is given to the method, the entire array will be replaced.
+     *
+     * @param mixed $value
+     */
+    public static function set(array &$array, string $key, $value): array
+    {
+        if (is_null($key)) {
+            return $array = $value;
+        }
+
+        $keys = explode('.', $key);
+
+        while (count($keys) > 1) {
+            $key = array_shift($keys);
+            // If the key doesn't exist at this depth, we will just create an empty array
+            // to hold the next value, allowing us to create the arrays to hold final
+            // values at the correct depth. Then we'll keep digging into the array.
+            if (!isset($array[$key]) || !is_array($array[$key])) {
+                $array[$key] = [];
+            }
+            $array = &$array[$key];
+        }
+        $array[array_shift($keys)] = $value;
+
+        return $array;
+    }
+
+    /**
+     * Sort the array using the given Closure.
+     */
+    public static function sort(array $array, callable $callback): array
+    {
+        $results = [];
+
+        foreach ($array as $key => $value) {
+            $results[$key] = $callback($value);
+        }
+
+        return $results;
+    }
+
+    /**
+     * Shuffle the given array and return the result.
+     *
+     * @param array    $array
+     * @param int|null $seed
+     *
+     * @return array
+     */
+    public static function shuffle(array $array, $seed = null): array
+    {
+        if (is_null($seed)) {
+            shuffle($array);
+        } else {
+            mt_srand($seed);
+            shuffle($array);
+            mt_srand();
+        }
+
+        return $array;
+    }
+
+    /**
+     * Convert the array into a query string.
+     */
+    public static function query(array $array): string
+    {
+        return http_build_query($array, null, '&', PHP_QUERY_RFC3986);
+    }
+
+    /**
+     * Filter the array using the given callback.
+     */
+    public static function where(array $array, ?callable $callback = null): array
+    {
+        return array_filter($array, $callback ?? function ($value) use ($callback) {
+            if (static::accessible($value)) {
+                $value = static::where($value, $callback);
+            }
+
+            if (is_array($value) && 0 === count($value)) {
+                $value = null;
+            }
+
+            return '' !== $value && !is_null($value);
+        }, ARRAY_FILTER_USE_BOTH);
+    }
+
+    /**
+     * Convert encoding.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $from_encoding
+     */
+    public static function encoding(array $array, string $to_encoding, $from_encoding = 'gb2312'): array
+    {
+        $encoded = [];
+
+        foreach ($array as $key => $value) {
+            $encoded[$key] = is_array($value) ? self::encoding($value, $to_encoding, $from_encoding) :
+                                                mb_convert_encoding($value, $to_encoding, $from_encoding);
+        }
+
+        return $encoded;
+    }
+
+    /**
+     * camelCaseKey.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param mixed $data
+     *
+     * @return mixed
+     */
+    public static function camelCaseKey($data)
+    {
+        if (!self::accessible($data) &&
+            !(is_object($data) && method_exists($data, 'toArray'))) {
+            return $data;
+        }
+
+        $result = [];
+        $data = self::access($data);
+
+        foreach ($data as $key => $value) {
+            $result[is_string($key) ? Str::camel($key) : $key] = self::camelCaseKey($value);
+        }
+
+        return $result;
+    }
+
+    /**
+     * snakeCaseKey.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param mixed $data
+     *
+     * @return mixed
+     */
+    public static function snakeCaseKey($data)
+    {
+        if (!self::accessible($data) &&
+            !(is_object($data) && method_exists($data, 'toArray'))) {
+            return $data;
+        }
+
+        $data = self::access($data);
+        $result = [];
+
+        foreach ($data as $key => $value) {
+            $result[is_string($key) ? Str::snake($key) : $key] = self::snakeCaseKey($value);
+        }
+
+        return $result;
+    }
+}

+ 363 - 0
vendor/yansongda/supports/src/Collection.php

@@ -0,0 +1,363 @@
+<?php
+
+namespace Yansongda\Supports;
+
+use ArrayAccess;
+use ArrayIterator;
+use Countable;
+use IteratorAggregate;
+use JsonSerializable;
+use Serializable;
+
+class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable, Serializable
+{
+    /**
+     * The collection data.
+     *
+     * @var array
+     */
+    protected $items = [];
+
+    /**
+     * set data.
+     *
+     * @param mixed $items
+     */
+    public function __construct(array $items = [])
+    {
+        foreach ($items as $key => $value) {
+            $this->set($key, $value);
+        }
+    }
+
+    /**
+     * To string.
+     */
+    public function __toString(): string
+    {
+        return $this->toJson();
+    }
+
+    /**
+     * Get a data by key.
+     *
+     * @return mixed
+     */
+    public function __get(string $key)
+    {
+        return $this->get($key);
+    }
+
+    /**
+     * Assigns a value to the specified data.
+     *
+     * @param mixed $value
+     */
+    public function __set(string $key, $value)
+    {
+        $this->set($key, $value);
+    }
+
+    /**
+     * Whether or not an data exists by key.
+     */
+    public function __isset(string $key): bool
+    {
+        return $this->has($key);
+    }
+
+    /**
+     * Unsets an data by key.
+     */
+    public function __unset(string $key)
+    {
+        $this->forget($key);
+    }
+
+    /**
+     * Return all items.
+     */
+    public function all(): array
+    {
+        return $this->items;
+    }
+
+    /**
+     * Return specific items.
+     */
+    public function only(array $keys): array
+    {
+        $return = [];
+
+        foreach ($keys as $key) {
+            $value = $this->get($key);
+
+            if (!is_null($value)) {
+                $return[$key] = $value;
+            }
+        }
+
+        return $return;
+    }
+
+    /**
+     * Get all items except for those with the specified keys.
+     *
+     * @param mixed $keys
+     *
+     * @return static
+     */
+    public function except($keys)
+    {
+        $keys = is_array($keys) ? $keys : func_get_args();
+
+        return new static(Arr::except($this->items, $keys));
+    }
+
+    /**
+     * Merge data.
+     *
+     * @param Collection|array $items
+     */
+    public function merge($items): array
+    {
+        foreach ($items as $key => $value) {
+            $this->set($key, $value);
+        }
+
+        return $this->all();
+    }
+
+    /**
+     * To determine Whether the specified element exists.
+     */
+    public function has(string $key): bool
+    {
+        return !is_null(Arr::get($this->items, $key));
+    }
+
+    /**
+     * Retrieve the first item.
+     *
+     * @return mixed
+     */
+    public function first()
+    {
+        return reset($this->items);
+    }
+
+    /**
+     * Retrieve the last item.
+     *
+     * @return mixed
+     */
+    public function last()
+    {
+        $end = end($this->items);
+
+        reset($this->items);
+
+        return $end;
+    }
+
+    /**
+     * add the item value.
+     *
+     * @param mixed $value
+     */
+    public function add(string $key, $value)
+    {
+        Arr::set($this->items, $key, $value);
+    }
+
+    /**
+     * Set the item value.
+     *
+     * @param mixed $value
+     */
+    public function set(string $key, $value)
+    {
+        Arr::set($this->items, $key, $value);
+    }
+
+    /**
+     * Retrieve item from Collection.
+     *
+     * @param string $key
+     * @param mixed  $default
+     *
+     * @return mixed
+     */
+    public function get(?string $key = null, $default = null)
+    {
+        return Arr::get($this->items, $key, $default);
+    }
+
+    /**
+     * Remove item form Collection.
+     */
+    public function forget(string $key)
+    {
+        Arr::forget($this->items, $key);
+    }
+
+    /**
+     * Build to array.
+     */
+    public function toArray(): array
+    {
+        return $this->all();
+    }
+
+    /**
+     * Build to json.
+     */
+    public function toJson(int $option = JSON_UNESCAPED_UNICODE): string
+    {
+        return json_encode($this->all(), $option);
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.4.0)<br/>
+     * Specify data which should be serialized to JSON.
+     *
+     * @see http://php.net/manual/en/jsonserializable.jsonserialize.php
+     *
+     * @return mixed data which can be serialized by <b>json_encode</b>,
+     *               which is a value of any type other than a resource
+     */
+    public function jsonSerialize()
+    {
+        return $this->items;
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.1.0)<br/>
+     * String representation of object.
+     *
+     * @see http://php.net/manual/en/serializable.serialize.php
+     *
+     * @return string the string representation of the object or null
+     */
+    public function serialize()
+    {
+        return serialize($this->items);
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.0.0)<br/>
+     * Retrieve an external iterator.
+     *
+     * @see http://php.net/manual/en/iteratoraggregate.getiterator.php
+     *
+     * @return ArrayIterator An instance of an object implementing <b>Iterator</b> or
+     *                       <b>ArrayIterator</b>
+     */
+    public function getIterator()
+    {
+        return new ArrayIterator($this->items);
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.1.0)<br/>
+     * Count elements of an object.
+     *
+     * @see http://php.net/manual/en/countable.count.php
+     *
+     * @return int The custom count as an integer.
+     *             </p>
+     *             <p>
+     *             The return value is cast to an integer
+     */
+    public function count()
+    {
+        return count($this->items);
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.1.0)<br/>
+     * Constructs the object.
+     *
+     * @see  http://php.net/manual/en/serializable.unserialize.php
+     *
+     * @param string $serialized <p>
+     *                           The string representation of the object.
+     *                           </p>
+     *
+     * @return mixed|void
+     */
+    public function unserialize($serialized)
+    {
+        return $this->items = unserialize($serialized);
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.0.0)<br/>
+     * Whether a offset exists.
+     *
+     * @see http://php.net/manual/en/arrayaccess.offsetexists.php
+     *
+     * @param mixed $offset <p>
+     *                      An offset to check for.
+     *                      </p>
+     *
+     * @return bool true on success or false on failure.
+     *              The return value will be casted to boolean if non-boolean was returned
+     */
+    public function offsetExists($offset)
+    {
+        return $this->has($offset);
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.0.0)<br/>
+     * Offset to unset.
+     *
+     * @see http://php.net/manual/en/arrayaccess.offsetunset.php
+     *
+     * @param mixed $offset <p>
+     *                      The offset to unset.
+     *                      </p>
+     */
+    public function offsetUnset($offset)
+    {
+        if ($this->offsetExists($offset)) {
+            $this->forget($offset);
+        }
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.0.0)<br/>
+     * Offset to retrieve.
+     *
+     * @see http://php.net/manual/en/arrayaccess.offsetget.php
+     *
+     * @param mixed $offset <p>
+     *                      The offset to retrieve.
+     *                      </p>
+     *
+     * @return mixed Can return all value types
+     */
+    public function offsetGet($offset)
+    {
+        return $this->offsetExists($offset) ? $this->get($offset) : null;
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.0.0)<br/>
+     * Offset to set.
+     *
+     * @see http://php.net/manual/en/arrayaccess.offsetset.php
+     *
+     * @param mixed $offset <p>
+     *                      The offset to assign the value to.
+     *                      </p>
+     * @param mixed $value  <p>
+     *                      The value to set.
+     *                      </p>
+     */
+    public function offsetSet($offset, $value)
+    {
+        $this->set($offset, $value);
+    }
+}

+ 7 - 0
vendor/yansongda/supports/src/Config.php

@@ -0,0 +1,7 @@
+<?php
+
+namespace Yansongda\Supports;
+
+class Config extends Collection
+{
+}

+ 91 - 0
vendor/yansongda/supports/src/Log.php

@@ -0,0 +1,91 @@
+<?php
+
+namespace Yansongda\Supports;
+
+/**
+ * @method static void emergency($message, array $context = array())
+ * @method static void alert($message, array $context = array())
+ * @method static void critical($message, array $context = array())
+ * @method static void error($message, array $context = array())
+ * @method static void warning($message, array $context = array())
+ * @method static void notice($message, array $context = array())
+ * @method static void info($message, array $context = array())
+ * @method static void debug($message, array $context = array())
+ * @method static void log($message, array $context = array())
+ */
+class Log extends Logger
+{
+    /**
+     * instance.
+     *
+     * @var \Psr\Log\LoggerInterface
+     */
+    private static $instance;
+
+    /**
+     * Bootstrap.
+     */
+    private function __construct()
+    {
+    }
+
+    /**
+     * __call.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $method
+     * @param array  $args
+     *
+     * @throws \Exception
+     */
+    public function __call($method, $args): void
+    {
+        call_user_func_array([self::getInstance(), $method], $args);
+    }
+
+    /**
+     * __callStatic.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $method
+     * @param array  $args
+     *
+     * @throws \Exception
+     */
+    public static function __callStatic($method, $args): void
+    {
+        forward_static_call_array([self::getInstance(), $method], $args);
+    }
+
+    /**
+     * getInstance.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return \Yansongda\Supports\Logger
+     */
+    public static function getInstance(): Logger
+    {
+        if (is_null(self::$instance)) {
+            self::$instance = new Logger();
+        }
+
+        return self::$instance;
+    }
+
+    /**
+     * setInstance.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param \Yansongda\Supports\Logger $logger
+     *
+     * @throws \Exception
+     */
+    public static function setInstance(Logger $logger): void
+    {
+        self::$instance = $logger;
+    }
+}

+ 240 - 0
vendor/yansongda/supports/src/Logger.php

@@ -0,0 +1,240 @@
+<?php
+
+namespace Yansongda\Supports;
+
+use Exception;
+use Monolog\Formatter\FormatterInterface;
+use Monolog\Formatter\LineFormatter;
+use Monolog\Handler\AbstractHandler;
+use Monolog\Handler\RotatingFileHandler;
+use Monolog\Handler\StreamHandler;
+use Monolog\Logger as BaseLogger;
+use Psr\Log\LoggerInterface;
+
+/**
+ * @method void emergency($message, array $context = array())
+ * @method void alert($message, array $context = array())
+ * @method void critical($message, array $context = array())
+ * @method void error($message, array $context = array())
+ * @method void warning($message, array $context = array())
+ * @method void notice($message, array $context = array())
+ * @method void info($message, array $context = array())
+ * @method void debug($message, array $context = array())
+ * @method void log($message, array $context = array())
+ */
+class Logger
+{
+    /**
+     * Logger instance.
+     *
+     * @var LoggerInterface
+     */
+    protected $logger;
+
+    /**
+     * formatter.
+     *
+     * @var \Monolog\Formatter\FormatterInterface
+     */
+    protected $formatter;
+
+    /**
+     * handler.
+     *
+     * @var AbstractHandler
+     */
+    protected $handler;
+
+    /**
+     * config.
+     *
+     * @var array
+     */
+    protected $config = [
+        'file' => null,
+        'identify' => 'yansongda.supports',
+        'level' => BaseLogger::DEBUG,
+        'type' => 'daily',
+        'max_files' => 30,
+    ];
+
+    /**
+     * Forward call.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $method
+     * @param array  $args
+     *
+     * @throws Exception
+     */
+    public function __call($method, $args): void
+    {
+        call_user_func_array([$this->getLogger(), $method], $args);
+    }
+
+    /**
+     * Set logger.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function setLogger(LoggerInterface $logger): Logger
+    {
+        $this->logger = $logger;
+
+        return $this;
+    }
+
+    /**
+     * Return the logger instance.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws Exception
+     */
+    public function getLogger(): LoggerInterface
+    {
+        if (is_null($this->logger)) {
+            $this->logger = $this->createLogger();
+        }
+
+        return $this->logger;
+    }
+
+    /**
+     * Make a default log instance.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws Exception
+     */
+    public function createLogger(): BaseLogger
+    {
+        $handler = $this->getHandler();
+
+        $handler->setFormatter($this->getFormatter());
+
+        $logger = new BaseLogger($this->config['identify']);
+
+        $logger->pushHandler($handler);
+
+        return $logger;
+    }
+
+    /**
+     * setFormatter.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return $this
+     */
+    public function setFormatter(FormatterInterface $formatter): self
+    {
+        $this->formatter = $formatter;
+
+        return $this;
+    }
+
+    /**
+     * getFormatter.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function getFormatter(): FormatterInterface
+    {
+        if (is_null($this->formatter)) {
+            $this->formatter = $this->createFormatter();
+        }
+
+        return $this->formatter;
+    }
+
+    /**
+     * createFormatter.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function createFormatter(): LineFormatter
+    {
+        return new LineFormatter(
+            "%datetime% > %channel%.%level_name% > %message% %context% %extra%\n\n",
+            null,
+            false,
+            true
+        );
+    }
+
+    /**
+     * setHandler.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return $this
+     */
+    public function setHandler(AbstractHandler $handler): self
+    {
+        $this->handler = $handler;
+
+        return $this;
+    }
+
+    /**
+     * getHandler.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws \Exception
+     */
+    public function getHandler(): AbstractHandler
+    {
+        if (is_null($this->handler)) {
+            $this->handler = $this->createHandler();
+        }
+
+        return $this->handler;
+    }
+
+    /**
+     * createHandler.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws \Exception
+     *
+     * @return \Monolog\Handler\RotatingFileHandler|\Monolog\Handler\StreamHandler
+     */
+    public function createHandler(): AbstractHandler
+    {
+        $file = $this->config['file'] ?? sys_get_temp_dir().'/logs/'.$this->config['identify'].'.log';
+
+        if ('single' === $this->config['type']) {
+            return new StreamHandler($file, $this->config['level']);
+        }
+
+        return new RotatingFileHandler($file, $this->config['max_files'], $this->config['level']);
+    }
+
+    /**
+     * setConfig.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return $this
+     */
+    public function setConfig(array $config): self
+    {
+        $this->config = array_merge($this->config, $config);
+
+        return $this;
+    }
+
+    /**
+     * getConfig.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function getConfig(): array
+    {
+        return $this->config;
+    }
+}

+ 36 - 0
vendor/yansongda/supports/src/Logger/StdoutHandler.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace Yansongda\Supports\Logger;
+
+use Monolog\Handler\AbstractProcessingHandler;
+use Monolog\Logger;
+use Symfony\Component\Console\Output\ConsoleOutput;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class StdoutHandler extends AbstractProcessingHandler
+{
+    /**
+     * @var OutputInterface
+     */
+    private $output;
+
+    /**
+     * Bootstrap.
+     *
+     * @param int  $level
+     * @param bool $bubble
+     */
+    public function __construct($level = Logger::DEBUG, $bubble = true, ?OutputInterface $output = null)
+    {
+        $this->output = $output ?? new ConsoleOutput();
+        parent::__construct($level, $bubble);
+    }
+
+    /**
+     * Writes the record down to the log of the implementing handler.
+     */
+    protected function write(array $record): void
+    {
+        $this->output->writeln($record['formatted']);
+    }
+}

+ 570 - 0
vendor/yansongda/supports/src/Str.php

@@ -0,0 +1,570 @@
+<?php
+
+namespace Yansongda\Supports;
+
+use Exception;
+
+/**
+ * modify from Illuminate\Support.
+ */
+class Str
+{
+    /**
+     * The cache of snake-cased words.
+     *
+     * @var array
+     */
+    protected static $snakeCache = [];
+
+    /**
+     * The cache of camel-cased words.
+     *
+     * @var array
+     */
+    protected static $camelCache = [];
+
+    /**
+     * The cache of studly-cased words.
+     *
+     * @var array
+     */
+    protected static $studlyCache = [];
+
+    /**
+     * Return the remainder of a string after a given value.
+     */
+    public static function after(string $subject, string $search): string
+    {
+        return '' === $search ? $subject : array_reverse(explode($search, $subject, 2))[0];
+    }
+
+    /**
+     * Transliterate a UTF-8 value to ASCII.
+     */
+    public static function ascii(string $value, string $language = 'en'): string
+    {
+        $languageSpecific = static::languageSpecificCharsArray($language);
+
+        if (!is_null($languageSpecific)) {
+            $value = str_replace($languageSpecific[0], $languageSpecific[1], $value);
+        }
+
+        foreach (static::charsArray() as $key => $val) {
+            $value = str_replace($val, $key, $value);
+        }
+
+        return preg_replace('/[^\x20-\x7E]/u', '', $value);
+    }
+
+    /**
+     * Get the portion of a string before a given value.
+     */
+    public static function before(string $subject, string $search): string
+    {
+        return '' === $search ? $subject : explode($search, $subject)[0];
+    }
+
+    /**
+     * Convert a value to camel case.
+     */
+    public static function camel(string $value): string
+    {
+        if (isset(static::$camelCache[$value])) {
+            return static::$camelCache[$value];
+        }
+
+        return static::$camelCache[$value] = lcfirst(static::studly($value));
+    }
+
+    /**
+     * Determine if a given string contains a given substring.
+     *
+     * @param string|array $needles
+     */
+    public static function contains(string $haystack, $needles): bool
+    {
+        foreach ((array) $needles as $needle) {
+            if ('' !== $needle && false !== mb_strpos($haystack, $needle)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Determine if a given string ends with a given substring.
+     *
+     * @param string|array $needles
+     */
+    public static function endsWith(string $haystack, $needles): bool
+    {
+        foreach ((array) $needles as $needle) {
+            if (substr($haystack, -strlen($needle)) === (string) $needle) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Cap a string with a single instance of a given value.
+     */
+    public static function finish(string $value, string $cap): string
+    {
+        $quoted = preg_quote($cap, '/');
+
+        return preg_replace('/(?:'.$quoted.')+$/u', '', $value).$cap;
+    }
+
+    /**
+     * Determine if a given string matches a given pattern.
+     *
+     * @param string|array $pattern
+     */
+    public static function is($pattern, string $value): bool
+    {
+        $patterns = is_array($pattern) ? $pattern : (array) $pattern;
+
+        if (empty($patterns)) {
+            return false;
+        }
+
+        foreach ($patterns as $pattern) {
+            // If the given value is an exact match we can of course return true right
+            // from the beginning. Otherwise, we will translate asterisks and do an
+            // actual pattern match against the two strings to see if they match.
+            if ($pattern == $value) {
+                return true;
+            }
+
+            $pattern = preg_quote($pattern, '#');
+
+            // Asterisks are translated into zero-or-more regular expression wildcards
+            // to make it convenient to check if the strings starts with the given
+            // pattern such as "library/*", making any string check convenient.
+            $pattern = str_replace('\*', '.*', $pattern);
+
+            if (1 === preg_match('#^'.$pattern.'\z#u', $value)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Convert a string to kebab case.
+     */
+    public static function kebab(string $value): string
+    {
+        return static::snake($value, '-');
+    }
+
+    /**
+     * Return the length of the given string.
+     *
+     * @param string $encoding
+     */
+    public static function length(string $value, ?string $encoding = null): int
+    {
+        if (null !== $encoding) {
+            return mb_strlen($value, $encoding);
+        }
+
+        return mb_strlen($value);
+    }
+
+    /**
+     * Limit the number of characters in a string.
+     */
+    public static function limit(string $value, int $limit = 100, string $end = '...'): string
+    {
+        if (mb_strwidth($value, 'UTF-8') <= $limit) {
+            return $value;
+        }
+
+        return rtrim(mb_strimwidth($value, 0, $limit, '', 'UTF-8')).$end;
+    }
+
+    /**
+     * Convert the given string to lower-case.
+     */
+    public static function lower(string $value): string
+    {
+        return mb_strtolower($value, 'UTF-8');
+    }
+
+    /**
+     * Limit the number of words in a string.
+     */
+    public static function words(string $value, int $words = 100, string $end = '...'): string
+    {
+        preg_match('/^\s*+(?:\S++\s*+){1,'.$words.'}/u', $value, $matches);
+
+        if (!isset($matches[0]) || static::length($value) === static::length($matches[0])) {
+            return $value;
+        }
+
+        return rtrim($matches[0]).$end;
+    }
+
+    /**
+     * Parse a Class.
+     */
+    public static function parseCallback(string $callback, ?string $default = null): array
+    {
+        return static::contains($callback, '@') ? explode('@', $callback, 2) : [$callback, $default];
+    }
+
+    /**
+     * Generate a more truly "random" alpha-numeric string.
+     *
+     * @throws Exception
+     */
+    public static function random(int $length = 16): string
+    {
+        $string = '';
+
+        while (($len = strlen($string)) < $length) {
+            $size = $length - $len;
+
+            $bytes = function_exists('random_bytes') ? random_bytes($size) : mt_rand();
+
+            $string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size);
+        }
+
+        return $string;
+    }
+
+    /**
+     * Replace a given value in the string sequentially with an array.
+     */
+    public static function replaceArray(string $search, array $replace, string $subject): string
+    {
+        foreach ($replace as $value) {
+            $subject = static::replaceFirst($search, $value, $subject);
+        }
+
+        return $subject;
+    }
+
+    /**
+     * Replace the first occurrence of a given value in the string.
+     */
+    public static function replaceFirst(string $search, string $replace, string $subject): string
+    {
+        if ('' == $search) {
+            return $subject;
+        }
+
+        $position = strpos($subject, $search);
+
+        if (false !== $position) {
+            return substr_replace($subject, $replace, $position, strlen($search));
+        }
+
+        return $subject;
+    }
+
+    /**
+     * Replace the last occurrence of a given value in the string.
+     */
+    public static function replaceLast(string $search, string $replace, string $subject): string
+    {
+        $position = strrpos($subject, $search);
+
+        if (false !== $position) {
+            return substr_replace($subject, $replace, $position, strlen($search));
+        }
+
+        return $subject;
+    }
+
+    /**
+     * Begin a string with a single instance of a given value.
+     */
+    public static function start(string $value, string $prefix): string
+    {
+        $quoted = preg_quote($prefix, '/');
+
+        return $prefix.preg_replace('/^(?:'.$quoted.')+/u', '', $value);
+    }
+
+    /**
+     * Convert the given string to upper-case.
+     */
+    public static function upper(string $value): string
+    {
+        return mb_strtoupper($value, 'UTF-8');
+    }
+
+    /**
+     * Convert the given string to title case.
+     */
+    public static function title(string $value): string
+    {
+        return mb_convert_case($value, MB_CASE_TITLE, 'UTF-8');
+    }
+
+    /**
+     * Generate a URL friendly "slug" from a given string.
+     */
+    public static function slug(string $title, string $separator = '-', string $language = 'en'): string
+    {
+        $title = static::ascii($title, $language);
+
+        // Convert all dashes/underscores into separator
+        $flip = '-' == $separator ? '_' : '-';
+
+        $title = preg_replace('!['.preg_quote($flip).']+!u', $separator, $title);
+
+        // Replace @ with the word 'at'
+        $title = str_replace('@', $separator.'at'.$separator, $title);
+
+        // Remove all characters that are not the separator, letters, numbers, or whitespace.
+        $title = preg_replace('![^'.preg_quote($separator).'\pL\pN\s]+!u', '', mb_strtolower($title));
+
+        // Replace all separator characters and whitespace by a single separator
+        $title = preg_replace('!['.preg_quote($separator).'\s]+!u', $separator, $title);
+
+        return trim($title, $separator);
+    }
+
+    /**
+     * Convert a string to snake case.
+     */
+    public static function snake(string $value, string $delimiter = '_'): string
+    {
+        $key = $value;
+
+        if (isset(static::$snakeCache[$key][$delimiter])) {
+            return static::$snakeCache[$key][$delimiter];
+        }
+
+        if (!ctype_lower($value)) {
+            $value = preg_replace('/\s+/u', '', ucwords($value));
+
+            $value = static::lower(preg_replace('/(.)(?=[A-Z])/u', '$1'.$delimiter, $value));
+        }
+
+        return static::$snakeCache[$key][$delimiter] = $value;
+    }
+
+    /**
+     * Determine if a given string starts with a given substring.
+     *
+     * @param string|array $needles
+     */
+    public static function startsWith(string $haystack, $needles): bool
+    {
+        foreach ((array) $needles as $needle) {
+            if ('' !== $needle && substr($haystack, 0, strlen($needle)) === (string) $needle) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Convert a value to studly caps case.
+     */
+    public static function studly(string $value): string
+    {
+        $key = $value;
+
+        if (isset(static::$studlyCache[$key])) {
+            return static::$studlyCache[$key];
+        }
+
+        $value = ucwords(str_replace(['-', '_'], ' ', $value));
+
+        return static::$studlyCache[$key] = str_replace(' ', '', $value);
+    }
+
+    /**
+     * Returns the portion of string specified by the start and length parameters.
+     */
+    public static function substr(string $string, int $start, ?int $length = null): string
+    {
+        return mb_substr($string, $start, $length, 'UTF-8');
+    }
+
+    /**
+     * Make a string's first character uppercase.
+     */
+    public static function ucfirst(string $string): string
+    {
+        return static::upper(static::substr($string, 0, 1)).static::substr($string, 1);
+    }
+
+    /**
+     * Convert string's encoding.
+     *
+     * @author yansongda <me@yansonga.cn>
+     */
+    public static function encoding(string $string, string $to = 'utf-8', string $from = 'gb2312'): string
+    {
+        return mb_convert_encoding($string, $to, $from);
+    }
+
+    /**
+     * Returns the replacements for the ascii method.
+     *
+     * Note: Adapted from Stringy\Stringy.
+     *
+     * @see https://github.com/danielstjules/Stringy/blob/3.1.0/LICENSE.txt
+     */
+    protected static function charsArray(): array
+    {
+        static $charsArray;
+
+        if (isset($charsArray)) {
+            return $charsArray;
+        }
+
+        return $charsArray = [
+            '0' => ['°', '₀', '۰', '0'],
+            '1' => ['¹', '₁', '۱', '1'],
+            '2' => ['²', '₂', '۲', '2'],
+            '3' => ['³', '₃', '۳', '3'],
+            '4' => ['⁴', '₄', '۴', '٤', '4'],
+            '5' => ['⁵', '₅', '۵', '٥', '5'],
+            '6' => ['⁶', '₆', '۶', '٦', '6'],
+            '7' => ['⁷', '₇', '۷', '7'],
+            '8' => ['⁸', '₈', '۸', '8'],
+            '9' => ['⁹', '₉', '۹', '9'],
+            'a' => ['à', 'á', 'ả', 'ã', 'ạ', 'ă', 'ắ', 'ằ', 'ẳ', 'ẵ', 'ặ', 'â', 'ấ', 'ầ', 'ẩ', 'ẫ', 'ậ', 'ā', 'ą', 'å', 'α', 'ά', 'ἀ', 'ἁ', 'ἂ', 'ἃ', 'ἄ', 'ἅ', 'ἆ', 'ἇ', 'ᾀ', 'ᾁ', 'ᾂ', 'ᾃ', 'ᾄ', 'ᾅ', 'ᾆ', 'ᾇ', 'ὰ', 'ά', 'ᾰ', 'ᾱ', 'ᾲ', 'ᾳ', 'ᾴ', 'ᾶ', 'ᾷ', 'а', 'أ', 'အ', 'ာ', 'ါ', 'ǻ', 'ǎ', 'ª', 'ა', 'अ', 'ا', 'a', 'ä'],
+            'b' => ['б', 'β', 'ب', 'ဗ', 'ბ', 'b'],
+            'c' => ['ç', 'ć', 'č', 'ĉ', 'ċ', 'c'],
+            'd' => ['ď', 'ð', 'đ', 'ƌ', 'ȡ', 'ɖ', 'ɗ', 'ᵭ', 'ᶁ', 'ᶑ', 'д', 'δ', 'د', 'ض', 'ဍ', 'ဒ', 'დ', 'd'],
+            'e' => ['é', 'è', 'ẻ', 'ẽ', 'ẹ', 'ê', 'ế', 'ề', 'ể', 'ễ', 'ệ', 'ë', 'ē', 'ę', 'ě', 'ĕ', 'ė', 'ε', 'έ', 'ἐ', 'ἑ', 'ἒ', 'ἓ', 'ἔ', 'ἕ', 'ὲ', 'έ', 'е', 'ё', 'э', 'є', 'ə', 'ဧ', 'ေ', 'ဲ', 'ე', 'ए', 'إ', 'ئ', 'e'],
+            'f' => ['ф', 'φ', 'ف', 'ƒ', 'ფ', 'f'],
+            'g' => ['ĝ', 'ğ', 'ġ', 'ģ', 'г', 'ґ', 'γ', 'ဂ', 'გ', 'گ', 'g'],
+            'h' => ['ĥ', 'ħ', 'η', 'ή', 'ح', 'ه', 'ဟ', 'ှ', 'ჰ', 'h'],
+            'i' => ['í', 'ì', 'ỉ', 'ĩ', 'ị', 'î', 'ï', 'ī', 'ĭ', 'į', 'ı', 'ι', 'ί', 'ϊ', 'ΐ', 'ἰ', 'ἱ', 'ἲ', 'ἳ', 'ἴ', 'ἵ', 'ἶ', 'ἷ', 'ὶ', 'ί', 'ῐ', 'ῑ', 'ῒ', 'ΐ', 'ῖ', 'ῗ', 'і', 'ї', 'и', 'ဣ', 'ိ', 'ီ', 'ည်', 'ǐ', 'ი', 'इ', 'ی', 'i'],
+            'j' => ['ĵ', 'ј', 'Ј', 'ჯ', 'ج', 'j'],
+            'k' => ['ķ', 'ĸ', 'к', 'κ', 'Ķ', 'ق', 'ك', 'က', 'კ', 'ქ', 'ک', 'k'],
+            'l' => ['ł', 'ľ', 'ĺ', 'ļ', 'ŀ', 'л', 'λ', 'ل', 'လ', 'ლ', 'l'],
+            'm' => ['м', 'μ', 'م', 'မ', 'მ', 'm'],
+            'n' => ['ñ', 'ń', 'ň', 'ņ', 'ʼn', 'ŋ', 'ν', 'н', 'ن', 'န', 'ნ', 'n'],
+            'o' => ['ó', 'ò', 'ỏ', 'õ', 'ọ', 'ô', 'ố', 'ồ', 'ổ', 'ỗ', 'ộ', 'ơ', 'ớ', 'ờ', 'ở', 'ỡ', 'ợ', 'ø', 'ō', 'ő', 'ŏ', 'ο', 'ὀ', 'ὁ', 'ὂ', 'ὃ', 'ὄ', 'ὅ', 'ὸ', 'ό', 'о', 'و', 'θ', 'ို', 'ǒ', 'ǿ', 'º', 'ო', 'ओ', 'o', 'ö'],
+            'p' => ['п', 'π', 'ပ', 'პ', 'پ', 'p'],
+            'q' => ['ყ', 'q'],
+            'r' => ['ŕ', 'ř', 'ŗ', 'р', 'ρ', 'ر', 'რ', 'r'],
+            's' => ['ś', 'š', 'ş', 'с', 'σ', 'ș', 'ς', 'س', 'ص', 'စ', 'ſ', 'ს', 's'],
+            't' => ['ť', 'ţ', 'т', 'τ', 'ț', 'ت', 'ط', 'ဋ', 'တ', 'ŧ', 'თ', 'ტ', 't'],
+            'u' => ['ú', 'ù', 'ủ', 'ũ', 'ụ', 'ư', 'ứ', 'ừ', 'ử', 'ữ', 'ự', 'û', 'ū', 'ů', 'ű', 'ŭ', 'ų', 'µ', 'у', 'ဉ', 'ု', 'ူ', 'ǔ', 'ǖ', 'ǘ', 'ǚ', 'ǜ', 'უ', 'उ', 'u', 'ў', 'ü'],
+            'v' => ['в', 'ვ', 'ϐ', 'v'],
+            'w' => ['ŵ', 'ω', 'ώ', 'ဝ', 'ွ', 'w'],
+            'x' => ['χ', 'ξ', 'x'],
+            'y' => ['ý', 'ỳ', 'ỷ', 'ỹ', 'ỵ', 'ÿ', 'ŷ', 'й', 'ы', 'υ', 'ϋ', 'ύ', 'ΰ', 'ي', 'ယ', 'y'],
+            'z' => ['ź', 'ž', 'ż', 'з', 'ζ', 'ز', 'ဇ', 'ზ', 'z'],
+            'aa' => ['ع', 'आ', 'آ'],
+            'ae' => ['æ', 'ǽ'],
+            'ai' => ['ऐ'],
+            'ch' => ['ч', 'ჩ', 'ჭ', 'چ'],
+            'dj' => ['ђ', 'đ'],
+            'dz' => ['џ', 'ძ'],
+            'ei' => ['ऍ'],
+            'gh' => ['غ', 'ღ'],
+            'ii' => ['ई'],
+            'ij' => ['ij'],
+            'kh' => ['х', 'خ', 'ხ'],
+            'lj' => ['љ'],
+            'nj' => ['њ'],
+            'oe' => ['ö', 'œ', 'ؤ'],
+            'oi' => ['ऑ'],
+            'oii' => ['ऒ'],
+            'ps' => ['ψ'],
+            'sh' => ['ш', 'შ', 'ش'],
+            'shch' => ['щ'],
+            'ss' => ['ß'],
+            'sx' => ['ŝ'],
+            'th' => ['þ', 'ϑ', 'ث', 'ذ', 'ظ'],
+            'ts' => ['ц', 'ც', 'წ'],
+            'ue' => ['ü'],
+            'uu' => ['ऊ'],
+            'ya' => ['я'],
+            'yu' => ['ю'],
+            'zh' => ['ж', 'ჟ', 'ژ'],
+            '(c)' => ['©'],
+            'A' => ['Á', 'À', 'Ả', 'Ã', 'Ạ', 'Ă', 'Ắ', 'Ằ', 'Ẳ', 'Ẵ', 'Ặ', 'Â', 'Ấ', 'Ầ', 'Ẩ', 'Ẫ', 'Ậ', 'Å', 'Ā', 'Ą', 'Α', 'Ά', 'Ἀ', 'Ἁ', 'Ἂ', 'Ἃ', 'Ἄ', 'Ἅ', 'Ἆ', 'Ἇ', 'ᾈ', 'ᾉ', 'ᾊ', 'ᾋ', 'ᾌ', 'ᾍ', 'ᾎ', 'ᾏ', 'Ᾰ', 'Ᾱ', 'Ὰ', 'Ά', 'ᾼ', 'А', 'Ǻ', 'Ǎ', 'A', 'Ä'],
+            'B' => ['Б', 'Β', 'ब', 'B'],
+            'C' => ['Ç', 'Ć', 'Č', 'Ĉ', 'Ċ', 'C'],
+            'D' => ['Ď', 'Ð', 'Đ', 'Ɖ', 'Ɗ', 'Ƌ', 'ᴅ', 'ᴆ', 'Д', 'Δ', 'D'],
+            'E' => ['É', 'È', 'Ẻ', 'Ẽ', 'Ẹ', 'Ê', 'Ế', 'Ề', 'Ể', 'Ễ', 'Ệ', 'Ë', 'Ē', 'Ę', 'Ě', 'Ĕ', 'Ė', 'Ε', 'Έ', 'Ἐ', 'Ἑ', 'Ἒ', 'Ἓ', 'Ἔ', 'Ἕ', 'Έ', 'Ὲ', 'Е', 'Ё', 'Э', 'Є', 'Ə', 'E'],
+            'F' => ['Ф', 'Φ', 'F'],
+            'G' => ['Ğ', 'Ġ', 'Ģ', 'Г', 'Ґ', 'Γ', 'G'],
+            'H' => ['Η', 'Ή', 'Ħ', 'H'],
+            'I' => ['Í', 'Ì', 'Ỉ', 'Ĩ', 'Ị', 'Î', 'Ï', 'Ī', 'Ĭ', 'Į', 'İ', 'Ι', 'Ί', 'Ϊ', 'Ἰ', 'Ἱ', 'Ἳ', 'Ἴ', 'Ἵ', 'Ἶ', 'Ἷ', 'Ῐ', 'Ῑ', 'Ὶ', 'Ί', 'И', 'І', 'Ї', 'Ǐ', 'ϒ', 'I'],
+            'J' => ['J'],
+            'K' => ['К', 'Κ', 'K'],
+            'L' => ['Ĺ', 'Ł', 'Л', 'Λ', 'Ļ', 'Ľ', 'Ŀ', 'ल', 'L'],
+            'M' => ['М', 'Μ', 'M'],
+            'N' => ['Ń', 'Ñ', 'Ň', 'Ņ', 'Ŋ', 'Н', 'Ν', 'N'],
+            'O' => ['Ó', 'Ò', 'Ỏ', 'Õ', 'Ọ', 'Ô', 'Ố', 'Ồ', 'Ổ', 'Ỗ', 'Ộ', 'Ơ', 'Ớ', 'Ờ', 'Ở', 'Ỡ', 'Ợ', 'Ø', 'Ō', 'Ő', 'Ŏ', 'Ο', 'Ό', 'Ὀ', 'Ὁ', 'Ὂ', 'Ὃ', 'Ὄ', 'Ὅ', 'Ὸ', 'Ό', 'О', 'Θ', 'Ө', 'Ǒ', 'Ǿ', 'O', 'Ö'],
+            'P' => ['П', 'Π', 'P'],
+            'Q' => ['Q'],
+            'R' => ['Ř', 'Ŕ', 'Р', 'Ρ', 'Ŗ', 'R'],
+            'S' => ['Ş', 'Ŝ', 'Ș', 'Š', 'Ś', 'С', 'Σ', 'S'],
+            'T' => ['Ť', 'Ţ', 'Ŧ', 'Ț', 'Т', 'Τ', 'T'],
+            'U' => ['Ú', 'Ù', 'Ủ', 'Ũ', 'Ụ', 'Ư', 'Ứ', 'Ừ', 'Ử', 'Ữ', 'Ự', 'Û', 'Ū', 'Ů', 'Ű', 'Ŭ', 'Ų', 'У', 'Ǔ', 'Ǖ', 'Ǘ', 'Ǚ', 'Ǜ', 'U', 'Ў', 'Ü'],
+            'V' => ['В', 'V'],
+            'W' => ['Ω', 'Ώ', 'Ŵ', 'W'],
+            'X' => ['Χ', 'Ξ', 'X'],
+            'Y' => ['Ý', 'Ỳ', 'Ỷ', 'Ỹ', 'Ỵ', 'Ÿ', 'Ῠ', 'Ῡ', 'Ὺ', 'Ύ', 'Ы', 'Й', 'Υ', 'Ϋ', 'Ŷ', 'Y'],
+            'Z' => ['Ź', 'Ž', 'Ż', 'З', 'Ζ', 'Z'],
+            'AE' => ['Æ', 'Ǽ'],
+            'Ch' => ['Ч'],
+            'Dj' => ['Ђ'],
+            'Dz' => ['Џ'],
+            'Gx' => ['Ĝ'],
+            'Hx' => ['Ĥ'],
+            'Ij' => ['IJ'],
+            'Jx' => ['Ĵ'],
+            'Kh' => ['Х'],
+            'Lj' => ['Љ'],
+            'Nj' => ['Њ'],
+            'Oe' => ['Œ'],
+            'Ps' => ['Ψ'],
+            'Sh' => ['Ш'],
+            'Shch' => ['Щ'],
+            'Ss' => ['ẞ'],
+            'Th' => ['Þ'],
+            'Ts' => ['Ц'],
+            'Ya' => ['Я'],
+            'Yu' => ['Ю'],
+            'Zh' => ['Ж'],
+            ' ' => ["\xC2\xA0", "\xE2\x80\x80", "\xE2\x80\x81", "\xE2\x80\x82", "\xE2\x80\x83", "\xE2\x80\x84", "\xE2\x80\x85", "\xE2\x80\x86", "\xE2\x80\x87", "\xE2\x80\x88", "\xE2\x80\x89", "\xE2\x80\x8A", "\xE2\x80\xAF", "\xE2\x81\x9F", "\xE3\x80\x80", "\xEF\xBE\xA0"],
+        ];
+    }
+
+    /**
+     * Returns the language specific replacements for the ascii method.
+     *
+     * Note: Adapted from Stringy\Stringy.
+     *
+     * @see https://github.com/danielstjules/Stringy/blob/3.1.0/LICENSE.txt
+     */
+    protected static function languageSpecificCharsArray(string $language): ?array
+    {
+        static $languageSpecific;
+        if (!isset($languageSpecific)) {
+            $languageSpecific = [
+                'bg' => [
+                    ['х', 'Х', 'щ', 'Щ', 'ъ', 'Ъ', 'ь', 'Ь'],
+                    ['h', 'H', 'sht', 'SHT', 'a', 'А', 'y', 'Y'],
+                ],
+                'de' => [
+                    ['ä',  'ö',  'ü',  'Ä',  'Ö',  'Ü'],
+                    ['ae', 'oe', 'ue', 'AE', 'OE', 'UE'],
+                ],
+            ];
+        }
+
+        return isset($languageSpecific[$language]) ? $languageSpecific[$language] : null;
+    }
+}

+ 142 - 0
vendor/yansongda/supports/src/Traits/Accessable.php

@@ -0,0 +1,142 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Yansongda\Supports\Traits;
+
+trait Accessable
+{
+    /**
+     * __get.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return mixed
+     */
+    public function __get(string $key)
+    {
+        return $this->get($key);
+    }
+
+    /**
+     * __set.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param mixed $value
+     *
+     * @return mixed
+     */
+    public function __set(string $key, $value)
+    {
+        return $this->set($key, $value);
+    }
+
+    /**
+     * get.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param mixed $default
+     *
+     * @return mixed
+     */
+    public function get(?string $key = null, $default = null)
+    {
+        if (is_null($key)) {
+            return method_exists($this, 'toArray') ? $this->toArray() : $default;
+        }
+
+        $method = 'get';
+        foreach (explode('_', $key) as $item) {
+            $method .= ucfirst($item);
+        }
+
+        if (method_exists($this, $method)) {
+            return $this->{$method}();
+        }
+
+        return $default;
+    }
+
+    /**
+     * set.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param mixed $value
+     *
+     * @return $this
+     */
+    public function set(string $key, $value)
+    {
+        $method = 'set';
+        foreach (explode('_', $key) as $item) {
+            $method .= ucfirst($item);
+        }
+
+        if (method_exists($this, $method)) {
+            return $this->{$method}($value);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Whether a offset exists.
+     *
+     * @see https://php.net/manual/en/arrayaccess.offsetexists.php
+     *
+     * @param mixed $offset an offset to check for
+     *
+     * @return bool true on success or false on failure.
+     *
+     * The return value will be casted to boolean if non-boolean was returned.
+     */
+    public function offsetExists($offset)
+    {
+        return !is_null($this->get($offset));
+    }
+
+    /**
+     * Offset to retrieve.
+     *
+     * @see https://php.net/manual/en/arrayaccess.offsetget.php
+     *
+     * @param mixed $offset the offset to retrieve
+     *
+     * @return mixed can return all value types
+     */
+    public function offsetGet($offset)
+    {
+        return $this->get($offset);
+    }
+
+    /**
+     * Offset to set.
+     *
+     * @see https://php.net/manual/en/arrayaccess.offsetset.php
+     *
+     * @param mixed $offset the offset to assign the value to
+     * @param mixed $value  the value to set
+     *
+     * @return void
+     */
+    public function offsetSet($offset, $value)
+    {
+        $this->set($offset, $value);
+    }
+
+    /**
+     * Offset to unset.
+     *
+     * @see https://php.net/manual/en/arrayaccess.offsetunset.php
+     *
+     * @param mixed $offset the offset to unset
+     *
+     * @return void
+     */
+    public function offsetUnset($offset)
+    {
+    }
+}

+ 32 - 0
vendor/yansongda/supports/src/Traits/Arrayable.php

@@ -0,0 +1,32 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Yansongda\Supports\Traits;
+
+use ReflectionClass;
+use Yansongda\Supports\Str;
+
+trait Arrayable
+{
+    /**
+     * toArray.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @throws \ReflectionException
+     */
+    public function toArray(): array
+    {
+        $result = [];
+
+        foreach ((new ReflectionClass($this))->getProperties() as $item) {
+            $k = $item->getName();
+            $method = 'get'.Str::studly($k);
+
+            $result[Str::snake($k)] = method_exists($this, $method) ? $this->{$method}() : $this->{$k};
+        }
+
+        return $result;
+    }
+}

+ 229 - 0
vendor/yansongda/supports/src/Traits/HasHttpRequest.php

@@ -0,0 +1,229 @@
+<?php
+
+namespace Yansongda\Supports\Traits;
+
+use GuzzleHttp\Client;
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * Trait HasHttpRequest.
+ *
+ * @property string $baseUri
+ * @property float  $timeout
+ * @property float  $connectTimeout
+ */
+trait HasHttpRequest
+{
+    /**
+     * Http client.
+     *
+     * @var Client|null
+     */
+    protected $httpClient = null;
+
+    /**
+     * Http client options.
+     *
+     * @var array
+     */
+    protected $httpOptions = [];
+
+    /**
+     * Send a GET request.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return array|string
+     */
+    public function get(string $endpoint, array $query = [], array $headers = [])
+    {
+        return $this->request('get', $endpoint, [
+            'headers' => $headers,
+            'query' => $query,
+        ]);
+    }
+
+    /**
+     * Send a POST request.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string|array $data
+     *
+     * @return array|string
+     */
+    public function post(string $endpoint, $data, array $options = [])
+    {
+        if (!is_array($data)) {
+            $options['body'] = $data;
+        } else {
+            $options['form_params'] = $data;
+        }
+
+        return $this->request('post', $endpoint, $options);
+    }
+
+    /**
+     * Send request.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return array|string
+     */
+    public function request(string $method, string $endpoint, array $options = [])
+    {
+        return $this->unwrapResponse($this->getHttpClient()->{$method}($endpoint, $options));
+    }
+
+    /**
+     * Set http client.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return $this
+     */
+    public function setHttpClient(Client $client): self
+    {
+        $this->httpClient = $client;
+
+        return $this;
+    }
+
+    /**
+     * Return http client.
+     */
+    public function getHttpClient(): Client
+    {
+        if (is_null($this->httpClient)) {
+            $this->httpClient = $this->getDefaultHttpClient();
+        }
+
+        return $this->httpClient;
+    }
+
+    /**
+     * Get default http client.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function getDefaultHttpClient(): Client
+    {
+        return new Client($this->getOptions());
+    }
+
+    /**
+     * setBaseUri.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return $this
+     */
+    public function setBaseUri(string $url): self
+    {
+        if (property_exists($this, 'baseUri')) {
+            $parsedUrl = parse_url($url);
+
+            $this->baseUri = ($parsedUrl['scheme'] ?? 'http').'://'.
+                $parsedUrl['host'].(isset($parsedUrl['port']) ? (':'.$parsedUrl['port']) : '');
+        }
+
+        return $this;
+    }
+
+    /**
+     * getBaseUri.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function getBaseUri(): string
+    {
+        return property_exists($this, 'baseUri') ? $this->baseUri : '';
+    }
+
+    public function getTimeout(): float
+    {
+        return property_exists($this, 'timeout') ? $this->timeout : 5.0;
+    }
+
+    public function setTimeout(float $timeout): self
+    {
+        if (property_exists($this, 'timeout')) {
+            $this->timeout = $timeout;
+        }
+
+        return $this;
+    }
+
+    public function getConnectTimeout(): float
+    {
+        return property_exists($this, 'connectTimeout') ? $this->connectTimeout : 3.0;
+    }
+
+    public function setConnectTimeout(float $connectTimeout): self
+    {
+        if (property_exists($this, 'connectTimeout')) {
+            $this->connectTimeout = $connectTimeout;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get default options.
+     *
+     * @author yansongda <me@yansongda.cn>
+     */
+    public function getOptions(): array
+    {
+        return array_merge([
+            'base_uri' => $this->getBaseUri(),
+            'timeout' => $this->getTimeout(),
+            'connect_timeout' => $this->getConnectTimeout(),
+        ], $this->getHttpOptions());
+    }
+
+    /**
+     * setOptions.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return $this
+     */
+    public function setOptions(array $options): self
+    {
+        return $this->setHttpOptions($options);
+    }
+
+    public function getHttpOptions(): array
+    {
+        return $this->httpOptions;
+    }
+
+    public function setHttpOptions(array $httpOptions): self
+    {
+        $this->httpOptions = $httpOptions;
+
+        return $this;
+    }
+
+    /**
+     * Convert response.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return array|string
+     */
+    public function unwrapResponse(ResponseInterface $response)
+    {
+        $contentType = $response->getHeaderLine('Content-Type');
+        $contents = $response->getBody()->getContents();
+
+        if (false !== stripos($contentType, 'json') || stripos($contentType, 'javascript')) {
+            return json_decode($contents, true);
+        } elseif (false !== stripos($contentType, 'xml')) {
+            return json_decode(json_encode(simplexml_load_string($contents, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true);
+        }
+
+        return $contents;
+    }
+}

+ 85 - 0
vendor/yansongda/supports/src/Traits/Serializable.php

@@ -0,0 +1,85 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Yansongda\Supports\Traits;
+
+use RuntimeException;
+
+trait Serializable
+{
+    /**
+     * toJson.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @return string
+     */
+    public function toJson()
+    {
+        return $this->serialize();
+    }
+
+    /**
+     * Specify data which should be serialized to JSON.
+     *
+     * @see   https://php.net/manual/en/jsonserializable.jsonserialize.php
+     *
+     * @return mixed data which can be serialized by <b>json_encode</b>,
+     *               which is a value of any type other than a resource
+     *
+     * @since 5.4.0
+     */
+    public function jsonSerialize()
+    {
+        if (method_exists($this, 'toArray')) {
+            return $this->toArray();
+        }
+
+        return [];
+    }
+
+    /**
+     * String representation of object.
+     *
+     * @see   https://php.net/manual/en/serializable.serialize.php
+     *
+     * @return string the string representation of the object or null
+     *
+     * @since 5.1.0
+     */
+    public function serialize()
+    {
+        if (method_exists($this, 'toArray')) {
+            return json_encode($this->toArray());
+        }
+
+        return json_encode([]);
+    }
+
+    /**
+     * Constructs the object.
+     *
+     * @see   https://php.net/manual/en/serializable.unserialize.php
+     *
+     * @param string $serialized <p>
+     *                           The string representation of the object.
+     *                           </p>
+     *
+     * @since 5.1.0
+     */
+    public function unserialize($serialized)
+    {
+        $data = json_decode($serialized, true);
+
+        if (JSON_ERROR_NONE !== json_last_error()) {
+            throw new RuntimeException('Invalid Json Format');
+        }
+
+        foreach ($data as $key => $item) {
+            if (method_exists($this, 'set')) {
+                $this->set($key, $item);
+            }
+        }
+    }
+}

+ 147 - 0
vendor/yansongda/supports/src/Traits/ShouldThrottle.php

@@ -0,0 +1,147 @@
+<?php
+
+namespace Yansongda\Supports\Traits;
+
+use Predis\Client;
+
+/**
+ * Trait ShouldThrottle.
+ *
+ * @property Client $redis
+ */
+trait ShouldThrottle
+{
+    /**
+     * _throttle.
+     *
+     * @var array
+     */
+    protected $_throttle = [
+        'limit' => 60,
+        'period' => 60,
+        'count' => 0,
+        'reset_time' => 0,
+    ];
+
+    /**
+     * isThrottled.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $key
+     * @param int    $limit
+     * @param int    $period
+     * @param bool   $auto_add
+     *
+     * @return bool
+     */
+    public function isThrottled($key, $limit = 60, $period = 60, $auto_add = false)
+    {
+        if (-1 === $limit) {
+            return false;
+        }
+
+        $now = microtime(true) * 1000;
+
+        $this->redis->zremrangebyscore($key, 0, $now - $period * 1000);
+
+        $this->_throttle = [
+            'limit' => $limit,
+            'period' => $period,
+            'count' => $this->getThrottleCounts($key, $period),
+            'reset_time' => $this->getThrottleResetTime($key, $now),
+        ];
+
+        if ($this->_throttle['count'] < $limit) {
+            if ($auto_add) {
+                $this->throttleAdd($key, $period);
+            }
+
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * 限流 + 1.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $key
+     * @param int    $period
+     */
+    public function throttleAdd($key, $period = 60)
+    {
+        $now = microtime(true) * 1000;
+
+        $this->redis->zadd($key, [$now => $now]);
+        $this->redis->expire($key, $period * 2);
+    }
+
+    /**
+     * getResetTime.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param $key
+     * @param $now
+     *
+     * @return int
+     */
+    public function getThrottleResetTime($key, $now)
+    {
+        $data = $this->redis->zrangebyscore(
+            $key,
+            $now - $this->_throttle['period'] * 1000,
+            $now,
+            ['limit' => [0, 1]]
+        );
+
+        if (0 === count($data)) {
+            return $this->_throttle['reset_time'] = time() + $this->_throttle['period'];
+        }
+
+        return intval($data[0] / 1000) + $this->_throttle['period'];
+    }
+
+    /**
+     * 获取限流相关信息.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string|null $key
+     * @param mixed|null  $default
+     *
+     * @return array|null
+     */
+    public function getThrottleInfo($key = null, $default = null)
+    {
+        if (is_null($key)) {
+            return $this->_throttle;
+        }
+
+        if (isset($this->_throttle[$key])) {
+            return $this->_throttle[$key];
+        }
+
+        return $default;
+    }
+
+    /**
+     * 获取已使用次数.
+     *
+     * @author yansongda <me@yansongda.cn>
+     *
+     * @param string $key
+     * @param int    $period
+     *
+     * @return string
+     */
+    public function getThrottleCounts($key, $period = 60)
+    {
+        $now = microtime(true) * 1000;
+
+        return $this->redis->zcount($key, $now - $period * 1000, $now);
+    }
+}

Some files were not shown because too many files changed in this diff