diff --git a/assets/images/alipay-barcode-icon.svg b/assets/images/alipay-barcode-icon.svg
deleted file mode 100644
index 37eca398..00000000
--- a/assets/images/alipay-barcode-icon.svg
+++ /dev/null
@@ -1,202 +0,0 @@
-
\ No newline at end of file
diff --git a/assets/images/alipay-f2f-icon.svg b/assets/images/alipay-f2f-icon.svg
deleted file mode 100644
index cdb13020..00000000
--- a/assets/images/alipay-f2f-icon.svg
+++ /dev/null
@@ -1,93 +0,0 @@
-
\ No newline at end of file
diff --git a/assets/images/alipay-huabei-icon.svg b/assets/images/alipay-huabei-icon.svg
deleted file mode 100644
index a13fc87d..00000000
--- a/assets/images/alipay-huabei-icon.svg
+++ /dev/null
@@ -1,74 +0,0 @@
-
\ No newline at end of file
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 00000000..9a0f5de8
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,832 @@
+{
+ "_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": "524ca69e0ade104f4102231471f256e3",
+ "packages": [
+ {
+ "name": "adbario/php-dot-notation",
+ "version": "2.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/adbario/php-dot-notation.git",
+ "reference": "081e2cca50c84bfeeea2e3ef9b2c8d206d80ccae"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/adbario/php-dot-notation/zipball/081e2cca50c84bfeeea2e3ef9b2c8d206d80ccae",
+ "reference": "081e2cca50c84bfeeea2e3ef9b2c8d206d80ccae",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "php": "^5.5 || ^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8|^5.7|^6.6|^7.5|^8.5|^9.5",
+ "squizlabs/php_codesniffer": "^3.6"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/helpers.php"
+ ],
+ "psr-4": {
+ "Adbar\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Riku Särkinen",
+ "email": "riku@adbar.io"
+ }
+ ],
+ "description": "PHP dot notation access to arrays",
+ "homepage": "https://github.com/adbario/php-dot-notation",
+ "keywords": [
+ "ArrayAccess",
+ "dotnotation"
+ ],
+ "support": {
+ "issues": "https://github.com/adbario/php-dot-notation/issues",
+ "source": "https://github.com/adbario/php-dot-notation/tree/2.5.0"
+ },
+ "time": "2022-10-14T20:31:46+00:00"
+ },
+ {
+ "name": "alibabacloud/tea",
+ "version": "3.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/aliyun/tea-php.git",
+ "reference": "1619cb96c158384f72b873e1f85de8b299c9c367"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/aliyun/tea-php/zipball/1619cb96c158384f72b873e1f85de8b299c9c367",
+ "reference": "1619cb96c158384f72b873e1f85de8b299c9c367",
+ "shasum": ""
+ },
+ "require": {
+ "adbario/php-dot-notation": "^2.4",
+ "ext-curl": "*",
+ "ext-json": "*",
+ "ext-libxml": "*",
+ "ext-mbstring": "*",
+ "ext-openssl": "*",
+ "ext-simplexml": "*",
+ "ext-xmlwriter": "*",
+ "guzzlehttp/guzzle": "^6.3|^7.0",
+ "php": ">=5.5"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "*",
+ "symfony/dotenv": "^3.4",
+ "symfony/var-dumper": "^3.4"
+ },
+ "suggest": {
+ "ext-sockets": "To use client-side monitoring"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "AlibabaCloud\\Tea\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "Alibaba Cloud SDK",
+ "email": "sdk-team@alibabacloud.com",
+ "homepage": "http://www.alibabacloud.com"
+ }
+ ],
+ "description": "Client of Tea for PHP",
+ "homepage": "https://www.alibabacloud.com/",
+ "keywords": [
+ "alibabacloud",
+ "client",
+ "cloud",
+ "tea"
+ ],
+ "support": {
+ "issues": "https://github.com/aliyun/tea-php/issues",
+ "source": "https://github.com/aliyun/tea-php"
+ },
+ "time": "2023-05-16T06:43:41+00:00"
+ },
+ {
+ "name": "alibabacloud/tea-fileform",
+ "version": "0.3.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/alibabacloud-sdk-php/tea-fileform.git",
+ "reference": "4bf0c75a045c8115aa8cb1a394bd08d8bb833181"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/alibabacloud-sdk-php/tea-fileform/zipball/4bf0c75a045c8115aa8cb1a394bd08d8bb833181",
+ "reference": "4bf0c75a045c8115aa8cb1a394bd08d8bb833181",
+ "shasum": ""
+ },
+ "require": {
+ "alibabacloud/tea": "^3.0",
+ "php": ">5.5"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.35|^5.4.3"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "AlibabaCloud\\Tea\\FileForm\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "Alibaba Cloud SDK",
+ "email": "sdk-team@alibabacloud.com"
+ }
+ ],
+ "description": "Alibaba Cloud Tea File Library for PHP",
+ "support": {
+ "issues": "https://github.com/alibabacloud-sdk-php/tea-fileform/issues",
+ "source": "https://github.com/alibabacloud-sdk-php/tea-fileform/tree/0.3.4"
+ },
+ "time": "2020-12-01T07:24:35+00:00"
+ },
+ {
+ "name": "alipaysdk/easysdk",
+ "version": "2.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/alipay/alipay-easysdk.git",
+ "reference": "c6008839a22a5fca08e9f8536730f7abfed522d5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/alipay/alipay-easysdk/zipball/c6008839a22a5fca08e9f8536730f7abfed522d5",
+ "reference": "c6008839a22a5fca08e9f8536730f7abfed522d5",
+ "shasum": ""
+ },
+ "require": {
+ "alibabacloud/tea": "^3.1",
+ "alibabacloud/tea-fileform": "^0.3.2",
+ "ext-ctype": "*",
+ "ext-curl": "*",
+ "ext-dom": "*",
+ "ext-fileinfo": "*",
+ "ext-json": "*",
+ "ext-libxml": "*",
+ "ext-mbstring": "*",
+ "ext-openssl": "*",
+ "ext-simplexml": "*",
+ "ext-xmlwriter": "*",
+ "guzzlehttp/guzzle": ">=6.3",
+ "php": ">=7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^7.5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Alipay\\EasySDK\\": "php/src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "junying.wjy",
+ "email": "junying.wjy@antfin.com"
+ }
+ ],
+ "description": "支付宝官方 Alipay Easy SDK",
+ "support": {
+ "source": "https://github.com/alipay/alipay-easysdk/tree/v2.2.3"
+ },
+ "time": "2022-11-28T14:04:57+00:00"
+ },
+ {
+ "name": "guzzlehttp/guzzle",
+ "version": "7.10.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/guzzle.git",
+ "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4",
+ "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "guzzlehttp/promises": "^2.3",
+ "guzzlehttp/psr7": "^2.8",
+ "php": "^7.2.5 || ^8.0",
+ "psr/http-client": "^1.0",
+ "symfony/deprecation-contracts": "^2.2 || ^3.0"
+ },
+ "provide": {
+ "psr/http-client-implementation": "1.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "ext-curl": "*",
+ "guzzle/client-integration-tests": "3.0.2",
+ "php-http/message-factory": "^1.1",
+ "phpunit/phpunit": "^8.5.39 || ^9.6.20",
+ "psr/log": "^1.1 || ^2.0 || ^3.0"
+ },
+ "suggest": {
+ "ext-curl": "Required for CURL handler support",
+ "ext-intl": "Required for Internationalized Domain Name (IDN) support",
+ "psr/log": "Required for using the Log middleware"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/functions_include.php"
+ ],
+ "psr-4": {
+ "GuzzleHttp\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Jeremy Lindblom",
+ "email": "jeremeamia@gmail.com",
+ "homepage": "https://github.com/jeremeamia"
+ },
+ {
+ "name": "George Mponos",
+ "email": "gmponos@gmail.com",
+ "homepage": "https://github.com/gmponos"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://github.com/sagikazarmark"
+ },
+ {
+ "name": "Tobias Schultze",
+ "email": "webmaster@tubo-world.de",
+ "homepage": "https://github.com/Tobion"
+ }
+ ],
+ "description": "Guzzle is a PHP HTTP client library",
+ "keywords": [
+ "client",
+ "curl",
+ "framework",
+ "http",
+ "http client",
+ "psr-18",
+ "psr-7",
+ "rest",
+ "web service"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/guzzle/issues",
+ "source": "https://github.com/guzzle/guzzle/tree/7.10.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-23T22:36:01+00:00"
+ },
+ {
+ "name": "guzzlehttp/promises",
+ "version": "2.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/promises.git",
+ "reference": "481557b130ef3790cf82b713667b43030dc9c957"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957",
+ "reference": "481557b130ef3790cf82b713667b43030dc9c957",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "phpunit/phpunit": "^8.5.44 || ^9.6.25"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Promise\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ },
+ {
+ "name": "Tobias Schultze",
+ "email": "webmaster@tubo-world.de",
+ "homepage": "https://github.com/Tobion"
+ }
+ ],
+ "description": "Guzzle promises library",
+ "keywords": [
+ "promise"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/promises/issues",
+ "source": "https://github.com/guzzle/promises/tree/2.3.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-22T14:34:08+00:00"
+ },
+ {
+ "name": "guzzlehttp/psr7",
+ "version": "2.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/psr7.git",
+ "reference": "21dc724a0583619cd1652f673303492272778051"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051",
+ "reference": "21dc724a0583619cd1652f673303492272778051",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0",
+ "psr/http-factory": "^1.0",
+ "psr/http-message": "^1.1 || ^2.0",
+ "ralouphie/getallheaders": "^3.0"
+ },
+ "provide": {
+ "psr/http-factory-implementation": "1.0",
+ "psr/http-message-implementation": "1.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "http-interop/http-factory-tests": "0.9.0",
+ "phpunit/phpunit": "^8.5.44 || ^9.6.25"
+ },
+ "suggest": {
+ "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Psr7\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "George Mponos",
+ "email": "gmponos@gmail.com",
+ "homepage": "https://github.com/gmponos"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://github.com/sagikazarmark"
+ },
+ {
+ "name": "Tobias Schultze",
+ "email": "webmaster@tubo-world.de",
+ "homepage": "https://github.com/Tobion"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://sagikazarmark.hu"
+ }
+ ],
+ "description": "PSR-7 message implementation that also provides common utility methods",
+ "keywords": [
+ "http",
+ "message",
+ "psr-7",
+ "request",
+ "response",
+ "stream",
+ "uri",
+ "url"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/psr7/issues",
+ "source": "https://github.com/guzzle/psr7/tree/2.8.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-23T21:21:41+00:00"
+ },
+ {
+ "name": "psr/http-client",
+ "version": "1.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-client.git",
+ "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
+ "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.0 || ^8.0",
+ "psr/http-message": "^1.0 || ^2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Client\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP clients",
+ "homepage": "https://github.com/php-fig/http-client",
+ "keywords": [
+ "http",
+ "http-client",
+ "psr",
+ "psr-18"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-client"
+ },
+ "time": "2023-09-23T14:17:50+00:00"
+ },
+ {
+ "name": "psr/http-factory",
+ "version": "1.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-factory.git",
+ "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
+ "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1",
+ "psr/http-message": "^1.0 || ^2.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": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
+ "keywords": [
+ "factory",
+ "http",
+ "message",
+ "psr",
+ "psr-17",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-factory"
+ },
+ "time": "2024-04-15T12:06:14+00:00"
+ },
+ {
+ "name": "psr/http-message",
+ "version": "2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-message.git",
+ "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
+ "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://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"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-message/tree/2.0"
+ },
+ "time": "2023-04-04T09:54:51+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": ""
+ },
+ "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.",
+ "support": {
+ "issues": "https://github.com/ralouphie/getallheaders/issues",
+ "source": "https://github.com/ralouphie/getallheaders/tree/develop"
+ },
+ "time": "2019-03-08T08:55:37+00:00"
+ },
+ {
+ "name": "symfony/deprecation-contracts",
+ "version": "v3.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/deprecation-contracts.git",
+ "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62",
+ "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.6-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "function.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": "A generic function and convention to trigger deprecation notices",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-25T14:21:43+00:00"
+ }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": {},
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": {},
+ "platform-dev": {},
+ "plugin-api-version": "2.6.0"
+}
diff --git a/css/alipay-facetopay.css b/css/alipay-facetopay.css
new file mode 100644
index 00000000..50972195
--- /dev/null
+++ b/css/alipay-facetopay.css
@@ -0,0 +1,201 @@
+/**
+ * 当面付支付样式
+ */
+
+.alipay-qrcode-container {
+ max-width: 500px;
+ margin: 30px auto;
+ padding: 30px;
+ background: #fff;
+ border-radius: 8px;
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
+ text-align: center;
+}
+
+.alipay-qrcode-header {
+ margin-bottom: 20px;
+}
+
+.alipay-qrcode-header h2 {
+ margin: 0 0 10px 0;
+ font-size: 24px;
+ color: #333;
+}
+
+.alipay-qrcode-header .order-info {
+ font-size: 16px;
+ color: #666;
+}
+
+.alipay-qrcode-header .order-amount {
+ font-size: 32px;
+ font-weight: 700;
+ color: #1677ff;
+ margin: 15px 0;
+}
+
+.alipay-qrcode-wrapper {
+ margin: 20px 0;
+ padding: 20px;
+ background: #f9f9f9;
+ border-radius: 8px;
+ display: inline-block;
+}
+
+#alipay-qrcode {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+#alipay-qrcode canvas,
+#alipay-qrcode img {
+ max-width: 100%;
+ height: auto;
+ border: 8px solid #fff;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ border-radius: 4px;
+}
+
+.alipay-qrcode-tips {
+ margin-top: 20px;
+ padding: 15px;
+ background: #f0f7ff;
+ border: 1px solid #d6e4ff;
+ border-radius: 4px;
+ color: #096dd9;
+}
+
+.alipay-qrcode-tips p {
+ margin: 8px 0;
+ font-size: 14px;
+ line-height: 1.6;
+}
+
+.alipay-qrcode-tips .tip-icon {
+ display: inline-block;
+ margin-right: 5px;
+ font-weight: bold;
+}
+
+.alipay-payment-status {
+ margin-top: 20px;
+ padding: 15px;
+ background: #fff9e6;
+ border: 1px solid #ffd666;
+ border-radius: 4px;
+}
+
+.alipay-payment-status.success {
+ background: #f6ffed;
+ border-color: #b7eb8f;
+ color: #52c41a;
+}
+
+.alipay-payment-status.error {
+ background: #fff1f0;
+ border-color: #ffa39e;
+ color: #ff4d4f;
+}
+
+.alipay-payment-status .status-icon {
+ display: inline-block;
+ margin-right: 8px;
+ font-size: 18px;
+}
+
+.alipay-payment-status .status-text {
+ font-size: 15px;
+ font-weight: 500;
+}
+
+.alipay-qrcode-loading {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ min-height: 300px;
+}
+
+.alipay-qrcode-loading .spinner {
+ width: 50px;
+ height: 50px;
+ border: 4px solid #f0f0f0;
+ border-top: 4px solid #1677ff;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+.alipay-qrcode-loading .loading-text {
+ margin-top: 15px;
+ font-size: 14px;
+ color: #666;
+}
+
+.alipay-qrcode-expired {
+ padding: 30px;
+ text-align: center;
+}
+
+.alipay-qrcode-expired .expired-icon {
+ font-size: 48px;
+ color: #ff4d4f;
+ margin-bottom: 15px;
+}
+
+.alipay-qrcode-expired .expired-text {
+ font-size: 16px;
+ color: #666;
+ margin-bottom: 20px;
+}
+
+.alipay-qrcode-expired .refresh-button {
+ padding: 10px 30px;
+ background: #1677ff;
+ color: #fff;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 15px;
+ transition: background 0.3s;
+}
+
+.alipay-qrcode-expired .refresh-button:hover {
+ background: #4096ff;
+}
+
+.alipay-qrcode-timer {
+ margin-top: 15px;
+ font-size: 14px;
+ color: #999;
+}
+
+.alipay-qrcode-timer.warning {
+ color: #ff4d4f;
+ font-weight: 600;
+}
+
+/* 移动端适配 */
+@media (max-width: 768px) {
+ .alipay-qrcode-container {
+ margin: 15px;
+ padding: 20px;
+ }
+
+ .alipay-qrcode-header h2 {
+ font-size: 20px;
+ }
+
+ .alipay-qrcode-header .order-amount {
+ font-size: 28px;
+ }
+
+ .alipay-qrcode-wrapper {
+ padding: 15px;
+ }
+}
diff --git a/css/alipay-installment.css b/css/alipay-installment.css
new file mode 100644
index 00000000..10f87769
--- /dev/null
+++ b/css/alipay-installment.css
@@ -0,0 +1,120 @@
+/**
+ * 花呗分期支付样式
+ */
+
+.alipay-installment-selector {
+ margin: 15px 0;
+ padding: 15px;
+ background: #f9f9f9;
+ border: 1px solid #e0e0e0;
+ border-radius: 4px;
+}
+
+.alipay-installment-selector p {
+ margin: 0 0 10px 0;
+ font-weight: 600;
+ color: #333;
+}
+
+.installment-periods {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ gap: 10px;
+}
+
+.installment-periods li {
+ margin: 0;
+ padding: 0;
+}
+
+.installment-periods label {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 12px 15px;
+ background: #fff;
+ border: 2px solid #ddd;
+ border-radius: 4px;
+ cursor: pointer;
+ transition: all 0.3s ease;
+}
+
+.installment-periods label:hover {
+ border-color: #1677ff;
+ background: #f0f7ff;
+}
+
+.installment-periods input[type="radio"] {
+ margin-right: 10px;
+ width: 18px;
+ height: 18px;
+}
+
+.installment-periods input[type="radio"]:checked + .period-label {
+ color: #1677ff;
+ font-weight: 600;
+}
+
+.installment-periods label:has(input:checked) {
+ border-color: #1677ff;
+ background: #f0f7ff;
+ box-shadow: 0 0 0 1px #1677ff;
+}
+
+.period-label {
+ flex: 1;
+ font-size: 15px;
+ font-weight: 500;
+ color: #333;
+}
+
+.period-amount {
+ font-size: 14px;
+ color: #666;
+ font-weight: 500;
+}
+
+.alipay-installment-notice {
+ padding: 10px 15px;
+ background: #fff3cd;
+ border: 1px solid #ffc107;
+ border-radius: 4px;
+ color: #856404;
+ margin: 10px 0;
+}
+
+.alipay-badge {
+ display: inline-block;
+ padding: 0 6px;
+ line-height: 18px;
+ height: 18px;
+ border-radius: 9px;
+ font-size: 12px;
+ font-weight: 600;
+ margin-left: 6px;
+}
+
+.alipay-badge--free {
+ background: #e8f5e9;
+ color: #2e7d32;
+ border: 1px solid #a5d6a7;
+}
+
+/* 移动端适配 */
+@media (max-width: 768px) {
+ .installment-periods {
+ grid-template-columns: 1fr;
+ }
+
+ .installment-periods label {
+ padding: 10px 12px;
+ }
+
+ .period-label,
+ .period-amount {
+ font-size: 14px;
+ }
+}
diff --git a/enable-gateways.php b/enable-gateways.php
deleted file mode 100644
index 2fb1a5e4..00000000
--- a/enable-gateways.php
+++ /dev/null
@@ -1,30 +0,0 @@
-✓ 完成!
所有支付网关已启用。
查看支付设置
' );
-}, 1 );
diff --git a/inc/class-wc-alipay-agreement.php b/inc/class-wc-alipay-agreement.php
deleted file mode 100644
index d8f9381a..00000000
--- a/inc/class-wc-alipay-agreement.php
+++ /dev/null
@@ -1,140 +0,0 @@
-api_request_url( 'WC_Alipay_Agreement_Notify' ) );
- $return_url = add_query_arg( [], WC()->api_request_url( 'WC_Alipay_Agreement_Return' ) );
-
- require_once WOO_ALIPAY_PLUGIN_PATH . 'lib/alipay/aop/AopClient.php';
- require_once WOO_ALIPAY_PLUGIN_PATH . 'lib/alipay/aop/request/AlipayUserAgreementPageSignRequest.php';
-
- $aop = new AopClient();
- $aop->gatewayUrl = ( 'yes' === $gateway->get_option( 'sandbox' ) ) ? WC_Alipay::GATEWAY_SANDBOX_URL : WC_Alipay::GATEWAY_URL;
- $aop->appId = $gateway->get_option( 'appid' );
- $aop->rsaPrivateKey = $gateway->get_option( 'private_key' );
- $aop->alipayrsaPublicKey= $gateway->get_option( 'public_key' );
- $aop->charset = 'utf-8';
- $aop->signType = 'RSA2';
-
- $external_sign_no = 'AGREE' . ( $user_id ? $user_id : get_current_user_id() ) . '-' . current_time( 'timestamp' );
- // 建立 external_sign_no 到用户的临时映射
- if ( $user_id ) {
- set_transient( 'woo_alipay_pending_user_' . $external_sign_no, absint( $user_id ), DAY_IN_SECONDS );
- update_user_meta( $user_id, self::META_USER_PENDING_EXTERNAL_SIGN_NO, $external_sign_no );
- }
-
- $personal_product_code = apply_filters( 'woo_alipay_agreement_personal_product_code', 'CYCLE_PAY_AUTH', $user_id );
- $sign_scene = apply_filters( 'woo_alipay_agreement_sign_scene', 'INDUSTRY|DEFAULT', $user_id );
-
- $biz = [
- 'external_sign_no' => $external_sign_no,
- 'personal_product_code' => $personal_product_code,
- 'sign_scene' => $sign_scene,
- 'access_params' => [ 'channel' => 'ALIPAYAPP' ],
- 'merchant_process_url' => $return_url,
- 'notify_url' => $notify_url,
- ];
- if ( ! empty( $context['return_url'] ) ) {
- $biz['merchant_process_url'] = $context['return_url'];
- }
-
- $request = new AlipayUserAgreementPageSignRequest();
- $request->setBizContent( wp_json_encode( $biz ) );
- $request->setNotifyUrl( $notify_url );
- $request->setReturnUrl( $return_url );
-
- // pageExecute 返回跳转 URL/表单
- $sign_form_or_url = $aop->pageExecute( $request );
- return [ 'payload' => $sign_form_or_url, 'external_sign_no' => $external_sign_no ];
- }
-
- public static function handle_agreement_return() {
- // 用户浏览器跳转回站点,可用于展示成功页面;协议最终以异步通知为准
- wp_safe_redirect( home_url() );
- exit;
- }
-
- public static function handle_agreement_notify() {
- $gateway = new WC_Alipay( false );
- require_once WOO_ALIPAY_PLUGIN_PATH . 'lib/alipay/aop/AopClient.php';
- $aop = new AopClient();
- $aop->alipayrsaPublicKey= $gateway->get_option( 'public_key' );
- $aop->charset = 'utf-8';
- $aop->signType = 'RSA2';
-
- $params = wp_unslash( $_POST );
- $verified = $aop->rsaCheckV1( $params, $aop->alipayrsaPublicKey, $aop->signType );
- if ( ! $verified ) {
- echo 'fail';
- exit;
- }
-
- $agreement_no = isset( $params['agreement_no'] ) ? sanitize_text_field( $params['agreement_no'] ) : '';
- $alipay_user_id = isset( $params['alipay_user_id'] ) ? sanitize_text_field( $params['alipay_user_id'] ) : '';
- $status = isset( $params['status'] ) ? sanitize_text_field( $params['status'] ) : '';
- $external_sign_no = isset( $params['external_sign_no'] ) ? sanitize_text_field( $params['external_sign_no'] ) : '';
-
- // 根据 external_sign_no 还原用户
- $user_id = 0;
- if ( $external_sign_no ) {
- $user_id = absint( get_transient( 'woo_alipay_pending_user_' . $external_sign_no ) );
- }
- if ( ! $user_id && is_user_logged_in() ) {
- $user_id = get_current_user_id();
- }
- if ( $user_id ) {
- update_user_meta( $user_id, self::META_USER_AGREEMENT_NO, $agreement_no );
- update_user_meta( $user_id, self::META_USER_AGREEMENT_STATUS, $status );
- delete_transient( 'woo_alipay_pending_user_' . $external_sign_no );
- delete_user_meta( $user_id, self::META_USER_PENDING_EXTERNAL_SIGN_NO );
- }
-
- echo 'success';
- exit;
- }
-
- public static function get_user_agreement_no( $user_id ) {
- return get_user_meta( $user_id, self::META_USER_AGREEMENT_NO, true );
- }
-
- public static function has_user_agreement( $user_id ) {
- return (bool) self::get_user_agreement_no( $user_id );
- }
-
- public static function handle_agreement_start() {
- if ( ! is_user_logged_in() ) {
- wp_die( __( '请先登录后再进行签约授权。', 'woo-alipay' ) );
- }
- $user_id = get_current_user_id();
- $context = [];
- if ( isset( $_GET['return_url'] ) ) {
- $context['return_url'] = esc_url_raw( wp_unslash( $_GET['return_url'] ) );
- }
- $result = self::get_sign_url( $user_id, $context );
- if ( is_array( $result ) && ! empty( $result['payload'] ) ) {
- // 输出表单/HTML,通常将自动跳转到支付宝
- echo $result['payload'];
- exit;
- }
- wp_die( __( '无法生成签约请求,请稍后重试。', 'woo-alipay' ) );
- }
-
-}
-
-WC_Alipay_Agreement::init();
diff --git a/inc/class-wc-alipay-facetopay-blocks-support.php b/inc/class-wc-alipay-facetopay-blocks-support.php
new file mode 100644
index 00000000..b5ea026b
--- /dev/null
+++ b/inc/class-wc-alipay-facetopay-blocks-support.php
@@ -0,0 +1,71 @@
+name = 'alipay_facetopay';
+ }
+
+ public function initialize() {
+ $this->settings = get_option( 'woocommerce_alipay_facetopay_settings', array() );
+
+ $gateways = WC()->payment_gateways->payment_gateways();
+ $this->gateway = isset( $gateways['alipay_facetopay'] ) ? $gateways['alipay_facetopay'] : false;
+ }
+
+ public function is_active() {
+ $enabled = isset( $this->settings['enabled'] ) ? $this->settings['enabled'] : 'no';
+ return 'yes' === $enabled;
+ }
+
+ public function get_payment_method_script_handles() {
+ $script_path = 'js/frontend/blocks-facetopay.js';
+ $script_asset_path = WOO_ALIPAY_PLUGIN_PATH . 'js/frontend/blocks-facetopay.asset.php';
+ $script_asset = file_exists( $script_asset_path )
+ ? require( $script_asset_path )
+ : array(
+ 'dependencies' => array( 'wc-blocks-registry', 'wp-element', 'wp-html-entities', 'wp-i18n' ),
+ 'version' => WOO_ALIPAY_VERSION
+ );
+ $script_url = trailingslashit( WOO_ALIPAY_PLUGIN_URL ) . $script_path;
+
+ wp_register_script(
+ 'wc-alipay-facetopay-payments-blocks',
+ $script_url,
+ $script_asset['dependencies'],
+ $script_asset['version'],
+ true
+ );
+
+ if ( function_exists( 'wp_set_script_translations' ) ) {
+ wp_set_script_translations( 'wc-alipay-facetopay-payments-blocks', 'woo-alipay', WOO_ALIPAY_PLUGIN_PATH . 'languages' );
+ }
+
+ return [ 'wc-alipay-facetopay-payments-blocks' ];
+ }
+
+ public function get_payment_method_script_handles_for_admin() {
+ return $this->get_payment_method_script_handles();
+ }
+
+ public function get_payment_method_data() {
+ return [
+ 'title' => $this->get_setting( 'title', '支付宝扫码支付' ),
+ 'description' => $this->get_setting( 'description', '使用支付宝扫描二维码完成支付' ),
+ 'supports' => $this->get_supported_features(),
+ 'icon' => WOO_ALIPAY_PLUGIN_URL . 'assets/images/alipay-icon.svg',
+ ];
+ }
+
+ public function get_supported_features() {
+ return $this->gateway ? $this->gateway->supports : ['products'];
+ }
+}
diff --git a/inc/class-wc-alipay-facetopay.php b/inc/class-wc-alipay-facetopay.php
new file mode 100644
index 00000000..3aee2ef7
--- /dev/null
+++ b/inc/class-wc-alipay-facetopay.php
@@ -0,0 +1,568 @@
+id = self::GATEWAY_ID;
+ $this->method_title = __('支付宝当面付', 'woo-alipay');
+ $this->method_description = __('支持当面付扫码支付,商家展示二维码供用户扫描。适用于收银台、实体店等场景。需要在支付宝商户后台开通当面付功能。', 'woo-alipay');
+ $this->icon = WOO_ALIPAY_PLUGIN_URL . 'assets/images/alipay-icon.svg';
+ $this->has_fields = false;
+ $this->charset = strtolower(get_bloginfo('charset'));
+
+ if (!in_array($this->charset, array('gbk', 'utf-8'), true)) {
+ $this->charset = 'utf-8';
+ }
+
+ $this->init_form_fields();
+ $this->init_settings();
+
+ $this->title = $this->get_option('title', __('支付宝扫码支付', 'woo-alipay'));
+ $this->description = $this->get_option('description');
+ $this->enabled = $this->get_option('enabled');
+ $this->current_currency = get_option('woocommerce_currency');
+ $this->exchange_rate = $this->get_option('exchange_rate');
+ $this->order_prefix = $this->get_option('order_prefix', 'F2F');
+ $this->notify_url = WC()->api_request_url('WC_Alipay_FaceToPay');
+
+ self::$log_enabled = ('yes' === $this->get_option('debug', 'no'));
+
+ $this->supports = array(
+ 'products',
+ 'refunds',
+ );
+
+ add_action('woocommerce_update_options_payment_gateways_' . $this->id, array($this, 'process_admin_options'));
+ add_action('woocommerce_receipt_' . $this->id, array($this, 'receipt_page'));
+ add_action('woocommerce_api_wc_alipay_facetopay', array($this, 'check_alipay_response'));
+ add_action('wp_ajax_alipay_facetopay_query', array($this, 'ajax_query_payment_status'));
+ add_action('wp_ajax_nopriv_alipay_facetopay_query', array($this, 'ajax_query_payment_status'));
+ add_action('wp_ajax_alipay_facetopay_refresh_qrcode', array($this, 'ajax_refresh_qrcode'));
+ add_action('wp_ajax_nopriv_alipay_facetopay_refresh_qrcode', array($this, 'ajax_refresh_qrcode'));
+ add_action('wp_enqueue_scripts', array($this, 'payment_scripts'));
+ }
+
+ public function init_form_fields()
+ {
+ $this->form_fields = array(
+ 'enabled' => array(
+ 'title' => __('启用/禁用', 'woo-alipay'),
+ 'type' => 'checkbox',
+ 'label' => __('启用支付宝当面付', 'woo-alipay'),
+ 'default' => 'no',
+ ),
+ 'title' => array(
+ 'title' => __('标题', 'woo-alipay'),
+ 'type' => 'text',
+ 'description' => __('用户在结账时看到的支付方式名称', 'woo-alipay'),
+ 'default' => __('支付宝扫码支付', 'woo-alipay'),
+ 'desc_tip' => true,
+ ),
+ 'description' => array(
+ 'title' => __('描述', 'woo-alipay'),
+ 'type' => 'textarea',
+ 'description' => __('支付方式描述', 'woo-alipay'),
+ 'default' => __('使用支付宝扫描二维码完成支付', 'woo-alipay'),
+ 'desc_tip' => true,
+ ),
+
+ 'qrcode_settings' => array(
+ 'title' => __('二维码设置', 'woo-alipay'),
+ 'type' => 'title',
+ ),
+ 'qrcode_size' => array(
+ 'title' => __('二维码尺寸', 'woo-alipay'),
+ 'type' => 'number',
+ 'description' => __('二维码显示尺寸(像素)', 'woo-alipay'),
+ 'default' => '300',
+ 'desc_tip' => true,
+ 'custom_attributes' => array(
+ 'min' => '100',
+ 'max' => '500',
+ 'step' => '10',
+ ),
+ ),
+ 'qrcode_timeout' => array(
+ 'title' => __('二维码有效期', 'woo-alipay'),
+ 'type' => 'select',
+ 'description' => __('二维码的有效时间', 'woo-alipay'),
+ 'default' => '120',
+ 'options' => array(
+ '60' => __('1分钟', 'woo-alipay'),
+ '120' => __('2分钟', 'woo-alipay'),
+ '180' => __('3分钟', 'woo-alipay'),
+ '300' => __('5分钟', 'woo-alipay'),
+ '600' => __('10分钟', 'woo-alipay'),
+ ),
+ 'desc_tip' => true,
+ ),
+
+ 'polling_settings' => array(
+ 'title' => __('轮询设置', 'woo-alipay'),
+ 'type' => 'title',
+ ),
+ 'polling_interval' => array(
+ 'title' => __('轮询间隔', 'woo-alipay'),
+ 'type' => 'number',
+ 'description' => __('查询支付状态的间隔时间(秒)', 'woo-alipay'),
+ 'default' => '2',
+ 'desc_tip' => true,
+ 'custom_attributes' => array(
+ 'min' => '1',
+ 'max' => '10',
+ 'step' => '1',
+ ),
+ ),
+
+ 'order_prefix' => array(
+ 'title' => __('订单号前缀', 'woo-alipay'),
+ 'type' => 'text',
+ 'description' => __('当面付订单号的前缀', 'woo-alipay'),
+ 'default' => 'F2F',
+ 'desc_tip' => true,
+ ),
+
+ 'debug' => array(
+ 'title' => __('调试日志', 'woo-alipay'),
+ 'type' => 'checkbox',
+ 'label' => __('启用日志记录', 'woo-alipay'),
+ 'default' => 'no',
+ 'description' => sprintf(
+ __('记录当面付相关日志到 %s', 'woo-alipay'),
+ '' . WC_Log_Handler_File::get_log_file_path($this->id) . ''
+ ),
+ ),
+ );
+
+ if (!in_array($this->current_currency, array('CNY', 'RMB'), true)) {
+ $this->form_fields['exchange_rate'] = array(
+ 'title' => __('汇率', 'woo-alipay'),
+ 'type' => 'number',
+ 'description' => sprintf(
+ __('设置 %s 与人民币的汇率', 'woo-alipay'),
+ $this->current_currency
+ ),
+ 'default' => '7.0',
+ 'desc_tip' => true,
+ 'custom_attributes' => array(
+ 'step' => '0.01',
+ 'min' => '0.01',
+ ),
+ );
+ }
+ }
+
+ public function payment_scripts()
+ {
+ if (!is_checkout() && !is_order_received_page()) {
+ return;
+ }
+
+ wp_enqueue_style(
+ 'alipay-facetopay',
+ WOO_ALIPAY_PLUGIN_URL . 'css/alipay-facetopay.css',
+ array(),
+ WOO_ALIPAY_VERSION
+ );
+
+ wp_enqueue_script(
+ 'qrcodejs',
+ 'https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js',
+ array(),
+ '1.0.0',
+ true
+ );
+
+ wp_enqueue_script(
+ 'alipay-facetopay',
+ WOO_ALIPAY_PLUGIN_URL . 'js/alipay-facetopay.js',
+ array('jquery', 'qrcodejs'),
+ WOO_ALIPAY_VERSION,
+ true
+ );
+
+ wp_localize_script('alipay-facetopay', 'alipayFaceToPayParams', array(
+ 'ajax_url' => admin_url('admin-ajax.php'),
+ 'polling_interval' => intval($this->get_option('polling_interval', 2)) * 1000,
+ 'timeout' => intval($this->get_option('qrcode_timeout', 120)),
+ ));
+ }
+
+ public function process_payment($order_id)
+ {
+ $order = wc_get_order($order_id);
+
+ $order->update_status('pending', __('等待扫码支付', 'woo-alipay'));
+ WC()->cart->empty_cart();
+
+ return array(
+ 'result' => 'success',
+ 'redirect' => $order->get_checkout_payment_url(true),
+ );
+ }
+
+ public function receipt_page($order_id)
+ {
+ $order = wc_get_order($order_id);
+
+ if (!$order || $order->is_paid()) {
+ return;
+ }
+
+ require_once WOO_ALIPAY_PLUGIN_PATH . 'inc/class-alipay-sdk-helper.php';
+
+ $main_gateway = new WC_Alipay(false);
+ $config = Alipay_SDK_Helper::get_alipay_config(array(
+ 'appid' => $main_gateway->get_option('appid'),
+ 'private_key' => $main_gateway->get_option('private_key'),
+ 'public_key' => $main_gateway->get_option('public_key'),
+ 'sandbox' => $main_gateway->get_option('sandbox'),
+ ));
+
+ $aop = Alipay_SDK_Helper::create_alipay_service($config);
+ if (!$aop) {
+ wc_add_notice(__('支付初始化失败', 'woo-alipay'), 'error');
+ return;
+ }
+
+ $total = $this->convert_to_rmb($order->get_total());
+ $out_trade_no = Alipay_SDK_Helper::generate_out_trade_no($order_id, $this->order_prefix);
+
+ $order->update_meta_data('_alipay_out_trade_no', $out_trade_no);
+ $order->save();
+
+ try {
+ require_once WOO_ALIPAY_PLUGIN_PATH . 'lib/alipay/aop/request/AlipayTradePrecreateRequest.php';
+
+ $request = new AlipayTradePrecreateRequest();
+
+ $biz_content = array(
+ 'out_trade_no' => $out_trade_no,
+ 'total_amount' => Alipay_SDK_Helper::format_amount($total),
+ 'subject' => $this->get_order_title($order),
+ 'body' => $this->get_order_description($order),
+ 'timeout_express' => $this->get_option('qrcode_timeout', 120) . 's',
+ );
+
+ $request->setBizContent(json_encode($biz_content));
+ $request->setNotifyUrl($this->notify_url);
+
+ $response = $aop->execute($request);
+ $response_node = 'alipay_trade_precreate_response';
+ $result = $response->$response_node;
+
+ if (isset($result->code) && $result->code === '10000') {
+ $qr_code = $result->qr_code;
+ $order->update_meta_data('_alipay_qrcode', $qr_code);
+ $order->save();
+
+ self::log('当面付二维码生成成功: ' . $out_trade_no);
+
+ $this->display_qrcode_page($order, $qr_code);
+ } else {
+ self::log('当面付二维码生成失败: ' . ($result->sub_msg ?? $result->msg), 'error');
+ wc_add_notice(__('生成支付二维码失败', 'woo-alipay'), 'error');
+ }
+
+ } catch (Exception $e) {
+ self::log('当面付异常: ' . $e->getMessage(), 'error');
+ wc_add_notice(__('支付请求失败', 'woo-alipay'), 'error');
+ }
+ }
+
+ protected function display_qrcode_page($order, $qr_code)
+ {
+ $qrcode_size = intval($this->get_option('qrcode_size', 300));
+ $timeout = intval($this->get_option('qrcode_timeout', 120));
+
+ include WOO_ALIPAY_PLUGIN_PATH . 'inc/templates/payment-qrcode.php';
+ }
+
+ public function ajax_refresh_qrcode()
+ {
+ check_ajax_referer('alipay_facetopay_query', 'nonce');
+ $order_id = intval($_POST['order_id'] ?? 0);
+ if (!$order_id) {
+ wp_send_json_error(array('message' => 'Invalid order ID'));
+ }
+ $order = wc_get_order($order_id);
+ if (!$order || $order->is_paid()) {
+ wp_send_json_error(array('message' => 'Invalid order'));
+ }
+ require_once WOO_ALIPAY_PLUGIN_PATH . 'inc/class-alipay-sdk-helper.php';
+ $main_gateway = new WC_Alipay(false);
+ $config = Alipay_SDK_Helper::get_alipay_config(array(
+ 'appid' => $main_gateway->get_option('appid'),
+ 'private_key' => $main_gateway->get_option('private_key'),
+ 'public_key' => $main_gateway->get_option('public_key'),
+ 'sandbox' => $main_gateway->get_option('sandbox'),
+ ));
+ try {
+ require_once WOO_ALIPAY_PLUGIN_PATH . 'lib/alipay/aop/request/AlipayTradePrecreateRequest.php';
+ $aop = Alipay_SDK_Helper::create_alipay_service($config);
+ if (!$aop) {
+ wp_send_json_error(array('message' => 'SDK init failed'));
+ }
+ $out_trade_no = Alipay_SDK_Helper::generate_out_trade_no($order_id, $this->order_prefix);
+ $order->update_meta_data('_alipay_out_trade_no', $out_trade_no);
+ $order->save();
+ $request = new AlipayTradePrecreateRequest();
+ $biz_content = array(
+ 'out_trade_no' => $out_trade_no,
+ 'total_amount' => Alipay_SDK_Helper::format_amount($this->convert_to_rmb($order->get_total())),
+ 'subject' => $this->get_order_title($order),
+ 'body' => $this->get_order_description($order),
+ 'timeout_express' => $this->get_option('qrcode_timeout', 120) . 's',
+ );
+ $request->setBizContent(json_encode($biz_content));
+ $request->setNotifyUrl($this->notify_url);
+ $response = $aop->execute($request);
+ $response_node = 'alipay_trade_precreate_response';
+ $result = $response->$response_node;
+ if (isset($result->code) && $result->code === '10000') {
+ $qr_code = $result->qr_code;
+ $order->update_meta_data('_alipay_qrcode', $qr_code);
+ $order->save();
+ wp_send_json_success(array(
+ 'qr_code' => $qr_code,
+ 'timeout' => intval($this->get_option('qrcode_timeout', 120))
+ ));
+ }
+ wp_send_json_error(array('message' => 'Precreate failed'));
+ } catch (Exception $e) {
+ wp_send_json_error(array('message' => $e->getMessage()));
+ }
+ }
+
+ public function ajax_query_payment_status()
+ {
+ check_ajax_referer('alipay_facetopay_query', 'nonce');
+
+ $order_id = intval($_POST['order_id'] ?? 0);
+
+ if (!$order_id) {
+ wp_send_json_error(array('message' => 'Invalid order ID'));
+ }
+
+ $order = wc_get_order($order_id);
+
+ if (!$order) {
+ wp_send_json_error(array('message' => 'Order not found'));
+ }
+
+ if ($order->is_paid()) {
+ wp_send_json_success(array(
+ 'status' => 'paid',
+ 'redirect_url' => $order->get_checkout_order_received_url(),
+ ));
+ }
+
+ require_once WOO_ALIPAY_PLUGIN_PATH . 'inc/class-alipay-sdk-helper.php';
+
+ $main_gateway = new WC_Alipay(false);
+ $config = Alipay_SDK_Helper::get_alipay_config(array(
+ 'appid' => $main_gateway->get_option('appid'),
+ 'private_key' => $main_gateway->get_option('private_key'),
+ 'public_key' => $main_gateway->get_option('public_key'),
+ 'sandbox' => $main_gateway->get_option('sandbox'),
+ ));
+
+ $out_trade_no = $order->get_meta('_alipay_out_trade_no');
+ $result = Alipay_SDK_Helper::query_order($out_trade_no, '', $config);
+
+ if (!is_wp_error($result) && $result['success']) {
+ if (in_array($result['trade_status'], array('TRADE_SUCCESS', 'TRADE_FINISHED'), true)) {
+ if (!$order->is_paid()) {
+ $order->payment_complete($result['trade_no']);
+ $order->add_order_note(
+ sprintf(__('当面付支付完成 - 交易号: %s', 'woo-alipay'), $result['trade_no'])
+ );
+ }
+
+ wp_send_json_success(array(
+ 'status' => 'paid',
+ 'redirect_url' => $order->get_checkout_order_received_url(),
+ ));
+ }
+ }
+
+ wp_send_json_success(array('status' => 'pending'));
+ }
+
+ public function check_alipay_response()
+ {
+ require_once WOO_ALIPAY_PLUGIN_PATH . 'inc/class-alipay-sdk-helper.php';
+
+ $main_gateway = new WC_Alipay(false);
+ $alipay_public_key = $main_gateway->get_option('public_key');
+
+ if (!Alipay_SDK_Helper::verify_notify($_POST, $alipay_public_key)) {
+ self::log('当面付通知签名验证失败', 'error');
+ echo 'fail';
+ exit;
+ }
+
+ $out_trade_no = $_POST['out_trade_no'] ?? '';
+ $trade_no = $_POST['trade_no'] ?? '';
+ $trade_status = $_POST['trade_status'] ?? '';
+
+ self::log('当面付支付通知: ' . print_r($_POST, true));
+
+ $orders = wc_get_orders(array(
+ 'meta_key' => '_alipay_out_trade_no',
+ 'meta_value' => $out_trade_no,
+ 'limit' => 1,
+ ));
+
+ if (empty($orders)) {
+ self::log('未找到订单: ' . $out_trade_no, 'error');
+ echo 'fail';
+ exit;
+ }
+
+ $order = $orders[0];
+
+ if ($trade_status === 'TRADE_SUCCESS' || $trade_status === 'TRADE_FINISHED') {
+ if (!$order->is_paid()) {
+ $order->payment_complete($trade_no);
+ $order->add_order_note(
+ sprintf(__('当面付支付完成 - 交易号: %s', 'woo-alipay'), $trade_no)
+ );
+ }
+ echo 'success';
+ } else {
+ echo 'fail';
+ }
+
+ exit;
+ }
+
+ public function process_refund($order_id, $amount = null, $reason = '')
+ {
+ $order = wc_get_order($order_id);
+
+ if (!$order) {
+ return new WP_Error('error', __('订单不存在', 'woo-alipay'));
+ }
+
+ require_once WOO_ALIPAY_PLUGIN_PATH . 'inc/class-alipay-sdk-helper.php';
+ require_once WOO_ALIPAY_PLUGIN_PATH . 'lib/alipay/aop/AopClient.php';
+ require_once WOO_ALIPAY_PLUGIN_PATH . 'lib/alipay/aop/request/AlipayTradeRefundRequest.php';
+
+ $main_gateway = new WC_Alipay(false);
+ $config = Alipay_SDK_Helper::get_alipay_config(array(
+ 'appid' => $main_gateway->get_option('appid'),
+ 'private_key' => $main_gateway->get_option('private_key'),
+ 'public_key' => $main_gateway->get_option('public_key'),
+ 'sandbox' => $main_gateway->get_option('sandbox'),
+ ));
+
+ $aop = Alipay_SDK_Helper::create_alipay_service($config);
+ if (!$aop) {
+ return new WP_Error('sdk_error', __('创建支付宝服务失败', 'woo-alipay'));
+ }
+
+ $out_trade_no = $order->get_meta('_alipay_out_trade_no');
+ if (!$out_trade_no) {
+ $out_trade_no = Alipay_SDK_Helper::generate_out_trade_no($order_id, $this->order_prefix);
+ }
+ $refund_amount = $amount ? floatval($amount) : floatval($order->get_total());
+ $refund_reason = $reason ? $reason : __('订单退款', 'woo-alipay');
+
+ try {
+ $request = new AlipayTradeRefundRequest();
+ $biz_content = array(
+ 'out_trade_no' => $out_trade_no,
+ 'refund_amount' => Alipay_SDK_Helper::format_amount($refund_amount),
+ 'refund_reason' => $refund_reason,
+ );
+ $request->setBizContent(json_encode($biz_content));
+ $response = $aop->execute($request);
+ $node = 'alipay_trade_refund_response';
+ $result = $response->$node;
+ if (isset($result->code) && $result->code === '10000') {
+ $order->add_order_note(sprintf(__('支付宝退款成功,金额:¥%s', 'woo-alipay'), number_format($refund_amount, 2)));
+ return true;
+ }
+ return new WP_Error('refund_failed', $result->sub_msg ?? $result->msg ?? __('退款失败', 'woo-alipay'));
+ } catch (Exception $e) {
+ return new WP_Error('exception', $e->getMessage());
+ }
+ }
+
+ /**
+ * 检查支付方式是否可用
+ */
+ public function is_available()
+ {
+ $is_available = ('yes' === $this->enabled) ? true : false;
+
+ if (!$is_available) {
+ return false;
+ }
+
+ // 检查主支付宝网关是否配置
+ $main_gateway = new WC_Alipay(false);
+ if (!$main_gateway->get_option('appid') || !$main_gateway->get_option('private_key')) {
+ return false;
+ }
+
+ return $is_available;
+ }
+
+ protected function convert_to_rmb($amount)
+ {
+ require_once WOO_ALIPAY_PLUGIN_PATH . 'inc/class-alipay-sdk-helper.php';
+ return Alipay_SDK_Helper::convert_currency(
+ $amount,
+ $this->current_currency,
+ $this->exchange_rate
+ );
+ }
+
+ protected function get_order_title($order)
+ {
+ $title = get_bloginfo('name') . ' - ' . sprintf(__('订单 #%s', 'woo-alipay'), $order->get_id());
+ return mb_substr($title, 0, 256);
+ }
+
+ protected function get_order_description($order)
+ {
+ $items = array();
+ foreach ($order->get_items() as $item) {
+ $items[] = $item->get_name();
+ }
+ return mb_substr(implode(', ', $items), 0, 400);
+ }
+
+ protected static function log($message, $level = 'info')
+ {
+ if (self::$log_enabled) {
+ if (empty(self::$log)) {
+ self::$log = wc_get_logger();
+ }
+ self::$log->log($level, $message, array('source' => self::GATEWAY_ID));
+ }
+ }
+}
diff --git a/inc/class-wc-alipay-installment-blocks-support.php b/inc/class-wc-alipay-installment-blocks-support.php
new file mode 100644
index 00000000..8a72a335
--- /dev/null
+++ b/inc/class-wc-alipay-installment-blocks-support.php
@@ -0,0 +1,104 @@
+name = 'alipay_installment';
+ }
+
+ public function initialize() {
+ $this->settings = get_option( 'woocommerce_alipay_installment_settings', array() );
+
+ $gateways = WC()->payment_gateways->payment_gateways();
+ $this->gateway = isset( $gateways['alipay_installment'] ) ? $gateways['alipay_installment'] : false;
+ }
+
+ public function is_active() {
+ $enabled = isset( $this->settings['enabled'] ) ? $this->settings['enabled'] : 'no';
+ return 'yes' === $enabled;
+ }
+
+ public function get_payment_method_script_handles() {
+ $script_path = 'js/frontend/blocks-installment.js';
+ $script_asset_path = WOO_ALIPAY_PLUGIN_PATH . 'js/frontend/blocks-installment.asset.php';
+ $script_asset = file_exists( $script_asset_path )
+ ? require( $script_asset_path )
+ : array(
+ 'dependencies' => array( 'wc-blocks-registry', 'wp-element', 'wp-html-entities', 'wp-i18n' ),
+ 'version' => WOO_ALIPAY_VERSION
+ );
+ $script_url = trailingslashit( WOO_ALIPAY_PLUGIN_URL ) . $script_path;
+
+ wp_register_script(
+ 'wc-alipay-installment-payments-blocks',
+ $script_url,
+ $script_asset['dependencies'],
+ $script_asset['version'],
+ true
+ );
+
+ if ( function_exists( 'wp_set_script_translations' ) ) {
+ wp_set_script_translations( 'wc-alipay-installment-payments-blocks', 'woo-alipay', WOO_ALIPAY_PLUGIN_PATH . 'languages' );
+ }
+
+ return [ 'wc-alipay-installment-payments-blocks' ];
+ }
+
+ public function get_payment_method_script_handles_for_admin() {
+ return $this->get_payment_method_script_handles();
+ }
+
+ public function get_payment_method_data() {
+ $cart_total = WC()->cart ? WC()->cart->get_total('') : 0;
+ $min_amount = $this->get_setting( 'min_amount', 100 );
+ $available_periods = $this->get_setting( 'available_periods', array('3', '6', '12') );
+ // 动态按阈值过滤
+ $min6 = floatval($this->get_setting('min_amount_6', 0));
+ $min12 = floatval($this->get_setting('min_amount_12', 0));
+ $filtered_periods = array();
+ foreach ($available_periods as $p) {
+ if ($p === '6' && $min6 > 0 && $cart_total < $min6) { continue; }
+ if ($p === '12' && $min12 > 0 && $cart_total < $min12) { continue; }
+ $filtered_periods[] = $p;
+ }
+ $available_periods = $filtered_periods;
+ $default_period = $this->get_setting( 'default_period', '3' );
+
+ // 获取汇率并转换金额
+ $current_currency = get_option('woocommerce_currency');
+ $exchange_rate = $this->get_setting( 'exchange_rate', 7.0 );
+
+ if (!in_array($current_currency, array('CNY', 'RMB'), true)) {
+ $cart_total = floatval($cart_total) * floatval($exchange_rate);
+ }
+
+ $meets_min_amount = $cart_total >= floatval($min_amount);
+
+ return [
+ 'title' => $this->get_setting( 'title', '支付宝花呗分期' ),
+ 'description' => $this->get_setting( 'description', '使用支付宝花呗分期付款,支持3期、6期、12期免息或低息分期。' ),
+ 'supports' => $this->get_supported_features(),
+ 'icon' => WOO_ALIPAY_PLUGIN_URL . 'assets/images/alipay-icon.svg',
+ 'minAmount' => floatval($min_amount),
+ 'cartTotal' => floatval($cart_total),
+'meetsMinAmount' => $meets_min_amount,
+ 'insufficientBehavior' => $this->get_setting( 'blocks_insufficient_behavior', 'hide' ),
+ 'availablePeriods' => $available_periods,
+'defaultPeriod' => $default_period,
+'feePayer' => ($this->get_setting('fee_bearer', 'user') === 'seller' ? 'seller' : 'user'),
+ 'showInterestFreeBadge' => ($this->get_setting('show_interest_free_badge', 'yes') === 'yes'),
+ ];
+ }
+
+ public function get_supported_features() {
+ return $this->gateway ? $this->gateway->supports : ['products'];
+ }
+}
diff --git a/inc/class-wc-alipay-installment.php b/inc/class-wc-alipay-installment.php
new file mode 100644
index 00000000..c9247e75
--- /dev/null
+++ b/inc/class-wc-alipay-installment.php
@@ -0,0 +1,641 @@
+id = self::GATEWAY_ID;
+ $this->method_title = __('支付宝花呗分期', 'woo-alipay');
+ $this->method_description = __('支持花呗分期付款,用户可选择3期、6期、12期分期。需要在支付宝商户后台开通花呗分期功能。', 'woo-alipay');
+ $this->icon = WOO_ALIPAY_PLUGIN_URL . 'assets/images/alipay-icon.svg';
+ $this->has_fields = true; // 在结账页面显示分期选择
+ $this->charset = strtolower(get_bloginfo('charset'));
+
+ if (!in_array($this->charset, array('gbk', 'utf-8'), true)) {
+ $this->charset = 'utf-8';
+ }
+
+ // 加载设置
+ $this->init_form_fields();
+ $this->init_settings();
+
+ // 设置属性
+ $this->title = $this->get_option('title', __('支付宝花呗分期', 'woo-alipay'));
+ $this->description = $this->get_option('description');
+ $this->enabled = $this->get_option('enabled');
+ $this->current_currency = get_option('woocommerce_currency');
+ $this->exchange_rate = $this->get_option('exchange_rate');
+ $this->order_prefix = $this->get_option('order_prefix', 'HBFQ');
+ $this->notify_url = WC()->api_request_url('WC_Alipay_Installment');
+
+ self::$log_enabled = ('yes' === $this->get_option('debug', 'no'));
+
+ // 支持的功能
+ $this->supports = array(
+ 'products',
+ 'refunds',
+ );
+
+ // 添加钩子
+ add_action('woocommerce_update_options_payment_gateways_' . $this->id, array($this, 'process_admin_options'));
+ add_action('woocommerce_receipt_' . $this->id, array($this, 'receipt_page'));
+ add_action('woocommerce_api_wc_alipay_installment', array($this, 'check_alipay_response'));
+ add_action('wp_enqueue_scripts', array($this, 'payment_scripts'));
+ }
+
+ /**
+ * 初始化表单字段
+ */
+ public function init_form_fields()
+ {
+ $this->form_fields = array(
+ 'enabled' => array(
+ 'title' => __('启用/禁用', 'woo-alipay'),
+ 'type' => 'checkbox',
+ 'label' => __('启用支付宝花呗分期', 'woo-alipay'),
+ 'default' => 'no',
+ ),
+ 'title' => array(
+ 'title' => __('标题', 'woo-alipay'),
+ 'type' => 'text',
+ 'description' => __('用户在结账时看到的支付方式名称', 'woo-alipay'),
+ 'default' => __('支付宝花呗分期', 'woo-alipay'),
+ 'desc_tip' => true,
+ ),
+ 'description' => array(
+ 'title' => __('描述', 'woo-alipay'),
+ 'type' => 'textarea',
+ 'description' => __('支付方式描述,显示在结账页面', 'woo-alipay'),
+ 'default' => __('使用支付宝花呗分期付款,支持3期、6期、12期免息或低息分期。', 'woo-alipay'),
+ 'desc_tip' => true,
+ ),
+
+ 'alipay_config' => array(
+ 'title' => __('支付宝配置', 'woo-alipay'),
+ 'type' => 'title',
+ 'description' => __('使用主支付宝网关的配置(App ID、公钥、私钥等)', 'woo-alipay'),
+ ),
+
+ 'installment_settings' => array(
+ 'title' => __('分期设置', 'woo-alipay'),
+ 'type' => 'title',
+ ),
+ 'show_interest_free_badge' => array(
+ 'title' => __('显示“免息”标识', 'woo-alipay'),
+ 'type' => 'checkbox',
+ 'label' => __('在分期选项后显示“免息”标识(当商家承担手续费时)', 'woo-alipay'),
+ 'default' => 'yes',
+ 'desc_tip' => true,
+ ),
+ 'fee_bearer' => array(
+ 'title' => __('分期手续费承担方', 'woo-alipay'),
+ 'type' => 'select',
+ 'description' => __('选择分期手续费由谁承担。用户承担将会在支付宝端向用户收取手续费;商家承担则由商家承担。', 'woo-alipay'),
+ 'default' => 'user',
+ 'options' => array(
+ 'user' => __('用户承担', 'woo-alipay'),
+ 'seller' => __('商家承担', 'woo-alipay'),
+ ),
+ 'desc_tip' => true,
+ ),
+ 'min_amount_6' => array(
+ 'title' => __('6期最小金额', 'woo-alipay'),
+ 'type' => 'number',
+ 'description' => __('达到此金额才显示6期选项(留空或0表示不限制)', 'woo-alipay'),
+ 'default' => '',
+ 'desc_tip' => true,
+ 'custom_attributes' => array(
+ 'min' => '0',
+ 'step' => '1',
+ ),
+ ),
+ 'min_amount_12' => array(
+ 'title' => __('12期最小金额', 'woo-alipay'),
+ 'type' => 'number',
+ 'description' => __('达到此金额才显示12期选项(留空或0表示不限制)', 'woo-alipay'),
+ 'default' => '',
+ 'desc_tip' => true,
+ 'custom_attributes' => array(
+ 'min' => '0',
+ 'step' => '1',
+ ),
+ ),
+ 'min_amount' => array(
+ 'title' => __('最小分期金额', 'woo-alipay'),
+ 'type' => 'number',
+ 'description' => __('订单金额低于此值时不显示分期选项(人民币)', 'woo-alipay'),
+ 'default' => '100',
+ 'desc_tip' => true,
+ 'custom_attributes' => array(
+ 'min' => '1',
+ 'step' => '1',
+ ),
+ ),
+ 'available_periods' => array(
+ 'title' => __('可用分期期数', 'woo-alipay'),
+ 'type' => 'multiselect',
+ 'description' => __('选择允许的分期期数', 'woo-alipay'),
+ 'default' => array('3', '6', '12'),
+ 'options' => array(
+ '3' => __('3期', 'woo-alipay'),
+ '6' => __('6期', 'woo-alipay'),
+ '12' => __('12期', 'woo-alipay'),
+ ),
+ 'desc_tip' => true,
+ 'class' => 'wc-enhanced-select',
+ ),
+ 'default_period' => array(
+ 'title' => __('默认分期期数', 'woo-alipay'),
+ 'type' => 'select',
+ 'description' => __('用户未选择时的默认分期期数', 'woo-alipay'),
+ 'default' => '3',
+ 'options' => array(
+ '3' => __('3期', 'woo-alipay'),
+ '6' => __('6期', 'woo-alipay'),
+ '12' => __('12期', 'woo-alipay'),
+ ),
+ 'desc_tip' => true,
+ ),
+ 'blocks_insufficient_behavior' => array(
+ 'title' => __('结账区块金额不足行为', 'woo-alipay'),
+ 'type' => 'select',
+ 'description' => __('当订单金额低于最小分期金额时,在结账区块中隐藏支付方式或显示提示。', 'woo-alipay'),
+ 'default' => 'hide',
+ 'options' => array(
+ 'hide' => __('隐藏(推荐)', 'woo-alipay'),
+ 'show' => __('显示提示', 'woo-alipay'),
+ ),
+ 'desc_tip' => true,
+ ),
+
+ 'order_prefix' => array(
+ 'title' => __('订单号前缀', 'woo-alipay'),
+ 'type' => 'text',
+ 'description' => __('花呗分期订单号的前缀,便于区分', 'woo-alipay'),
+ 'default' => 'HBFQ',
+ 'desc_tip' => true,
+ ),
+
+ 'debug' => array(
+ 'title' => __('调试日志', 'woo-alipay'),
+ 'type' => 'checkbox',
+ 'label' => __('启用日志记录', 'woo-alipay'),
+ 'default' => 'no',
+ 'description' => sprintf(
+ __('记录花呗分期相关日志到 %s', 'woo-alipay'),
+ '' . WC_Log_Handler_File::get_log_file_path($this->id) . ''
+ ),
+ ),
+ );
+
+ // 如果不是人民币,添加汇率设置
+ if (!in_array($this->current_currency, array('CNY', 'RMB'), true)) {
+ $this->form_fields['exchange_rate'] = array(
+ 'title' => __('汇率', 'woo-alipay'),
+ 'type' => 'number',
+ 'description' => sprintf(
+ __('设置 %s 与人民币的汇率', 'woo-alipay'),
+ $this->current_currency
+ ),
+ 'default' => '7.0',
+ 'desc_tip' => true,
+ 'custom_attributes' => array(
+ 'step' => '0.01',
+ 'min' => '0.01',
+ ),
+ );
+ }
+ }
+
+ /**
+ * 在结账页面显示分期选择
+ */
+ public function payment_fields()
+ {
+ // 显示描述
+ if ($this->description) {
+ echo wpautop(wptexturize($this->description));
+ }
+
+ // 获取购物车总额
+ $cart_total = WC()->cart->get_total('');
+ $min_amount = floatval($this->get_option('min_amount', 100));
+ $available_periods = $this->get_option('available_periods', array('3', '6', '12'));
+ $default_period = $this->get_option('default_period', '3');
+
+ // 转换为人民币
+ $rmb_total = $this->convert_to_rmb($cart_total);
+
+ if ($rmb_total < $min_amount) {
+ echo '' .
+ sprintf(__('订单金额需满 ¥%s 才能使用花呗分期', 'woo-alipay'), $min_amount) .
+ '
';
+ return;
+ }
+
+ // 动态过滤可用分期期数(根据阈值)
+ $filtered_periods = array();
+ foreach ($available_periods as $p) {
+ $p = (string) $p;
+ if ($p === '6') {
+ $min6 = floatval($this->get_option('min_amount_6', 0));
+ if ($min6 > 0 && $rmb_total < $min6) { continue; }
+ }
+ if ($p === '12') {
+ $min12 = floatval($this->get_option('min_amount_12', 0));
+ if ($min12 > 0 && $rmb_total < $min12) { continue; }
+ }
+ $filtered_periods[] = $p;
+ }
+ ?>
+
+
+
+
+ get_option('fee_bearer', 'user');
+ echo $fee_bearer === 'seller'
+ ? __('免息分期(手续费由商家承担)', 'woo-alipay')
+ : __('可能产生分期手续费,以支付宝支付页面为准', 'woo-alipay');
+ ?>
+
+
+ get_option('default_period', '3');
+
+ // 从传统表单获取
+ if (isset($_POST['alipay_installment_period'])) {
+ $installment_period = sanitize_text_field($_POST['alipay_installment_period']);
+ }
+ // 从 Blocks 获取
+ elseif (isset($_POST['payment_data'])) {
+ $payment_data = json_decode(wp_unslash($_POST['payment_data']), true);
+ if (isset($payment_data['alipay_installment_period'])) {
+ $installment_period = sanitize_text_field($payment_data['alipay_installment_period']);
+ }
+ }
+
+ // 保存分期期数到订单
+ $order->update_meta_data('_alipay_installment_period', $installment_period);
+ $order->save();
+
+ // 标记订单为待支付
+ $order->update_status('pending', __('等待花呗分期支付', 'woo-alipay'));
+
+ // 清空购物车
+ WC()->cart->empty_cart();
+
+ // 跳转到支付页面
+ return array(
+ 'result' => 'success',
+ 'redirect' => $order->get_checkout_payment_url(true),
+ );
+ }
+
+ /**
+ * 收银页面 - 生成支付表单
+ */
+ public function receipt_page($order_id)
+ {
+ $order = wc_get_order($order_id);
+
+ if (!$order || $order->is_paid()) {
+ return;
+ }
+
+ // 加载辅助类
+ require_once WOO_ALIPAY_PLUGIN_PATH . 'inc/class-alipay-sdk-helper.php';
+
+ // 获取主支付宝网关配置
+ $main_gateway = new WC_Alipay(false);
+ $config = Alipay_SDK_Helper::get_alipay_config(array(
+ 'appid' => $main_gateway->get_option('appid'),
+ 'private_key' => $main_gateway->get_option('private_key'),
+ 'public_key' => $main_gateway->get_option('public_key'),
+ 'sandbox' => $main_gateway->get_option('sandbox'),
+ ));
+
+ // 创建支付宝服务
+ $aop = Alipay_SDK_Helper::create_alipay_service($config);
+ if (!$aop) {
+ $order->update_status('failed', __('创建支付宝服务失败', 'woo-alipay'));
+ wc_add_notice(__('支付初始化失败,请稍后重试', 'woo-alipay'), 'error');
+ return;
+ }
+
+ // 获取订单信息
+ $total = $this->convert_to_rmb($order->get_total());
+ $installment_period = $order->get_meta('_alipay_installment_period');
+ $out_trade_no = Alipay_SDK_Helper::generate_out_trade_no($order_id, $this->order_prefix);
+
+ // 保存商户订单号
+ $order->update_meta_data('_alipay_out_trade_no', $out_trade_no);
+ $order->save();
+
+ try {
+ require_once WOO_ALIPAY_PLUGIN_PATH . 'lib/alipay/aop/AopClient.php';
+ require_once WOO_ALIPAY_PLUGIN_PATH . 'lib/alipay/aop/request/AlipayTradePagePayRequest.php';
+
+ $request = new AlipayTradePagePayRequest();
+
+ // 设置业务参数
+ $biz_content = array(
+ 'out_trade_no' => $out_trade_no,
+ 'total_amount' => Alipay_SDK_Helper::format_amount($total),
+ 'subject' => $this->get_order_title($order),
+ 'body' => $this->get_order_description($order),
+ 'product_code' => 'FAST_INSTANT_TRADE_PAY',
+ // 花呗分期参数
+ 'enable_pay_channels' => 'pcreditpayInstallment',
+ 'extend_params' => array(
+ 'hb_fq_num' => $installment_period,
+'hb_fq_seller_percent' => ($this->get_option('fee_bearer', 'user') === 'seller' ? '100' : '0'),
+ ),
+ );
+
+ $request->setBizContent(json_encode($biz_content));
+ $request->setReturnUrl($order->get_checkout_order_received_url());
+ $request->setNotifyUrl($this->notify_url);
+
+ // 生成支付表单
+ $html = $aop->pageExecute($request, 'POST');
+
+ self::log('花呗分期支付请求: ' . print_r($biz_content, true));
+
+ echo $html;
+
+ } catch (Exception $e) {
+ self::log('花呗分期支付异常: ' . $e->getMessage(), 'error');
+ $order->update_status('failed', $e->getMessage());
+ wc_add_notice(__('支付请求失败,请稍后重试', 'woo-alipay'), 'error');
+ }
+ }
+
+ /**
+ * 检查支付宝响应
+ */
+ public function check_alipay_response()
+ {
+ // 加载辅助类
+ require_once WOO_ALIPAY_PLUGIN_PATH . 'inc/class-alipay-sdk-helper.php';
+
+ // 获取支付宝公钥
+ $main_gateway = new WC_Alipay(false);
+ $alipay_public_key = $main_gateway->get_option('public_key');
+
+ // 验证签名
+ if (!Alipay_SDK_Helper::verify_notify($_POST, $alipay_public_key)) {
+ self::log('花呗分期通知签名验证失败', 'error');
+ echo 'fail';
+ exit;
+ }
+
+ $out_trade_no = $_POST['out_trade_no'] ?? '';
+ $trade_no = $_POST['trade_no'] ?? '';
+ $trade_status = $_POST['trade_status'] ?? '';
+
+ self::log('花呗分期支付通知: ' . print_r($_POST, true));
+
+ // 查找订单
+ $orders = wc_get_orders(array(
+ 'meta_key' => '_alipay_out_trade_no',
+ 'meta_value' => $out_trade_no,
+ 'limit' => 1,
+ ));
+
+ if (empty($orders)) {
+ self::log('未找到订单: ' . $out_trade_no, 'error');
+ echo 'fail';
+ exit;
+ }
+
+ $order = $orders[0];
+
+ // 处理支付结果
+ if ($trade_status === 'TRADE_SUCCESS' || $trade_status === 'TRADE_FINISHED') {
+ if (!$order->is_paid()) {
+ $order->payment_complete($trade_no);
+ $order->add_order_note(
+ sprintf(__('花呗分期支付完成 - 交易号: %s', 'woo-alipay'), $trade_no)
+ );
+ }
+ echo 'success';
+ } else {
+ echo 'fail';
+ }
+
+ exit;
+ }
+
+ /**
+ * 处理退款
+ */
+ public function process_refund($order_id, $amount = null, $reason = '')
+ {
+ $order = wc_get_order($order_id);
+
+ if (!$order) {
+ return new WP_Error('error', __('订单不存在', 'woo-alipay'));
+ }
+
+ require_once WOO_ALIPAY_PLUGIN_PATH . 'inc/class-alipay-sdk-helper.php';
+ require_once WOO_ALIPAY_PLUGIN_PATH . 'lib/alipay/aop/AopClient.php';
+ require_once WOO_ALIPAY_PLUGIN_PATH . 'lib/alipay/aop/request/AlipayTradeRefundRequest.php';
+
+ $main_gateway = new WC_Alipay(false);
+ $config = Alipay_SDK_Helper::get_alipay_config(array(
+ 'appid' => $main_gateway->get_option('appid'),
+ 'private_key' => $main_gateway->get_option('private_key'),
+ 'public_key' => $main_gateway->get_option('public_key'),
+ 'sandbox' => $main_gateway->get_option('sandbox'),
+ ));
+
+ $aop = Alipay_SDK_Helper::create_alipay_service($config);
+ if (!$aop) {
+ return new WP_Error('sdk_error', __('创建支付宝服务失败', 'woo-alipay'));
+ }
+
+ $out_trade_no = $order->get_meta('_alipay_out_trade_no');
+ if (!$out_trade_no) {
+ $out_trade_no = Alipay_SDK_Helper::generate_out_trade_no($order_id, $this->order_prefix);
+ }
+ $refund_amount = $amount ? floatval($amount) : floatval($order->get_total());
+ $refund_reason = $reason ? $reason : __('订单退款', 'woo-alipay');
+
+ try {
+ $request = new AlipayTradeRefundRequest();
+ $biz_content = array(
+ 'out_trade_no' => $out_trade_no,
+ 'refund_amount' => Alipay_SDK_Helper::format_amount($refund_amount),
+ 'refund_reason' => $refund_reason,
+ );
+ $request->setBizContent(json_encode($biz_content));
+ $response = $aop->execute($request);
+ $node = 'alipay_trade_refund_response';
+ $result = $response->$node;
+ if (isset($result->code) && $result->code === '10000') {
+ $order->add_order_note(sprintf(__('支付宝退款成功,金额:¥%s', 'woo-alipay'), number_format($refund_amount, 2)));
+ return true;
+ }
+ return new WP_Error('refund_failed', $result->sub_msg ?? $result->msg ?? __('退款失败', 'woo-alipay'));
+ } catch (Exception $e) {
+ return new WP_Error('exception', $e->getMessage());
+ }
+ }
+
+ /**
+ * 检查支付方式是否可用
+ */
+ public function is_available()
+ {
+ $is_available = ('yes' === $this->enabled) ? true : false;
+
+ if (!$is_available) {
+ return false;
+ }
+
+ // 检查主支付宝网关是否配置
+ $main_gateway = new WC_Alipay(false);
+ if (!$main_gateway->get_option('appid') || !$main_gateway->get_option('private_key')) {
+ return false;
+ }
+
+ // 检查最小金额要求
+ if (WC()->cart) {
+ $cart_total = WC()->cart->get_total('');
+ $min_amount = floatval($this->get_option('min_amount', 100));
+ $rmb_total = $this->convert_to_rmb($cart_total);
+
+ if ($rmb_total < $min_amount) {
+ return false;
+ }
+ }
+
+ return $is_available;
+ }
+
+ /**
+ * 转换为人民币
+ */
+ protected function convert_to_rmb($amount)
+ {
+ return Alipay_SDK_Helper::convert_currency(
+ $amount,
+ $this->current_currency,
+ $this->exchange_rate
+ );
+ }
+
+ /**
+ * 获取订单标题
+ */
+ protected function get_order_title($order)
+ {
+ $title = get_bloginfo('name') . ' - ' . sprintf(__('订单 #%s', 'woo-alipay'), $order->get_id());
+ return mb_substr($title, 0, 256);
+ }
+
+ /**
+ * 获取订单描述
+ */
+ protected function get_order_description($order)
+ {
+ $items = array();
+ foreach ($order->get_items() as $item) {
+ $items[] = $item->get_name();
+ }
+ return mb_substr(implode(', ', $items), 0, 400);
+ }
+
+ /**
+ * 记录日志
+ */
+ protected static function log($message, $level = 'info')
+ {
+ if (self::$log_enabled) {
+ if (empty(self::$log)) {
+ self::$log = wc_get_logger();
+ }
+ self::$log->log($level, $message, array('source' => self::GATEWAY_ID));
+ }
+ }
+}
diff --git a/inc/class-wc-alipay-subscription-ui.php b/inc/class-wc-alipay-subscription-ui.php
deleted file mode 100644
index 2ba1026a..00000000
--- a/inc/class-wc-alipay-subscription-ui.php
+++ /dev/null
@@ -1,53 +0,0 @@
- rawurlencode( $return_url ) ], WC()->api_request_url( 'WC_Alipay_Agreement_Start' ) );
- echo '';
- }
-
- public static function render_view_subscription_sign( $subscription_id ) {
- if ( ! is_user_logged_in() ) { return; }
- $user_id = get_current_user_id();
- if ( ! self::can_offer_sign( $user_id ) ) { return; }
-
- $return_url = wc_get_account_endpoint_url( 'subscriptions' );
- $sign_url = add_query_arg( [ 'return_url' => rawurlencode( $return_url ) ], WC()->api_request_url( 'WC_Alipay_Agreement_Start' ) );
-
- echo '';
- }
-}
-
-WC_Alipay_Subscription_UI::init();
diff --git a/inc/class-wc-alipay.php b/inc/class-wc-alipay.php
index f706304b..67ccc9c7 100755
--- a/inc/class-wc-alipay.php
+++ b/inc/class-wc-alipay.php
@@ -63,7 +63,6 @@ class WC_Alipay extends WC_Payment_Gateway
'subscription_amount_changes',
'subscription_date_changes',
'multiple_subscriptions',
- 'tokenization',
);
self::$log_enabled = ('yes' === $this->get_option('debug', 'no'));
@@ -75,12 +74,6 @@ class WC_Alipay extends WC_Payment_Gateway
$this->setup_form_fields();
$this->init_settings();
-
- // Register subscription scheduled payment hook
- if ( class_exists('WC_Subscriptions') || function_exists('wcs_order_contains_subscription') || function_exists('wcs_is_subscription') ) {
- add_action('woocommerce_scheduled_subscription_payment_' . $this->id, array($this, 'scheduled_subscription_payment'), 10, 2);
- }
-
if ($init_hooks) {
// Add save gateway options callback
add_action('woocommerce_update_options_payment_gateways_' . $this->id, array(
@@ -108,100 +101,6 @@ class WC_Alipay extends WC_Payment_Gateway
}
}
- /**
- * Woo Subscriptions: scheduled payment callback
- *
- * @param float $amount_to_charge
- * @param WC_Order $renewal_order
- */
- public function scheduled_subscription_payment( $amount_to_charge, $renewal_order )
- {
- try {
- if ( 'yes' !== $this->get_option( 'enable_auto_renew', 'no' ) ) {
- $renewal_order->add_order_note( __( '未启用自动续费,跳过代扣。', 'woo-alipay' ) );
- $renewal_order->update_status( 'failed', __( '自动续费未启用', 'woo-alipay' ) );
- return;
- }
-
- $user_id = $renewal_order->get_user_id();
- if ( ! $user_id || ! class_exists( 'WC_Alipay_Agreement' ) ) {
- $renewal_order->update_status( 'failed', __( '找不到用户或协议管理不可用。', 'woo-alipay' ) );
- return;
- }
-
- $agreement_no = WC_Alipay_Agreement::get_user_agreement_no( $user_id );
- if ( ! $agreement_no ) {
- $renewal_order->update_status( 'failed', __( '未查询到支付宝扣款协议,请先完成签约。', 'woo-alipay' ) );
- return;
- }
-
- require_once WOO_ALIPAY_PLUGIN_PATH . 'inc/class-alipay-sdk-helper.php';
- require_once WOO_ALIPAY_PLUGIN_PATH . 'lib/alipay/aop/AopClient.php';
- require_once WOO_ALIPAY_PLUGIN_PATH . 'lib/alipay/aop/request/AlipayTradeCreateRequest.php';
-
- $config = Alipay_SDK_Helper::get_alipay_config( array(
- 'appid' => $this->get_option('appid'),
- 'private_key' => $this->get_option('private_key'),
- 'public_key' => $this->get_option('public_key'),
- 'sandbox' => $this->get_option('sandbox'),
- ) );
-
- $aop = Alipay_SDK_Helper::create_alipay_service( $config );
- if ( ! $aop ) {
- $renewal_order->update_status( 'failed', __( '创建支付宝服务失败(自动续费)。', 'woo-alipay' ) );
- return;
- }
-
- $out_trade_no = 'SubR' . $renewal_order->get_id() . '-' . current_time('timestamp');
- // 记录到订单元数据,便于回调查询定位
- $renewal_order->update_meta_data( '_alipay_out_trade_no', $out_trade_no );
- $renewal_order->save();
-
- $total = $this->maybe_convert_amount( $amount_to_charge );
- $subject = $this->get_option( 'auto_renew_subject_prefix', __( '订阅续费', 'woo-alipay' ) ) . ' - #' . $renewal_order->get_id();
-
- $product_code = apply_filters( 'woo_alipay_agreement_trade_product_code', $this->get_option( 'agreement_product_code', 'CYCLE_PAY_AUTH' ), $renewal_order );
- $biz = array(
- 'out_trade_no' => $out_trade_no,
- 'total_amount' => $total,
- 'subject' => $subject,
- 'product_code' => $product_code,
- 'agreement_params' => array(
- 'agreement_no' => $agreement_no,
- ),
- );
-
- $biz = apply_filters( 'woo_alipay_agreement_trade_biz_content', $biz, $renewal_order, $agreement_no );
-
- $request = new AlipayTradeCreateRequest();
- $request->setBizContent( wp_json_encode( $biz ) );
- $request->setNotifyUrl( apply_filters( 'woo_alipay_gateway_notify_url', $this->notify_url, $renewal_order->get_id() ) );
-
- $response = $aop->execute( $request );
- $node = 'alipay_trade_create_response';
- $result = $response->$node ?? null;
-
- if ( ! $result || ! isset( $result->code ) || '10000' !== $result->code ) {
- self::log( __METHOD__ . ' TradeCreate error: ' . wc_print_r( $response, true ), 'error' );
- $renewal_order->update_status( 'failed', __( '支付宝代扣下单失败。', 'woo-alipay' ) );
- return;
- }
-
- // 可选:立即查询一次状态
- $query = Alipay_SDK_Helper::query_order( $out_trade_no, '', $config );
- if ( ! is_wp_error( $query ) && ! empty( $query['trade_status'] ) && in_array( $query['trade_status'], array( 'TRADE_SUCCESS', 'TRADE_FINISHED' ), true ) ) {
- $renewal_order->payment_complete( $query['trade_no'] );
- $renewal_order->add_order_note( sprintf( __( '自动续费成功,交易号:%s', 'woo-alipay' ), $query['trade_no'] ) );
- } else {
- // 等待异步通知回调完成订单
- $renewal_order->add_order_note( __( '已发起支付宝代扣,等待异步通知确认。', 'woo-alipay' ) );
- }
- } catch ( Exception $e ) {
- self::log( __METHOD__ . ' exception: ' . $e->getMessage(), 'error' );
- $renewal_order->update_status( 'failed', $e->getMessage() );
- }
- }
-
protected function setup_form_fields()
{
$this->form_fields = array(
@@ -296,62 +195,16 @@ class WC_Alipay extends WC_Payment_Gateway
),
'desc_tip' => false,
),
-
- );
-
- // 将“支付增强功能”分组提前到“环境与调试”之后
- $this->add_payment_enhancement_settings();
-
- // 后续是“高级设置”和“订阅与自动续费(实验性)”
- $this->form_fields = array_merge(
- $this->form_fields,
- array(
- 'advanced_settings_title' => array(
- 'title' => __('高级设置', 'woo-alipay'),
- 'type' => 'title',
- 'description' => __('配置汇率转换和连接测试等高级功能', 'woo-alipay'),
- ),
- )
- );
-
- // Subscriptions & Auto-renew (Experimental)
-
- // 订阅与自动续费(实验性)分组与字段
- $this->form_fields['subscriptions_title'] = array(
- 'title' => __('订阅与自动续费(实验性)', 'woo-alipay'),
- 'type' => 'title',
- 'description' => __('启用后,支持 WooCommerce Subscriptions 的自动续费。需要先在支付宝签约周期扣款。', 'woo-alipay'),
- );
- $this->form_fields['enable_auto_renew'] = array(
- 'title' => __('启用自动续费', 'woo-alipay'),
- 'type' => 'checkbox',
- 'label' => __('允许通过支付宝协议代扣进行订阅续费', 'woo-alipay'),
- 'default' => 'no',
- );
- $this->form_fields['agreement_product_code'] = array(
- 'title' => __('签约/代扣产品码', 'woo-alipay'),
- 'type' => 'text',
- 'default' => 'CYCLE_PAY_AUTH',
- 'description' => __('用于协议签约与代扣的产品码。不同商户开通能力可能不同,可通过过滤器覆盖。', 'woo-alipay'),
- 'desc_tip' => true,
- );
- $this->form_fields['auto_renew_subject_prefix'] = array(
- 'title' => __('续费订单标题前缀', 'woo-alipay'),
- 'type' => 'text',
- 'default' => __('订阅续费', 'woo-alipay'),
+
+ 'advanced_settings_title' => array(
+ 'title' => __('高级设置', 'woo-alipay'),
+ 'type' => 'title',
+ 'description' => __('配置汇率转换和连接测试等高级功能', 'woo-alipay'),
+ ),
);
- // 仅当安装并启用了 WooCommerce Subscriptions 时,展示自动续费设置
- if ( ! ( class_exists('WC_Subscriptions') || function_exists('wcs_order_contains_subscription') || function_exists('wcs_is_subscription') ) ) {
- unset(
- $this->form_fields['enable_auto_renew'],
- $this->form_fields['agreement_product_code'],
- $this->form_fields['auto_renew_subject_prefix']
- );
- if ( isset( $this->form_fields['subscriptions_title'] ) ) {
- $this->form_fields['subscriptions_title']['description'] = __( '需要安装并启用 WooCommerce Subscriptions 才能设置自动续费。', 'woo-alipay' );
- }
- }
+ // 添加支付增强功能设置
+ $this->add_payment_enhancement_settings();
if (!in_array($this->current_currency, $this->supported_currencies, true)) {
$current_rate = $this->get_option('exchange_rate', '7.0');
@@ -800,7 +653,7 @@ class WC_Alipay extends WC_Payment_Gateway
$config = array(
'app_id' => $this->get_option('appid'),
'merchant_private_key' => $this->get_option('private_key'),
- 'notify_url' => apply_filters('woo_alipay_gateway_notify_url', $this->notify_url, $order_id),
+ 'notify_url' => $this->notify_url,
'return_url' => apply_filters('woo_alipay_gateway_return_url', ($order) ? $order->get_checkout_order_received_url() : get_home_url()),
'charset' => $this->charset,
'sign_type' => 'RSA2',
@@ -990,50 +843,11 @@ class WC_Alipay extends WC_Payment_Gateway
echo '';
echo '';
-
- // 实用端点与工具
- $notify_url = apply_filters( 'woo_alipay_gateway_notify_url', $this->notify_url, 0 );
- echo '';
- echo '
' . esc_html__( '工具与端点', 'woo-alipay' ) . '
';
- echo '
' . esc_html__( '异步通知 URL(请在支付宝开放平台中配置为支付结果通知 URL)', 'woo-alipay' ) . '
';
- echo '
' . esc_html( $notify_url ) . ' ';
- echo '
';
-
- // 若支持订阅,展示签约相关端点
- $subs_available = ( class_exists('WC_Subscriptions') || function_exists('wcs_order_contains_subscription') || function_exists('wcs_is_subscription') );
- if ( $subs_available ) {
- $start_url = WC()->api_request_url( 'WC_Alipay_Agreement_Start' );
- $notify_agre = WC()->api_request_url( 'WC_Alipay_Agreement_Notify' );
- $return_agre = WC()->api_request_url( 'WC_Alipay_Agreement_Return' );
- echo '
';
- echo '
' . esc_html__( '签约端点(用于支付宝协议授权与回调)', 'woo-alipay' ) . '
';
- echo '
';
- echo '- ' . esc_html__( '签约启动', 'woo-alipay' ) . ':
' . esc_html( $start_url ) . ' ';
- echo '- ' . esc_html__( '签约通知', 'woo-alipay' ) . ':
' . esc_html( $notify_agre ) . ' ';
- echo '- ' . esc_html__( '签约返回', 'woo-alipay' ) . ':
' . esc_html( $return_agre ) . ' ';
- echo '
';
- }
- echo '
';
-
echo '';
-
- // 简易复制函数
- echo '';
- }
-
- // Provider state methods for WooCommerce Payments list badges
- public function is_account_connected() {
- return (bool) ( $this->get_option('appid') && $this->get_option('private_key') && $this->get_option('public_key') );
- }
-
- public function needs_setup() {
- return ! $this->is_account_connected();
- }
-
- public function is_test_mode() {
- return 'yes' === $this->get_option('sandbox');
}
public function check_alipay_response()
@@ -1047,23 +861,9 @@ class WC_Alipay extends WC_Payment_Gateway
$fund_bill_list = isset($_POST['fund_bill_list']) ? stripslashes(sanitize_text_field($_POST['fund_bill_list'])) : '';
$needs_reply = false;
$error = false;
-
- // 先尝试通过既有规则(WooA前缀)解析订单ID
- $order = null;
- $order_id = 0;
- if ( strpos( $out_trade_no, 'WooA' ) === 0 ) {
- $out_trade_no_parts = explode('-', str_replace('WooA', '', $out_trade_no));
- $order_id = absint(array_shift($out_trade_no_parts));
- $order = wc_get_order($order_id);
- }
- // 若无法解析或未找到订单,改为通过订单元数据定位(适配自动续费 SubR*)
- if ( ! $order ) {
- $orders = wc_get_orders( array( 'meta_key' => '_alipay_out_trade_no', 'meta_value' => $out_trade_no, 'limit' => 1 ) );
- if ( ! empty( $orders ) ) {
- $order = $orders[0];
- $order_id = $order->get_id();
- }
- }
+ $out_trade_no_parts = explode('-', str_replace('WooA', '', $out_trade_no));
+ $order_id = absint(array_shift($out_trade_no_parts));
+ $order = wc_get_order($order_id);
$order_check = ($order instanceof WC_Order);
if (!$order_check) {
diff --git a/inc/class-woo-alipay.php b/inc/class-woo-alipay.php
index 37cf2d19..e1c18975 100755
--- a/inc/class-woo-alipay.php
+++ b/inc/class-woo-alipay.php
@@ -35,9 +35,6 @@ class Woo_Alipay {
// Add WooCommerce Blocks support
$this->woocommerce_gateway_alipay_woocommerce_block_support();
-
- // Enhance Payments providers response with contextual links for Alipay gateways
- add_filter( 'rest_post_dispatch', array( $this, 'filter_payments_providers_response' ), 10, 3 );
}
}
@@ -154,53 +151,39 @@ class Woo_Alipay {
}
}
- public function add_admin_scripts( $hook ) {
+ public function add_admin_scripts( $hook ) {
- if ( 'woocommerce_page_wc-settings' !== $hook ) {
- return;
- }
+ if ( 'woocommerce_page_wc-settings' === $hook ) {
+ $debug = (bool) ( constant( 'WP_DEBUG' ) );
+ $css_ext = ( $debug ) ? '.css' : '.min.css';
+ $js_ext = ( $debug ) ? '.js' : '.min.js';
+ $version_css = filemtime( WOO_ALIPAY_PLUGIN_PATH . 'css/admin/main' . $css_ext );
+ $version_js = filemtime( WOO_ALIPAY_PLUGIN_PATH . 'js/admin/main' . $js_ext );
- // Only load our admin assets on our gateway settings sections, not on the Payments list view.
- $tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : '';
- $section = isset( $_GET['section'] ) ? sanitize_key( wp_unslash( $_GET['section'] ) ) : '';
- $our_sections = array( 'alipay', 'alipay_installment', 'alipay_facetopay' );
- if ( 'checkout' !== $tab || ! in_array( $section, $our_sections, true ) ) {
- return;
- }
+ wp_enqueue_style(
+ 'woo-alipay-main-style',
+ WOO_ALIPAY_PLUGIN_URL . 'css/admin/main' . $css_ext,
+ array(),
+ $version_css
+ );
- $debug = (bool) ( constant( 'WP_DEBUG' ) );
- $css_ext = ( $debug ) ? '.css' : '.min.css';
- $js_ext = ( $debug ) ? '.js' : '.min.js';
- $version_css = filemtime( WOO_ALIPAY_PLUGIN_PATH . 'css/admin/main' . $css_ext );
- $version_js = filemtime( WOO_ALIPAY_PLUGIN_PATH . 'js/admin/main' . $js_ext );
+ $parameters = array(
+ 'ajax_url' => admin_url( 'admin-ajax.php' ),
+ 'debug' => $debug,
+ );
- wp_enqueue_style(
- 'woo-alipay-main-style',
- WOO_ALIPAY_PLUGIN_URL . 'css/admin/main' . $css_ext,
- array(),
- $version_css
- );
-
- $parameters = array(
- 'ajax_url' => admin_url( 'admin-ajax.php' ),
- 'debug' => $debug,
- );
-
- wp_enqueue_script(
- 'woo-alipay-admin-script',
- WOO_ALIPAY_PLUGIN_URL . 'js/admin/main' . $js_ext,
- array( 'jquery' ),
- $version_js,
- true
- );
- wp_localize_script( 'woo-alipay-admin-script', 'WooAlipay', $parameters );
+ wp_enqueue_script(
+ 'woo-alipay-admin-script',
+ WOO_ALIPAY_PLUGIN_URL . 'js/admin/main' . $js_ext,
+ array( 'jquery' ),
+ $version_js,
+ true
+ );
+ wp_localize_script( 'woo-alipay-admin-script', 'WooAlipay', $parameters );
+ }
}
public function add_admin_pages() {
- // If the dedicated Reconcile Pro extension is active, avoid adding a duplicate menu.
- if ( class_exists( 'Woo_Alipay_Reconcile_Admin' ) ) {
- return;
- }
add_submenu_page(
'woocommerce',
__( '支付宝对账工具', 'woo-alipay' ),
@@ -211,88 +194,6 @@ class Woo_Alipay {
);
}
- /**
- * Inject provider links into the WooCommerce Payments providers REST response.
- */
- public function filter_payments_providers_response( $response, $server, $request ) {
- try {
- // Allow site owners to disable this injection for troubleshooting.
- if ( false === apply_filters( 'woo_alipay_enable_payments_links_injection', true ) ) {
- return $response;
- }
-
- if ( ! $response || ! is_a( $response, 'WP_REST_Response' ) ) {
- return $response;
- }
-
- $route = is_object( $request ) && method_exists( $request, 'get_route' ) ? (string) $request->get_route() : '';
- $method = is_object( $request ) && method_exists( $request, 'get_method' ) ? (string) $request->get_method() : '';
-
- // Only act on the Payments providers endpoint used by the settings page (POST /wc-admin/settings/payments/providers).
- if ( false === strpos( $route, '/wc-admin/settings/payments/providers' ) || 'POST' !== strtoupper( $method ) ) {
- return $response;
- }
-
- $data = $response->get_data();
- if ( ! is_array( $data ) || empty( $data['providers'] ) || ! is_array( $data['providers'] ) ) {
- return $response;
- }
-
- $pricing_url = apply_filters( 'woo_alipay_pricing_url', 'https://woocn.com/product/woo-alipay.html#pricing' );
- $about_url = apply_filters( 'woo_alipay_learn_more_url', 'https://woocn.com/document/woo-alipay' );
- $terms_url = apply_filters( 'woo_alipay_terms_url', 'https://woocn.com/terms' );
- $docs_url = apply_filters( 'woo_alipay_docs_url', 'https://woocn.com/document/woo-alipay' );
- $support_url = apply_filters( 'woo_alipay_support_url', 'https://woocn.com/support' );
-
- $target_ids = array( 'alipay', 'alipay_installment', 'alipay_facetopay' );
-
- foreach ( $data['providers'] as $idx => $provider ) {
- if ( ! is_array( $provider ) || empty( $provider['id'] ) || ! in_array( $provider['id'], $target_ids, true ) ) {
- continue;
- }
-
- $links = array();
- if ( isset( $provider['links'] ) && is_array( $provider['links'] ) ) {
- $links = $provider['links'];
- }
-
- // Track existing link types to avoid duplicates.
- $existing_types = array();
- foreach ( $links as $link ) {
- if ( is_array( $link ) && ! empty( $link['_type'] ) ) {
- $existing_types[] = $link['_type'];
- }
- }
-
- $to_add = array(
- array( '_type' => 'pricing', 'url' => esc_url_raw( $pricing_url ) ),
- array( '_type' => 'about', 'url' => esc_url_raw( $about_url ) ),
- array( '_type' => 'terms', 'url' => esc_url_raw( $terms_url ) ),
- array( '_type' => 'documentation', 'url' => esc_url_raw( $docs_url ) ),
- array( '_type' => 'support', 'url' => esc_url_raw( $support_url ) ),
- );
-
- foreach ( $to_add as $entry ) {
- if ( empty( $entry['_type'] ) || empty( $entry['url'] ) ) {
- continue;
- }
- if ( in_array( $entry['_type'], $existing_types, true ) ) {
- continue;
- }
- $links[] = $entry;
- }
-
- $data['providers'][ $idx ]['links'] = $links;
- }
-
- $response->set_data( $data );
- } catch ( \Throwable $e ) {
- // Fail-safe: do not block the response.
- }
-
- return $response;
- }
-
public function render_admin_reconcile_page() {
if ( ! current_user_can( 'manage_woocommerce' ) ) {
wp_die( __( '权限不足', 'woo-alipay' ) );
@@ -347,15 +248,16 @@ class Woo_Alipay {
echo '';
}
- public function add_gateway( $methods ) {
+ public function add_gateway( $methods ) {
$methods[] = 'WC_Alipay';
+ $methods[] = 'WC_Alipay_Installment';
+ $methods[] = 'WC_Alipay_FaceToPay';
- // Extension gateways are registered by their own plugins now.
return $methods;
}
public function plugin_edit_link( $links ) {
- $url = admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=alipay' );
+ $url = admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=woo_alipay' );
return array_merge(
array(
@@ -429,12 +331,25 @@ class Woo_Alipay {
if ( file_exists( WOO_ALIPAY_PLUGIN_PATH . 'inc/class-wc-alipay-blocks-support.php' ) ) {
require_once WOO_ALIPAY_PLUGIN_PATH . 'inc/class-wc-alipay-blocks-support.php';
}
+ if ( file_exists( WOO_ALIPAY_PLUGIN_PATH . 'inc/class-wc-alipay-installment-blocks-support.php' ) ) {
+ require_once WOO_ALIPAY_PLUGIN_PATH . 'inc/class-wc-alipay-installment-blocks-support.php';
+ }
+ if ( file_exists( WOO_ALIPAY_PLUGIN_PATH . 'inc/class-wc-alipay-facetopay-blocks-support.php' ) ) {
+ require_once WOO_ALIPAY_PLUGIN_PATH . 'inc/class-wc-alipay-facetopay-blocks-support.php';
+ }
-
- // 注册主支付宝网关(扩展的 Blocks 支持由扩展自身注册)
+ // 注册主支付宝网关
if ( class_exists( 'WC_Alipay_Blocks_Support' ) ) {
$payment_method_registry->register( new WC_Alipay_Blocks_Support() );
}
+ // 注册花呗分期网关
+ if ( class_exists( 'WC_Alipay_Installment_Blocks_Support' ) ) {
+ $payment_method_registry->register( new WC_Alipay_Installment_Blocks_Support() );
+ }
+ // 注册当面付网关
+ if ( class_exists( 'WC_Alipay_FaceToPay_Blocks_Support' ) ) {
+ $payment_method_registry->register( new WC_Alipay_FaceToPay_Blocks_Support() );
+ }
}
);
}
diff --git a/inc/templates/payment-qrcode.php b/inc/templates/payment-qrcode.php
new file mode 100644
index 00000000..732a9b58
--- /dev/null
+++ b/inc/templates/payment-qrcode.php
@@ -0,0 +1,66 @@
+
+
+
+>
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/js/admin/main.js b/js/admin/main.js
index ec90e4c5..094a4155 100755
--- a/js/admin/main.js
+++ b/js/admin/main.js
@@ -1,7 +1,6 @@
/* global WooAlipay */
jQuery( document ).ready( function( $ ) {
-
$( '#woo-alipay-test-connection, #woo_alipay_test_connection' ).on( 'click', function( e ) {
e.preventDefault();
diff --git a/js/alipay-facetopay.js b/js/alipay-facetopay.js
new file mode 100644
index 00000000..f7bd255c
--- /dev/null
+++ b/js/alipay-facetopay.js
@@ -0,0 +1,209 @@
+/**
+ * 当面付支付前端脚本
+ */
+
+(function($) {
+ 'use strict';
+
+ let pollingTimer = null;
+ let timeoutTimer = null;
+ let remainingTime = 0;
+ let pollingAttempts = 0;
+ let refreshing = false;
+ const maxPollingAttempts = 100; // 最多轮询100次
+
+ const AlipayFaceToPay = {
+ init: function() {
+ const $qrcodeElement = $('#alipay-qrcode');
+
+ if ($qrcodeElement.length > 0 && typeof alipayFaceToPayParams !== 'undefined') {
+ const qrcodeData = $qrcodeElement.data('qrcode');
+ const qrcodeSize = parseInt($qrcodeElement.data('size'), 10) || 300;
+ const orderId = $qrcodeElement.data('order-id');
+
+ if (qrcodeData) {
+ this.generateQRCode(qrcodeData, qrcodeSize);
+ this.startPolling(orderId);
+ this.startTimeout();
+ }
+ }
+ },
+
+ generateQRCode: function(data, size) {
+ try {
+ new QRCode(document.getElementById('alipay-qrcode'), {
+ text: data,
+ width: size,
+ height: size,
+ colorDark: '#000000',
+ colorLight: '#ffffff',
+ correctLevel: QRCode.CorrectLevel.H
+ });
+ } catch (error) {
+ console.error('生成二维码失败:', error);
+ this.showError('生成二维码失败,请刷新页面重试');
+ }
+ },
+
+ startPolling: function(orderId) {
+ const self = this;
+ const nonce = $('#alipay-facetopay-nonce').val();
+
+ pollingTimer = setInterval(function() {
+ if (pollingAttempts >= maxPollingAttempts) {
+ self.stopPolling();
+ return;
+ }
+
+ pollingAttempts++;
+
+ $.ajax({
+ url: alipayFaceToPayParams.ajax_url,
+ type: 'POST',
+ data: {
+ action: 'alipay_facetopay_query',
+ order_id: orderId,
+ nonce: nonce
+ },
+ success: function(response) {
+ if (response.success && response.data.status === 'paid') {
+ self.stopPolling();
+ self.showSuccess();
+
+ setTimeout(function() {
+ window.location.href = response.data.redirect_url;
+ }, 1500);
+ }
+ },
+ error: function(xhr, status, error) {
+ console.error('查询支付状态失败:', error);
+ }
+ });
+ }, alipayFaceToPayParams.polling_interval || 2000);
+ },
+
+ stopPolling: function() {
+ if (pollingTimer) {
+ clearInterval(pollingTimer);
+ pollingTimer = null;
+ }
+ },
+
+ startTimeout: function() {
+ const self = this;
+ remainingTime = alipayFaceToPayParams.timeout || 120;
+
+ this.updateTimer();
+
+ timeoutTimer = setInterval(function() {
+ remainingTime--;
+ self.updateTimer();
+
+ // 即将过期时自动刷新二维码
+ if (remainingTime <= 5 && !refreshing) {
+ self.refreshQRCode();
+ }
+
+ if (remainingTime <= 0) {
+ self.stopPolling();
+ clearInterval(timeoutTimer);
+ self.showExpired();
+ }
+ }, 1000);
+ },
+
+ updateTimer: function() {
+ const $timer = $('.alipay-qrcode-timer');
+
+ if ($timer.length > 0) {
+ const minutes = Math.floor(remainingTime / 60);
+ const seconds = remainingTime % 60;
+ const timeStr = minutes + ':' + (seconds < 10 ? '0' : '') + seconds;
+
+ $timer.text('剩余时间: ' + timeStr);
+
+ if (remainingTime <= 30) {
+ $timer.addClass('warning');
+ }
+ }
+ },
+
+ showSuccess: function() {
+ const $status = $('.alipay-payment-status');
+
+ $status.removeClass('error').addClass('success');
+ $status.html(
+ '✓' +
+ '支付成功!正在跳转...'
+ );
+ },
+
+ showError: function(message) {
+ const $status = $('.alipay-payment-status');
+
+ $status.removeClass('success').addClass('error');
+ $status.html(
+ '✕' +
+ '' + message + ''
+ );
+ },
+
+ refreshQRCode: function() {
+ const self = this;
+ if (refreshing) return;
+ const $qrcodeElement = $('#alipay-qrcode');
+ const orderId = $qrcodeElement.data('order-id');
+ const nonce = $('#alipay-facetopay-nonce').val();
+ refreshing = true;
+ $.ajax({
+ url: alipayFaceToPayParams.ajax_url,
+ type: 'POST',
+ data: {
+ action: 'alipay_facetopay_refresh_qrcode',
+ order_id: orderId,
+ nonce: nonce
+ },
+ success: function(response) {
+ refreshing = false;
+ if (response.success && response.data.qr_code) {
+ // 重新渲染二维码
+ $('#alipay-qrcode').empty();
+ const size = parseInt($qrcodeElement.data('size'), 10) || 300;
+ $('#alipay-qrcode').attr('data-qrcode', response.data.qr_code);
+ AlipayFaceToPay.generateQRCode(response.data.qr_code, size);
+ // 重置计时
+ remainingTime = parseInt(response.data.timeout || 120, 10);
+ }
+ },
+ error: function() { refreshing = false; }
+ });
+ },
+
+ showExpired: function() {
+ const $container = $('.alipay-qrcode-container');
+
+ $container.html(
+ '' +
+ '
⏱
' +
+ '
二维码已过期
' +
+ '
' +
+ '
'
+ );
+ }
+ };
+
+ $(document).ready(function() {
+ AlipayFaceToPay.init();
+ });
+
+ // 页面卸载时停止轮询
+ $(window).on('beforeunload', function() {
+ if (pollingTimer) {
+ clearInterval(pollingTimer);
+ }
+ if (timeoutTimer) {
+ clearInterval(timeoutTimer);
+ }
+ });
+
+})(jQuery);
diff --git a/js/alipay-installment.js b/js/alipay-installment.js
new file mode 100644
index 00000000..2a4baf0f
--- /dev/null
+++ b/js/alipay-installment.js
@@ -0,0 +1,38 @@
+/**
+ * 花呗分期支付前端脚本
+ */
+
+(function($) {
+ 'use strict';
+
+ $(document).ready(function() {
+ // 确保至少选中一个分期选项
+ const $installmentRadios = $('input[name="alipay_installment_period"]');
+
+ if ($installmentRadios.length > 0) {
+ // 如果没有任何选中的,选中第一个
+ if ($installmentRadios.filter(':checked').length === 0) {
+ $installmentRadios.first().prop('checked', true);
+ }
+
+ // 添加点击动画效果
+ $installmentRadios.on('change', function() {
+ $(this).closest('label').addClass('period-selected');
+ $installmentRadios.not(this).closest('label').removeClass('period-selected');
+ });
+ }
+
+ // 在提交订单前验证是否选择了分期期数
+ $('form.checkout').on('checkout_place_order_alipay_installment', function() {
+ const $selectedPeriod = $('input[name="alipay_installment_period"]:checked');
+
+ if ($selectedPeriod.length === 0) {
+ alert('请选择分期期数');
+ return false;
+ }
+
+ return true;
+ });
+ });
+
+})(jQuery);
diff --git a/js/frontend/blocks-facetopay.asset.php b/js/frontend/blocks-facetopay.asset.php
new file mode 100644
index 00000000..31720372
--- /dev/null
+++ b/js/frontend/blocks-facetopay.asset.php
@@ -0,0 +1,9 @@
+ array(
+ 'wc-blocks-registry',
+ 'wp-element',
+ 'wp-html-entities',
+ 'wp-i18n',
+ ),
+ 'version' => '3.1.0',
+);
\ No newline at end of file
diff --git a/js/frontend/blocks-facetopay.js b/js/frontend/blocks-facetopay.js
new file mode 100644
index 00000000..9f11d977
--- /dev/null
+++ b/js/frontend/blocks-facetopay.js
@@ -0,0 +1,81 @@
+(function(){
+const { registerPaymentMethod } = window.wc.wcBlocksRegistry;
+const { createElement } = window.wp.element;
+const { __ } = window.wp.i18n;
+const { decodeEntities } = window.wp.htmlEntities;
+
+const settings = window.wc.wcSettings.getSetting( 'alipay_facetopay_data', {} );
+const defaultLabel = __( '支付宝扫码支付', 'woo-alipay' );
+const defaultDescription = __( '使用支付宝扫描二维码完成支付', 'woo-alipay' );
+
+const Label = ( props ) => {
+ const { PaymentMethodLabel } = props.components;
+ const iconElement = settings.icon ? createElement( 'img', {
+ src: settings.icon,
+ alt: decodeEntities( settings.title || defaultLabel ),
+ style: {
+ width: '24px',
+ height: '24px',
+ marginRight: '8px',
+ verticalAlign: 'middle'
+ }
+ } ) : null;
+
+ return createElement( 'div', {
+ style: { display: 'flex', alignItems: 'center' }
+ }, [
+ iconElement,
+ createElement( PaymentMethodLabel, {
+ text: decodeEntities( settings.title || defaultLabel ),
+ key: 'label'
+ } )
+ ] );
+};
+
+const Content = () => {
+ return createElement( 'div', {
+ style: { padding: '10px 0' }
+ }, [
+ createElement( 'p', {
+ key: 'description',
+ style: { marginBottom: '8px' }
+ }, decodeEntities( settings.description || defaultDescription ) ),
+
+ createElement( 'div', {
+ key: 'qr-info',
+ style: {
+ padding: '10px',
+ background: '#f0f7ff',
+ border: '1px solid #d6e4ff',
+ borderRadius: '4px',
+ fontSize: '13px',
+ color: '#096dd9'
+ }
+ }, [
+ createElement( 'p', {
+ key: 'tip1',
+ style: { margin: '4px 0' }
+ }, __( '点击下单后会显示支付二维码', 'woo-alipay' ) ),
+
+ createElement( 'p', {
+ key: 'tip2',
+ style: { margin: '4px 0' }
+ }, __( '请使用支付宝APP扫描二维码完成支付', 'woo-alipay' ) )
+ ])
+ ]);
+};
+
+const alipayFaceToPayPaymentMethod = {
+ name: 'alipay_facetopay',
+ label: createElement( Label ),
+ content: createElement( Content ),
+ edit: createElement( Content ),
+ canMakePayment: () => true,
+ ariaLabel: decodeEntities( settings.title || defaultLabel ),
+ supports: {
+ features: settings?.supports ?? ['products'],
+ },
+};
+
+registerPaymentMethod( alipayFaceToPayPaymentMethod );
+})();
diff --git a/js/frontend/blocks-installment.asset.php b/js/frontend/blocks-installment.asset.php
new file mode 100644
index 00000000..31720372
--- /dev/null
+++ b/js/frontend/blocks-installment.asset.php
@@ -0,0 +1,9 @@
+ array(
+ 'wc-blocks-registry',
+ 'wp-element',
+ 'wp-html-entities',
+ 'wp-i18n',
+ ),
+ 'version' => '3.1.0',
+);
\ No newline at end of file
diff --git a/js/frontend/blocks-installment.js b/js/frontend/blocks-installment.js
new file mode 100644
index 00000000..1813ea2d
--- /dev/null
+++ b/js/frontend/blocks-installment.js
@@ -0,0 +1,169 @@
+(function(){
+const { registerPaymentMethod } = window.wc.wcBlocksRegistry;
+const { createElement, useState, useEffect } = window.wp.element;
+const { __ } = window.wp.i18n;
+const { decodeEntities } = window.wp.htmlEntities;
+
+const settings = window.wc.wcSettings.getSetting( 'alipay_installment_data', {} );
+const defaultLabel = __( '支付宝花呗分期', 'woo-alipay' );
+const defaultDescription = __( '使用支付宝花呗分期付款,支持3期、6期、12期免息或低息分期。', 'woo-alipay' );
+
+const Label = ( props ) => {
+ const { PaymentMethodLabel } = props.components;
+ const iconElement = settings.icon ? createElement( 'img', {
+ src: settings.icon,
+ alt: decodeEntities( settings.title || defaultLabel ),
+ style: {
+ width: '24px',
+ height: '24px',
+ marginRight: '8px',
+ verticalAlign: 'middle'
+ }
+ } ) : null;
+
+ return createElement( 'div', {
+ style: { display: 'flex', alignItems: 'center' }
+ }, [
+ iconElement,
+ createElement( PaymentMethodLabel, {
+ text: decodeEntities( settings.title || defaultLabel ),
+ key: 'label'
+ } )
+ ] );
+};
+
+const Content = ( props ) => {
+ // 检查是否满足最小金额要求
+ const meetsMinAmount = settings.meetsMinAmount || false;
+ const minAmount = settings.minAmount || 100;
+ const cartTotal = settings.cartTotal || 0;
+ const availablePeriods = settings.availablePeriods || ['3', '6', '12'];
+ const defaultPeriod = settings.defaultPeriod || '3';
+
+ const [ selectedPeriod, setSelectedPeriod ] = useState( String(defaultPeriod) );
+
+ // 将选择的分期数传递到服务端(Store API -> payment_data)
+ useEffect( () => {
+ if ( ! props?.eventRegistration ) return;
+ const unsubscribe = props.eventRegistration.onPaymentSetup( () => {
+ return {
+ type: 'success',
+ paymentMethodData: {
+ alipay_installment_period: selectedPeriod,
+ },
+ };
+ } );
+ return () => {
+ if ( typeof unsubscribe === 'function' ) unsubscribe();
+ };
+ }, [ selectedPeriod ] );
+
+ // 如果不满足最小金额,显示提示
+ if (!meetsMinAmount) {
+ return createElement( 'div', {
+ style: {
+ padding: '10px',
+ background: '#fff3cd',
+ border: '1px solid #ffc107',
+ borderRadius: '4px',
+ color: '#856404',
+ marginTop: '10px'
+ }
+ }, __( '订单金额需满 ¥' + minAmount + ' 才能使用花呗分期', 'woo-alipay' ) );
+ }
+
+ return createElement( 'div', {
+ style: { padding: '10px 0' }
+ }, [
+ createElement( 'p', {
+ key: 'description',
+ style: { marginBottom: '15px' }
+ }, decodeEntities( settings.description || defaultDescription ) ),
+ createElement( 'p', {
+ key: 'fee-note',
+ style: { margin: '6px 0', fontSize: '12px', color: '#666' }
+ }, settings.feePayer === 'seller' ? __( '免息分期(手续费由商家承担)', 'woo-alipay' ) : __( '可能产生分期手续费,以支付宝支付页面为准', 'woo-alipay' ) ),
+
+ createElement( 'div', {
+ key: 'period-info',
+ style: {
+ padding: '12px',
+ background: '#f9f9f9',
+ border: '1px solid #e0e0e0',
+ borderRadius: '4px',
+ marginTop: '10px'
+ }
+ }, [
+ createElement( 'p', {
+ key: 'title',
+ style: {
+ fontWeight: '600',
+ marginBottom: '8px',
+ fontSize: '14px'
+ }
+ }, __( '选择分期期数:', 'woo-alipay' ) ),
+
+ createElement( 'div', {
+ key: 'periods',
+ style: {
+ margin: '0',
+ padding: '0',
+ display: 'grid',
+ gap: '6px'
+ }
+ }, availablePeriods.map(period => {
+ const monthlyAmount = (cartTotal / parseInt(period)).toFixed(2);
+ const id = 'alipay-installment-' + period;
+ return createElement( 'label', {
+ key: period,
+ htmlFor: id,
+ style: { display: 'flex', alignItems: 'center', gap: '8px', fontSize: '13px' }
+ }, [
+ createElement( 'input', {
+ id,
+ type: 'radio',
+ name: 'alipay_installment_period',
+ value: String(period),
+ checked: String(selectedPeriod) === String(period),
+ onChange: () => setSelectedPeriod(String(period))
+ } ),
+ createElement( 'span', { key: 'label-' + period }, [
+ period + ' 期(每期 ¥' + monthlyAmount,
+ (settings.feePayer === 'seller' && settings.showInterestFreeBadge) ? createElement('span', { key: 'free-'+period, className: 'alipay-badge alipay-badge--free', style: { marginLeft: '4px' } }, __('免息', 'woo-alipay')) : null,
+ ')'
+ ] )
+ ] );
+ }) )
+ ]),
+
+ createElement( 'p', {
+ key: 'note',
+ style: {
+ marginTop: '10px',
+ fontSize: '12px',
+ color: '#666'
+ }
+ }, __( '在支付页面将可以选择具体的分期期数', 'woo-alipay' ) )
+ ]);
+};
+
+const alipayInstallmentPaymentMethod = {
+ name: 'alipay_installment',
+ paymentMethodId: 'alipay_installment',
+ label: createElement( Label ),
+ content: createElement( Content ),
+ edit: createElement( Content ),
+ canMakePayment: () => {
+ const behavior = settings.insufficientBehavior || 'hide';
+ const meets = !!settings.meetsMinAmount;
+ if ( behavior === 'hide' && !meets ) return false;
+ return true;
+ },
+ ariaLabel: decodeEntities( settings.title || defaultLabel ),
+ supports: {
+ features: settings?.supports ?? ['products'],
+ },
+};
+
+registerPaymentMethod( alipayInstallmentPaymentMethod );
+})();
diff --git a/vendor/adbario/php-dot-notation/LICENSE.md b/vendor/adbario/php-dot-notation/LICENSE.md
new file mode 100644
index 00000000..fe013238
--- /dev/null
+++ b/vendor/adbario/php-dot-notation/LICENSE.md
@@ -0,0 +1,21 @@
+# The MIT License (MIT)
+
+Copyright (c) 2016-2019 Riku Särkinen
+
+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.
diff --git a/vendor/adbario/php-dot-notation/composer.json b/vendor/adbario/php-dot-notation/composer.json
new file mode 100644
index 00000000..cea71261
--- /dev/null
+++ b/vendor/adbario/php-dot-notation/composer.json
@@ -0,0 +1,29 @@
+{
+ "name": "adbario/php-dot-notation",
+ "description": "PHP dot notation access to arrays",
+ "keywords": ["dotnotation", "arrayaccess"],
+ "homepage": "https://github.com/adbario/php-dot-notation",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Riku Särkinen",
+ "email": "riku@adbar.io"
+ }
+ ],
+ "require": {
+ "php": "^5.5 || ^7.0 || ^8.0",
+ "ext-json": "*"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8|^5.7|^6.6|^7.5|^8.5|^9.5",
+ "squizlabs/php_codesniffer": "^3.6"
+ },
+ "autoload": {
+ "files": [
+ "src/helpers.php"
+ ],
+ "psr-4": {
+ "Adbar\\": "src"
+ }
+ }
+}
diff --git a/vendor/adbario/php-dot-notation/src/Dot.php b/vendor/adbario/php-dot-notation/src/Dot.php
new file mode 100644
index 00000000..3cd1c501
--- /dev/null
+++ b/vendor/adbario/php-dot-notation/src/Dot.php
@@ -0,0 +1,623 @@
+
+ * @link https://github.com/adbario/php-dot-notation
+ * @license https://github.com/adbario/php-dot-notation/blob/2.x/LICENSE.md (MIT License)
+ */
+namespace Adbar;
+
+use Countable;
+use ArrayAccess;
+use ArrayIterator;
+use JsonSerializable;
+use IteratorAggregate;
+
+/**
+ * Dot
+ *
+ * This class provides a dot notation access and helper functions for
+ * working with arrays of data. Inspired by Laravel Collection.
+ */
+class Dot implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable
+{
+ /**
+ * The stored items
+ *
+ * @var array
+ */
+ protected $items = [];
+
+
+ /**
+ * The delimiter (alternative to a '.') to be used.
+ *
+ * @var string
+ */
+ protected $delimiter = '.';
+
+
+ /**
+ * Create a new Dot instance
+ *
+ * @param mixed $items
+ * @param string $delimiter
+ */
+ public function __construct($items = [], $delimiter = '.')
+ {
+ $this->items = $this->getArrayItems($items);
+ $this->delimiter = strlen($delimiter) ? $delimiter : '.';
+ }
+
+ /**
+ * Set a given key / value pair or pairs
+ * if the key doesn't exist already
+ *
+ * @param array|int|string $keys
+ * @param mixed $value
+ */
+ public function add($keys, $value = null)
+ {
+ if (is_array($keys)) {
+ foreach ($keys as $key => $value) {
+ $this->add($key, $value);
+ }
+ } elseif (is_null($this->get($keys))) {
+ $this->set($keys, $value);
+ }
+ }
+
+ /**
+ * Return all the stored items
+ *
+ * @return array
+ */
+ public function all()
+ {
+ return $this->items;
+ }
+
+ /**
+ * Delete the contents of a given key or keys
+ *
+ * @param array|int|string|null $keys
+ */
+ public function clear($keys = null)
+ {
+ if (is_null($keys)) {
+ $this->items = [];
+
+ return;
+ }
+
+ $keys = (array) $keys;
+
+ foreach ($keys as $key) {
+ $this->set($key, []);
+ }
+ }
+
+ /**
+ * Delete the given key or keys
+ *
+ * @param array|int|string $keys
+ */
+ public function delete($keys)
+ {
+ $keys = (array) $keys;
+
+ foreach ($keys as $key) {
+ if ($this->exists($this->items, $key)) {
+ unset($this->items[$key]);
+
+ continue;
+ }
+
+ $items = &$this->items;
+ $segments = explode($this->delimiter, $key);
+ $lastSegment = array_pop($segments);
+
+ foreach ($segments as $segment) {
+ if (!isset($items[$segment]) || !is_array($items[$segment])) {
+ continue 2;
+ }
+
+ $items = &$items[$segment];
+ }
+
+ unset($items[$lastSegment]);
+ }
+ }
+
+ /**
+ * Checks if the given key exists in the provided array.
+ *
+ * @param array $array Array to validate
+ * @param int|string $key The key to look for
+ *
+ * @return bool
+ */
+ protected function exists($array, $key)
+ {
+ return array_key_exists($key, $array);
+ }
+
+ /**
+ * Flatten an array with the given character as a key delimiter
+ *
+ * @param string $delimiter
+ * @param array|null $items
+ * @param string $prepend
+ * @return array
+ */
+ public function flatten($delimiter = '.', $items = null, $prepend = '')
+ {
+ $flatten = [];
+
+ if (is_null($items)) {
+ $items = $this->items;
+ }
+
+ if (!func_num_args()) {
+ $delimiter = $this->delimiter;
+ }
+
+ foreach ($items as $key => $value) {
+ if (is_array($value) && !empty($value)) {
+ $flatten = array_merge(
+ $flatten,
+ $this->flatten($delimiter, $value, $prepend.$key.$delimiter)
+ );
+ } else {
+ $flatten[$prepend.$key] = $value;
+ }
+ }
+
+ return $flatten;
+ }
+
+ /**
+ * Return the value of a given key
+ *
+ * @param int|string|null $key
+ * @param mixed $default
+ * @return mixed
+ */
+ public function get($key = null, $default = null)
+ {
+ if (is_null($key)) {
+ return $this->items;
+ }
+
+ if ($this->exists($this->items, $key)) {
+ return $this->items[$key];
+ }
+
+ if (strpos($key, $this->delimiter) === false) {
+ return $default;
+ }
+
+ $items = $this->items;
+
+ foreach (explode($this->delimiter, $key) as $segment) {
+ if (!is_array($items) || !$this->exists($items, $segment)) {
+ return $default;
+ }
+
+ $items = &$items[$segment];
+ }
+
+ return $items;
+ }
+
+ /**
+ * Return the given items as an array
+ *
+ * @param mixed $items
+ * @return array
+ */
+ protected function getArrayItems($items)
+ {
+ if (is_array($items)) {
+ return $items;
+ } elseif ($items instanceof self) {
+ return $items->all();
+ }
+
+ return (array) $items;
+ }
+
+ /**
+ * Check if a given key or keys exists
+ *
+ * @param array|int|string $keys
+ * @return bool
+ */
+ public function has($keys)
+ {
+ $keys = (array) $keys;
+
+ if (!$this->items || $keys === []) {
+ return false;
+ }
+
+ foreach ($keys as $key) {
+ $items = $this->items;
+
+ if ($this->exists($items, $key)) {
+ continue;
+ }
+
+ foreach (explode($this->delimiter, $key) as $segment) {
+ if (!is_array($items) || !$this->exists($items, $segment)) {
+ return false;
+ }
+
+ $items = $items[$segment];
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Check if a given key or keys are empty
+ *
+ * @param array|int|string|null $keys
+ * @return bool
+ */
+ public function isEmpty($keys = null)
+ {
+ if (is_null($keys)) {
+ return empty($this->items);
+ }
+
+ $keys = (array) $keys;
+
+ foreach ($keys as $key) {
+ if (!empty($this->get($key))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Merge a given array or a Dot object with the given key
+ * or with the whole Dot object
+ *
+ * @param array|string|self $key
+ * @param array|self $value
+ */
+ public function merge($key, $value = [])
+ {
+ if (is_array($key)) {
+ $this->items = array_merge($this->items, $key);
+ } elseif (is_string($key)) {
+ $items = (array) $this->get($key);
+ $value = array_merge($items, $this->getArrayItems($value));
+
+ $this->set($key, $value);
+ } elseif ($key instanceof self) {
+ $this->items = array_merge($this->items, $key->all());
+ }
+ }
+
+ /**
+ * Recursively merge a given array or a Dot object with the given key
+ * or with the whole Dot object.
+ *
+ * Duplicate keys are converted to arrays.
+ *
+ * @param array|string|self $key
+ * @param array|self $value
+ */
+ public function mergeRecursive($key, $value = [])
+ {
+ if (is_array($key)) {
+ $this->items = array_merge_recursive($this->items, $key);
+ } elseif (is_string($key)) {
+ $items = (array) $this->get($key);
+ $value = array_merge_recursive($items, $this->getArrayItems($value));
+
+ $this->set($key, $value);
+ } elseif ($key instanceof self) {
+ $this->items = array_merge_recursive($this->items, $key->all());
+ }
+ }
+
+ /**
+ * Recursively merge a given array or a Dot object with the given key
+ * or with the whole Dot object.
+ *
+ * Instead of converting duplicate keys to arrays, the value from
+ * given array will replace the value in Dot object.
+ *
+ * @param array|string|self $key
+ * @param array|self $value
+ */
+ public function mergeRecursiveDistinct($key, $value = [])
+ {
+ if (is_array($key)) {
+ $this->items = $this->arrayMergeRecursiveDistinct($this->items, $key);
+ } elseif (is_string($key)) {
+ $items = (array) $this->get($key);
+ $value = $this->arrayMergeRecursiveDistinct($items, $this->getArrayItems($value));
+
+ $this->set($key, $value);
+ } elseif ($key instanceof self) {
+ $this->items = $this->arrayMergeRecursiveDistinct($this->items, $key->all());
+ }
+ }
+
+ /**
+ * Merges two arrays recursively. In contrast to array_merge_recursive,
+ * duplicate keys are not converted to arrays but rather overwrite the
+ * value in the first array with the duplicate value in the second array.
+ *
+ * @param array $array1 Initial array to merge
+ * @param array $array2 Array to recursively merge
+ * @return array
+ */
+ protected function arrayMergeRecursiveDistinct(array $array1, array $array2)
+ {
+ $merged = &$array1;
+
+ foreach ($array2 as $key => $value) {
+ if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
+ $merged[$key] = $this->arrayMergeRecursiveDistinct($merged[$key], $value);
+ } else {
+ $merged[$key] = $value;
+ }
+ }
+
+ return $merged;
+ }
+
+ /**
+ * Return the value of a given key and
+ * delete the key
+ *
+ * @param int|string|null $key
+ * @param mixed $default
+ * @return mixed
+ */
+ public function pull($key = null, $default = null)
+ {
+ if (is_null($key)) {
+ $value = $this->all();
+ $this->clear();
+
+ return $value;
+ }
+
+ $value = $this->get($key, $default);
+ $this->delete($key);
+
+ return $value;
+ }
+
+ /**
+ * Push a given value to the end of the array
+ * in a given key
+ *
+ * @param mixed $key
+ * @param mixed $value
+ */
+ public function push($key, $value = null)
+ {
+ if (is_null($value)) {
+ $this->items[] = $key;
+
+ return;
+ }
+
+ $items = $this->get($key);
+
+ if (is_array($items) || is_null($items)) {
+ $items[] = $value;
+ $this->set($key, $items);
+ }
+ }
+
+ /**
+ * Replace all values or values within the given key
+ * with an array or Dot object
+ *
+ * @param array|string|self $key
+ * @param array|self $value
+ */
+ public function replace($key, $value = [])
+ {
+ if (is_array($key)) {
+ $this->items = array_replace($this->items, $key);
+ } elseif (is_string($key)) {
+ $items = (array) $this->get($key);
+ $value = array_replace($items, $this->getArrayItems($value));
+
+ $this->set($key, $value);
+ } elseif ($key instanceof self) {
+ $this->items = array_replace($this->items, $key->all());
+ }
+ }
+
+ /**
+ * Set a given key / value pair or pairs
+ *
+ * @param array|int|string $keys
+ * @param mixed $value
+ */
+ public function set($keys, $value = null)
+ {
+ if (is_array($keys)) {
+ foreach ($keys as $key => $value) {
+ $this->set($key, $value);
+ }
+
+ return;
+ }
+
+ $items = &$this->items;
+
+ foreach (explode($this->delimiter, $keys) as $key) {
+ if (!isset($items[$key]) || !is_array($items[$key])) {
+ $items[$key] = [];
+ }
+
+ $items = &$items[$key];
+ }
+
+ $items = $value;
+ }
+
+ /**
+ * Replace all items with a given array
+ *
+ * @param mixed $items
+ */
+ public function setArray($items)
+ {
+ $this->items = $this->getArrayItems($items);
+ }
+
+ /**
+ * Replace all items with a given array as a reference
+ *
+ * @param array $items
+ */
+ public function setReference(array &$items)
+ {
+ $this->items = &$items;
+ }
+
+ /**
+ * Return the value of a given key or all the values as JSON
+ *
+ * @param mixed $key
+ * @param int $options
+ * @return string
+ */
+ public function toJson($key = null, $options = 0)
+ {
+ if (is_string($key)) {
+ return json_encode($this->get($key), $options);
+ }
+
+ $options = $key === null ? 0 : $key;
+
+ return json_encode($this->items, $options);
+ }
+
+ /*
+ * --------------------------------------------------------------
+ * ArrayAccess interface
+ * --------------------------------------------------------------
+ */
+
+ /**
+ * Check if a given key exists
+ *
+ * @param int|string $key
+ * @return bool
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetExists($key)
+ {
+ return $this->has($key);
+ }
+
+ /**
+ * Return the value of a given key
+ *
+ * @param int|string $key
+ * @return mixed
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetGet($key)
+ {
+ return $this->get($key);
+ }
+
+ /**
+ * Set a given value to the given key
+ *
+ * @param int|string|null $key
+ * @param mixed $value
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetSet($key, $value)
+ {
+ if (is_null($key)) {
+ $this->items[] = $value;
+
+ return;
+ }
+
+ $this->set($key, $value);
+ }
+
+ /**
+ * Delete the given key
+ *
+ * @param int|string $key
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetUnset($key)
+ {
+ $this->delete($key);
+ }
+
+ /*
+ * --------------------------------------------------------------
+ * Countable interface
+ * --------------------------------------------------------------
+ */
+
+ /**
+ * Return the number of items in a given key
+ *
+ * @param int|string|null $key
+ * @return int
+ */
+ #[\ReturnTypeWillChange]
+ public function count($key = null)
+ {
+ return count($this->get($key));
+ }
+
+ /*
+ * --------------------------------------------------------------
+ * IteratorAggregate interface
+ * --------------------------------------------------------------
+ */
+
+ /**
+ * Get an iterator for the stored items
+ *
+ * @return \ArrayIterator
+ */
+ #[\ReturnTypeWillChange]
+ public function getIterator()
+ {
+ return new ArrayIterator($this->items);
+ }
+
+ /*
+ * --------------------------------------------------------------
+ * JsonSerializable interface
+ * --------------------------------------------------------------
+ */
+
+ /**
+ * Return items for JSON serialization
+ *
+ * @return array
+ */
+ #[\ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ return $this->items;
+ }
+}
diff --git a/vendor/adbario/php-dot-notation/src/helpers.php b/vendor/adbario/php-dot-notation/src/helpers.php
new file mode 100644
index 00000000..bebb9527
--- /dev/null
+++ b/vendor/adbario/php-dot-notation/src/helpers.php
@@ -0,0 +1,24 @@
+
+ * @link https://github.com/adbario/php-dot-notation
+ * @license https://github.com/adbario/php-dot-notation/blob/2.x/LICENSE.md (MIT License)
+ */
+
+use Adbar\Dot;
+
+if (! function_exists('dot')) {
+ /**
+ * Create a new Dot object with the given items and optional delimiter
+ *
+ * @param mixed $items
+ * @param string $delimiter
+ * @return \Adbar\Dot
+ */
+ function dot($items, $delimiter = '.')
+ {
+ return new Dot($items, $delimiter);
+ }
+}
diff --git a/vendor/alibabacloud/tea-fileform/.gitignore b/vendor/alibabacloud/tea-fileform/.gitignore
new file mode 100644
index 00000000..84837df3
--- /dev/null
+++ b/vendor/alibabacloud/tea-fileform/.gitignore
@@ -0,0 +1,12 @@
+composer.phar
+/vendor/
+
+# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control
+# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
+composer.lock
+
+.idea
+.DS_Store
+
+cache/
+*.cache
diff --git a/vendor/alibabacloud/tea-fileform/.php_cs.dist b/vendor/alibabacloud/tea-fileform/.php_cs.dist
new file mode 100644
index 00000000..8617ec2f
--- /dev/null
+++ b/vendor/alibabacloud/tea-fileform/.php_cs.dist
@@ -0,0 +1,65 @@
+setRiskyAllowed(true)
+ ->setIndent(' ')
+ ->setRules([
+ '@PSR2' => true,
+ '@PhpCsFixer' => true,
+ '@Symfony:risky' => true,
+ 'concat_space' => ['spacing' => 'one'],
+ 'array_syntax' => ['syntax' => 'short'],
+ 'array_indentation' => true,
+ 'combine_consecutive_unsets' => true,
+ 'method_separation' => true,
+ 'single_quote' => true,
+ 'declare_equal_normalize' => true,
+ 'function_typehint_space' => true,
+ 'hash_to_slash_comment' => true,
+ 'include' => true,
+ 'lowercase_cast' => true,
+ 'no_multiline_whitespace_before_semicolons' => true,
+ 'no_leading_import_slash' => true,
+ 'no_multiline_whitespace_around_double_arrow' => true,
+ 'no_spaces_around_offset' => true,
+ 'no_unneeded_control_parentheses' => true,
+ 'no_unused_imports' => true,
+ 'no_whitespace_before_comma_in_array' => true,
+ 'no_whitespace_in_blank_line' => true,
+ 'object_operator_without_whitespace' => true,
+ 'single_blank_line_before_namespace' => true,
+ 'single_class_element_per_statement' => true,
+ 'space_after_semicolon' => true,
+ 'standardize_not_equals' => true,
+ 'ternary_operator_spaces' => true,
+ 'trailing_comma_in_multiline_array' => true,
+ 'trim_array_spaces' => true,
+ 'unary_operator_spaces' => true,
+ 'whitespace_after_comma_in_array' => true,
+ 'no_extra_consecutive_blank_lines' => [
+ 'curly_brace_block',
+ 'extra',
+ 'parenthesis_brace_block',
+ 'square_brace_block',
+ 'throw',
+ 'use',
+ ],
+ 'binary_operator_spaces' => [
+ 'align_double_arrow' => true,
+ 'align_equals' => true,
+ ],
+ 'braces' => [
+ 'allow_single_line_closure' => true,
+ ],
+ ])
+ ->setFinder(
+ PhpCsFixer\Finder::create()
+ ->exclude('vendor')
+ ->exclude('tests')
+ ->in(__DIR__)
+ );
diff --git a/vendor/alibabacloud/tea-fileform/README-CN.md b/vendor/alibabacloud/tea-fileform/README-CN.md
new file mode 100644
index 00000000..8d252382
--- /dev/null
+++ b/vendor/alibabacloud/tea-fileform/README-CN.md
@@ -0,0 +1,31 @@
+English | [简体中文](README-CN.md)
+
+
+
+## Alibaba Cloud Tea File Library for PHP
+
+## Installation
+
+### Composer
+
+```bash
+composer require alibabacloud/tea-fileform
+```
+
+## Issues
+
+[Opening an Issue](https://github.com/aliyun/tea-fileform/issues/new), Issues not conforming to the guidelines may be closed immediately.
+
+## Changelog
+
+Detailed changes for each release are documented in the [release notes](./ChangeLog.txt).
+
+## References
+
+* [Latest Release](https://github.com/aliyun/tea-fileform)
+
+## License
+
+[Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0)
+
+Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
diff --git a/vendor/alibabacloud/tea-fileform/README.md b/vendor/alibabacloud/tea-fileform/README.md
new file mode 100644
index 00000000..9917f3c0
--- /dev/null
+++ b/vendor/alibabacloud/tea-fileform/README.md
@@ -0,0 +1,31 @@
+[English](README.md) | 简体中文
+
+
+
+## Alibaba Cloud Tea File Library for PHP
+
+## 安装
+
+### Composer
+
+```bash
+composer require alibabacloud/tea-fileform
+```
+
+## 问题
+
+[提交 Issue](https://github.com/aliyun/tea-fileform/issues/new),不符合指南的问题可能会立即关闭。
+
+## 发行说明
+
+每个版本的详细更改记录在[发行说明](./ChangeLog.txt)中。
+
+## 相关
+
+* [最新源码](https://github.com/aliyun/tea-fileform)
+
+## 许可证
+
+[Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0)
+
+Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
diff --git a/vendor/alibabacloud/tea-fileform/composer.json b/vendor/alibabacloud/tea-fileform/composer.json
new file mode 100644
index 00000000..151fe7b7
--- /dev/null
+++ b/vendor/alibabacloud/tea-fileform/composer.json
@@ -0,0 +1,44 @@
+{
+ "name": "alibabacloud/tea-fileform",
+ "description": "Alibaba Cloud Tea File Library for PHP",
+ "type": "library",
+ "license": "Apache-2.0",
+ "authors": [
+ {
+ "name": "Alibaba Cloud SDK",
+ "email": "sdk-team@alibabacloud.com"
+ }
+ ],
+ "require": {
+ "php": ">5.5",
+ "alibabacloud/tea": "^3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.35|^5.4.3"
+ },
+ "autoload": {
+ "psr-4": {
+ "AlibabaCloud\\Tea\\FileForm\\": "src"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "AlibabaCloud\\Tea\\FileForm\\Tests\\": "tests"
+ }
+ },
+ "scripts": {
+ "fixer": "php-cs-fixer fix ./",
+ "test": [
+ "@clearCache",
+ "phpunit --colors=always"
+ ],
+ "clearCache": "rm -rf cache/*"
+ },
+ "config": {
+ "sort-packages": true,
+ "preferred-install": "dist",
+ "optimize-autoloader": true
+ },
+ "prefer-stable": true,
+ "minimum-stability": "dev"
+}
diff --git a/vendor/alibabacloud/tea-fileform/phpunit.xml b/vendor/alibabacloud/tea-fileform/phpunit.xml
new file mode 100644
index 00000000..8306a799
--- /dev/null
+++ b/vendor/alibabacloud/tea-fileform/phpunit.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+ tests
+
+
+ ./tests/Unit
+
+
+
+
+
+ integration
+
+
+
+
+
+
+
+
+
+
+
+ ./src
+
+
+
diff --git a/vendor/alibabacloud/tea-fileform/src/FileForm.php b/vendor/alibabacloud/tea-fileform/src/FileForm.php
new file mode 100644
index 00000000..ee44cbb5
--- /dev/null
+++ b/vendor/alibabacloud/tea-fileform/src/FileForm.php
@@ -0,0 +1,16 @@
+_required = [
+ 'filename' => true,
+ 'contentType' => true,
+ 'content' => true,
+ ];
+ parent::__construct($config);
+ }
+}
diff --git a/vendor/alibabacloud/tea-fileform/src/FileFormStream.php b/vendor/alibabacloud/tea-fileform/src/FileFormStream.php
new file mode 100644
index 00000000..a2cc2462
--- /dev/null
+++ b/vendor/alibabacloud/tea-fileform/src/FileFormStream.php
@@ -0,0 +1,321 @@
+stream = fopen('php://memory', 'a+');
+ $this->form = $map;
+ $this->boundary = $boundary;
+ $this->keys = array_keys($map);
+ do {
+ $read = $this->readForm(1024);
+ } while (null !== $read);
+ $meta = stream_get_meta_data($this->stream);
+ $this->seekable = $meta['seekable'];
+ $this->uri = $this->getMetadata('uri');
+ $this->seek(0);
+ $this->seek(0);
+ }
+
+ /**
+ * Closes the stream when the destructed.
+ */
+ public function __destruct()
+ {
+ $this->close();
+ }
+
+ public function __toString()
+ {
+ try {
+ $this->seek(0);
+
+ return (string) stream_get_contents($this->stream);
+ } catch (\Exception $e) {
+ return '';
+ }
+ }
+
+ /**
+ * @param int $length
+ *
+ * @return false|int|string
+ */
+ public function readForm($length)
+ {
+ if ($this->streaming) {
+ if (null !== $this->currStream) {
+ // @var string $content
+ $content = $this->currStream->read($length);
+ if (false !== $content && '' !== $content) {
+ fwrite($this->stream, $content);
+
+ return $content;
+ }
+
+ return $this->next("\r\n");
+ }
+
+ return $this->next();
+ }
+ $keysCount = \count($this->keys);
+ if ($this->index > $keysCount) {
+ return null;
+ }
+ if ($keysCount > 0) {
+ if ($this->index < $keysCount) {
+ $this->streaming = true;
+
+ $name = $this->keys[$this->index];
+ $field = $this->form[$name];
+ if (!empty($field) && $field instanceof FileField) {
+ if (!empty($field->content)) {
+ $this->currStream = $field->content;
+
+ $str = '--' . $this->boundary . "\r\n" .
+ 'Content-Disposition: form-data; name="' . $name . '"; filename="' . $field->filename . "\"\r\n" .
+ 'Content-Type: ' . $field->contentType . "\r\n\r\n";
+ $this->write($str);
+
+ return $str;
+ }
+
+ return $this->next();
+ }
+ $val = $field;
+ $str = '--' . $this->boundary . "\r\n" .
+ 'Content-Disposition: form-data; name="' . $name . "\"\r\n\r\n" .
+ $val . "\r\n";
+ fwrite($this->stream, $str);
+
+ return $str;
+ }
+ if ($this->index == $keysCount) {
+ return $this->next('--' . $this->boundary . "--\r\n");
+ }
+
+ return null;
+ }
+
+ return null;
+ }
+
+ public function getContents()
+ {
+ if (!isset($this->stream)) {
+ throw new \RuntimeException('Stream is detached');
+ }
+
+ $contents = stream_get_contents($this->stream);
+
+ if (false === $contents) {
+ throw new \RuntimeException('Unable to read stream contents');
+ }
+
+ return $contents;
+ }
+
+ public function close()
+ {
+ if (isset($this->stream)) {
+ if (\is_resource($this->stream)) {
+ fclose($this->stream);
+ }
+ $this->detach();
+ }
+ }
+
+ public function detach()
+ {
+ if (!isset($this->stream)) {
+ return null;
+ }
+
+ $result = $this->stream;
+ unset($this->stream);
+ $this->size = $this->uri = null;
+
+ return $result;
+ }
+
+ public function getSize()
+ {
+ if (null !== $this->size) {
+ return $this->size;
+ }
+
+ if (!isset($this->stream)) {
+ return null;
+ }
+
+ // Clear the stat cache if the stream has a URI
+ if ($this->uri) {
+ clearstatcache(true, $this->uri);
+ }
+
+ $stats = fstat($this->stream);
+ if (isset($stats['size'])) {
+ $this->size = $stats['size'];
+
+ return $this->size;
+ }
+
+ return null;
+ }
+
+ public function isReadable()
+ {
+ return $this->readable;
+ }
+
+ public function isWritable()
+ {
+ return $this->writable;
+ }
+
+ public function isSeekable()
+ {
+ return $this->seekable;
+ }
+
+ public function eof()
+ {
+ if (!isset($this->stream)) {
+ throw new \RuntimeException('Stream is detached');
+ }
+
+ return feof($this->stream);
+ }
+
+ public function tell()
+ {
+ if (!isset($this->stream)) {
+ throw new \RuntimeException('Stream is detached');
+ }
+
+ $result = ftell($this->stream);
+
+ if (false === $result) {
+ throw new \RuntimeException('Unable to determine stream position');
+ }
+
+ return $result;
+ }
+
+ public function rewind()
+ {
+ $this->seek(0);
+ }
+
+ public function seek($offset, $whence = SEEK_SET)
+ {
+ $whence = (int) $whence;
+
+ if (!isset($this->stream)) {
+ throw new \RuntimeException('Stream is detached');
+ }
+ if (!$this->seekable) {
+ throw new \RuntimeException('Stream is not seekable');
+ }
+ if (-1 === fseek($this->stream, $offset, $whence)) {
+ throw new \RuntimeException('Unable to seek to stream position ' . $offset . ' with whence ' . var_export($whence, true));
+ }
+ }
+
+ public function read($length)
+ {
+ if (!isset($this->stream)) {
+ throw new \RuntimeException('Stream is detached');
+ }
+ if (!$this->readable) {
+ throw new \RuntimeException('Cannot read from non-readable stream');
+ }
+ if ($length < 0) {
+ throw new \RuntimeException('Length parameter cannot be negative');
+ }
+
+ if (0 === $length) {
+ return '';
+ }
+
+ $string = fread($this->stream, $length);
+ if (false === $string) {
+ throw new \RuntimeException('Unable to read from stream');
+ }
+
+ return $string;
+ }
+
+ public function write($string)
+ {
+ if (!isset($this->stream)) {
+ throw new \RuntimeException('Stream is detached');
+ }
+ if (!$this->writable) {
+ throw new \RuntimeException('Cannot write to a non-writable stream');
+ }
+
+ // We can't know the size after writing anything
+ $this->size = null;
+ $result = fwrite($this->stream, $string);
+
+ if (false === $result) {
+ throw new \RuntimeException('Unable to write to stream');
+ }
+
+ return $result;
+ }
+
+ public function getMetadata($key = null)
+ {
+ if (!isset($this->stream)) {
+ return $key ? null : [];
+ }
+
+ $meta = stream_get_meta_data($this->stream);
+
+ return isset($meta[$key]) ? $meta[$key] : null;
+ }
+
+ private function next($endStr = '')
+ {
+ $this->streaming = false;
+ ++$this->index;
+ $this->write($endStr);
+ $this->currStream = null;
+
+ return $endStr;
+ }
+}
diff --git a/vendor/alibabacloud/tea-fileform/tests/FileFormTest.php b/vendor/alibabacloud/tea-fileform/tests/FileFormTest.php
new file mode 100644
index 00000000..67d24e8c
--- /dev/null
+++ b/vendor/alibabacloud/tea-fileform/tests/FileFormTest.php
@@ -0,0 +1,81 @@
+assertTrue($stream instanceof FileFormStream);
+ $stream->write($boundary);
+ $this->assertTrue(\strlen($boundary) === $stream->getSize());
+ }
+
+ public function testSet()
+ {
+ $fileField = new FileField([
+ 'filename' => 'fake filename',
+ 'contentType' => 'content type',
+ 'content' => null,
+ ]);
+
+ $this->assertEquals('fake filename', $fileField->filename);
+ $this->assertEquals('content type', $fileField->contentType);
+ }
+
+ public function testRead()
+ {
+ $fileField = new FileField();
+ $fileField->filename = 'haveContent';
+ $fileField->contentType = 'contentType';
+ $fileField->content = new Stream(fopen('data://text/plain;base64,' . base64_encode('This is file test. This sentence must be long'), 'r'));
+
+ $fileFieldNoContent = new FileField();
+ $fileFieldNoContent->filename = 'noContent';
+ $fileFieldNoContent->contentType = 'contentType';
+ $fileFieldNoContent->content = null;
+
+ $map = [
+ 'key' => 'value',
+ 'testKey' => 'testValue',
+ 'haveFile' => $fileField,
+ 'noFile' => $fileFieldNoContent,
+ ];
+
+ $stream = FileForm::toFileForm($map, 'testBoundary');
+
+ $result = $stream->getContents();
+ $target = "--testBoundary\r\nContent-Disposition: form-data; name=\"key\"\r\n\r\nvalue\r\n--testBoundary\r\nContent-Disposition: form-data; name=\"testKey\"\r\n\r\ntestValue\r\n--testBoundary\r\nContent-Disposition: form-data; name=\"haveFile\"; filename=\"haveContent\"\r\nContent-Type: contentType\r\n\r\nThis is file test. This sentence must be long\r\n--testBoundary--\r\n";
+
+ $this->assertEquals($target, $result);
+ }
+
+ public function testReadFile()
+ {
+ $fileField = new FileField();
+ $fileField->filename = 'composer.json';
+ $fileField->contentType = 'application/json';
+ $fileField->content = new Stream(fopen(__DIR__ . '/../composer.json', 'r'));
+ $map = [
+ 'name' => 'json_file',
+ 'type' => 'application/json',
+ 'json_file' => $fileField,
+ ];
+
+ $boundary = FileForm::getBoundary();
+ $fileStream = FileForm::toFileForm($map, $boundary);
+ $this->assertTrue(false !== strpos($fileStream->getContents(), 'json_file'));
+ }
+}
diff --git a/vendor/alibabacloud/tea-fileform/tests/bootstrap.php b/vendor/alibabacloud/tea-fileform/tests/bootstrap.php
new file mode 100644
index 00000000..c62c4e81
--- /dev/null
+++ b/vendor/alibabacloud/tea-fileform/tests/bootstrap.php
@@ -0,0 +1,3 @@
+setRiskyAllowed(true)
+ ->setIndent(' ')
+ ->setRules([
+ '@PSR2' => true,
+ '@PhpCsFixer' => true,
+ '@Symfony:risky' => true,
+ 'concat_space' => ['spacing' => 'one'],
+ 'array_syntax' => ['syntax' => 'short'],
+ 'array_indentation' => true,
+ 'combine_consecutive_unsets' => true,
+ 'method_separation' => true,
+ 'single_quote' => true,
+ 'declare_equal_normalize' => true,
+ 'function_typehint_space' => true,
+ 'hash_to_slash_comment' => true,
+ 'include' => true,
+ 'lowercase_cast' => true,
+ 'no_multiline_whitespace_before_semicolons' => true,
+ 'no_leading_import_slash' => true,
+ 'no_multiline_whitespace_around_double_arrow' => true,
+ 'no_spaces_around_offset' => true,
+ 'no_unneeded_control_parentheses' => true,
+ 'no_unused_imports' => true,
+ 'no_whitespace_before_comma_in_array' => true,
+ 'no_whitespace_in_blank_line' => true,
+ 'object_operator_without_whitespace' => true,
+ 'single_blank_line_before_namespace' => true,
+ 'single_class_element_per_statement' => true,
+ 'space_after_semicolon' => true,
+ 'standardize_not_equals' => true,
+ 'ternary_operator_spaces' => true,
+ 'trailing_comma_in_multiline_array' => true,
+ 'trim_array_spaces' => true,
+ 'unary_operator_spaces' => true,
+ 'whitespace_after_comma_in_array' => true,
+ 'no_extra_consecutive_blank_lines' => [
+ 'curly_brace_block',
+ 'extra',
+ 'parenthesis_brace_block',
+ 'square_brace_block',
+ 'throw',
+ 'use',
+ ],
+ 'binary_operator_spaces' => [
+ 'align_double_arrow' => true,
+ 'align_equals' => true,
+ ],
+ 'braces' => [
+ 'allow_single_line_closure' => true,
+ ],
+ ])
+ ->setFinder(
+ PhpCsFixer\Finder::create()
+ ->exclude('vendor')
+ ->exclude('tests')
+ ->in(__DIR__)
+ );
diff --git a/vendor/alibabacloud/tea/CHANGELOG.md b/vendor/alibabacloud/tea/CHANGELOG.md
new file mode 100644
index 00000000..a3b6a533
--- /dev/null
+++ b/vendor/alibabacloud/tea/CHANGELOG.md
@@ -0,0 +1,148 @@
+# CHANGELOG
+
+## 3.1.22 - 2021-05-11
+
+- Deprecate `stream_for` method.
+
+## 3.1.21 - 2021-03-15
+
+- Supported set proxy&timeout on request.
+
+## 3.1.20 - 2020-12-02
+
+- Fix the warning when the Tea::merge method received empty arguments.
+
+## 3.1.19 - 2020-10-09
+
+- Fix the error when the code value is a string.
+
+## 3.1.18 - 2020-09-28
+
+- Require Guzzle Version 7.0
+
+## 3.1.17 - 2020-09-24
+
+- TeaUnableRetryError support get error info.
+
+## 3.1.16 - 2020-08-31
+
+- Fix the Maximum function nesting level error when repeated network requests.
+
+## 3.1.15 - 2020-07-28
+
+- Improved validatePattern method.
+
+## 3.1.14 - 2020-07-03
+
+- Supported set properties of TeaError with `ErrorInfo`.
+
+## 3.1.13 - 2020-06-09
+
+- Reduce dependencies.
+
+## 3.1.12 - 2020-05-13
+
+- Add validate method.
+- Supported validate maximun&minimun of property.
+
+## 3.1.11 - 2020-05-07
+
+- Fixed error when class is undefined.
+
+## 3.1.10 - 2020-05-07
+
+- Fixed error when '$item' of array is null
+
+## 3.1.9 - 2020-04-13
+
+- TeaUnableRetryError add $lastException param.
+
+## 3.1.8 - 2020-04-02
+
+- Added some static methods of Model to validate fields of Model.
+
+## 3.1.7 - 2020-03-27
+
+- Improve Tea::isRetryable method.
+
+## 3.1.6 - 2020-03-25
+
+- Fixed bug when body is StreamInterface.
+
+## 3.1.5 - 2020-03-25
+
+- Improve Model.toMap method.
+- Improve Tea.merge method.
+- Fixed tests.
+
+## 3.1.4 - 2020-03-20
+
+- Added Tea::merge method.
+- Change Tea::isRetryable method.
+
+## 3.1.3 - 2020-03-20
+
+- Model: added toModel method.
+
+## 3.1.2 - 2020-03-19
+
+- Model constructor supported array type parameter.
+
+## 3.1.1 - 2020-03-18
+
+- Fixed bug : set method failed.
+- Fixed bug : get empty contents form body.
+
+## 3.1.0 - 2020-03-13
+
+- TeaUnableRetryError add 'lastRequest' property.
+- Change Tea.send() method return.
+- Fixed Tea. allowRetry() method.
+
+## 3.0.0 - 2020-02-14
+- Rename package name.
+
+## 2.0.3 - 2020-02-14
+- Improved Exception.
+
+## 2.0.2 - 2019-09-11
+- Supported `String`.
+
+## 2.0.1 - 2019-08-15
+- Supported `IteratorAggregate`.
+
+## 2.0.0 - 2019-08-14
+- Design `Request` as a standard `PsrRequest`.
+
+## 1.0.10 - 2019-08-12
+- Added `__toString` for `Response`.
+
+## 1.0.9 - 2019-08-01
+- Updated `Middleware`.
+
+## 1.0.8 - 2019-07-29
+- Supported `TransferStats`.
+
+## 1.0.7 - 2019-07-27
+- Improved request.
+
+## 1.0.6 - 2019-07-23
+- Trim key for parameter.
+
+## 1.0.5 - 2019-07-23
+- Supported default protocol.
+
+## 1.0.4 - 2019-07-22
+- Added `toArray()`.
+
+## 1.0.3 - 2019-05-02
+- Improved `Request`.
+
+## 1.0.2 - 2019-05-02
+- Added getHeader/getHeaders.
+
+## 1.0.1 - 2019-04-02
+- Improved design.
+
+## 1.0.0 - 2019-05-02
+- Initial release of the AlibabaCloud Tea Version 1.0.0 on Packagist See for more information.
diff --git a/vendor/alibabacloud/tea/LICENSE.md b/vendor/alibabacloud/tea/LICENSE.md
new file mode 100644
index 00000000..ec13fccd
--- /dev/null
+++ b/vendor/alibabacloud/tea/LICENSE.md
@@ -0,0 +1,13 @@
+Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/vendor/alibabacloud/tea/README.md b/vendor/alibabacloud/tea/README.md
new file mode 100644
index 00000000..a8cbe661
--- /dev/null
+++ b/vendor/alibabacloud/tea/README.md
@@ -0,0 +1,16 @@
+
+## Installation
+```
+composer require alibabacloud/tea --optimize-autoloader
+```
+> Some users may not be able to install due to network problems, you can try to switch the Composer mirror.
+
+
+## Changelog
+Detailed changes for each release are documented in the [release notes](CHANGELOG.md).
+
+
+## License
+[Apache-2.0](LICENSE.md)
+
+Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
diff --git a/vendor/alibabacloud/tea/composer.json b/vendor/alibabacloud/tea/composer.json
new file mode 100644
index 00000000..163689ec
--- /dev/null
+++ b/vendor/alibabacloud/tea/composer.json
@@ -0,0 +1,80 @@
+{
+ "name": "alibabacloud/tea",
+ "homepage": "https://www.alibabacloud.com/",
+ "description": "Client of Tea for PHP",
+ "keywords": [
+ "tea",
+ "client",
+ "alibabacloud",
+ "cloud"
+ ],
+ "type": "library",
+ "license": "Apache-2.0",
+ "support": {
+ "source": "https://github.com/aliyun/tea-php",
+ "issues": "https://github.com/aliyun/tea-php/issues"
+ },
+ "authors": [
+ {
+ "name": "Alibaba Cloud SDK",
+ "email": "sdk-team@alibabacloud.com",
+ "homepage": "http://www.alibabacloud.com"
+ }
+ ],
+ "require": {
+ "php": ">=5.5",
+ "ext-curl": "*",
+ "ext-json": "*",
+ "ext-libxml": "*",
+ "ext-mbstring": "*",
+ "ext-openssl": "*",
+ "ext-simplexml": "*",
+ "ext-xmlwriter": "*",
+ "guzzlehttp/guzzle": "^6.3|^7.0",
+ "adbario/php-dot-notation": "^2.4"
+ },
+ "require-dev": {
+ "symfony/dotenv": "^3.4",
+ "phpunit/phpunit": "*",
+ "symfony/var-dumper": "^3.4"
+ },
+ "suggest": {
+ "ext-sockets": "To use client-side monitoring"
+ },
+ "autoload": {
+ "psr-4": {
+ "AlibabaCloud\\Tea\\": "src"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "AlibabaCloud\\Tea\\Tests\\": "tests"
+ }
+ },
+ "config": {
+ "sort-packages": true,
+ "preferred-install": "dist",
+ "optimize-autoloader": true
+ },
+ "prefer-stable": true,
+ "minimum-stability": "dev",
+ "scripts": {
+ "cs": "phpcs --standard=PSR2 -n ./",
+ "cbf": "phpcbf --standard=PSR2 -n ./",
+ "fixer": "php-cs-fixer fix ./",
+ "test": [
+ "@clearCache",
+ "phpunit --colors=always"
+ ],
+ "unit": [
+ "@clearCache",
+ "phpunit --testsuite=Unit --colors=always"
+ ],
+ "feature": [
+ "@clearCache",
+ "phpunit --testsuite=Feature --colors=always"
+ ],
+ "clearCache": "rm -rf cache/*",
+ "coverage": "open cache/coverage/index.html"
+ }
+}
diff --git a/vendor/alibabacloud/tea/src/Exception/TeaError.php b/vendor/alibabacloud/tea/src/Exception/TeaError.php
new file mode 100644
index 00000000..f4ef0d9a
--- /dev/null
+++ b/vendor/alibabacloud/tea/src/Exception/TeaError.php
@@ -0,0 +1,53 @@
+errorInfo = $errorInfo;
+ if (!empty($errorInfo)) {
+ $properties = ['name', 'message', 'code', 'data', 'description', 'accessDeniedDetail'];
+ foreach ($properties as $property) {
+ if (isset($errorInfo[$property])) {
+ $this->{$property} = $errorInfo[$property];
+ if ($property === 'data' && isset($errorInfo['data']['statusCode'])) {
+ $this->statusCode = $errorInfo['data']['statusCode'];
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * @return array
+ */
+ public function getErrorInfo()
+ {
+ return $this->errorInfo;
+ }
+}
diff --git a/vendor/alibabacloud/tea/src/Exception/TeaRetryError.php b/vendor/alibabacloud/tea/src/Exception/TeaRetryError.php
new file mode 100644
index 00000000..30aa7f8e
--- /dev/null
+++ b/vendor/alibabacloud/tea/src/Exception/TeaRetryError.php
@@ -0,0 +1,21 @@
+getErrorInfo();
+ }
+ parent::__construct($error_info, $lastException->getMessage(), $lastException->getCode(), $lastException);
+ $this->lastRequest = $lastRequest;
+ $this->lastException = $lastException;
+ }
+
+ public function getLastRequest()
+ {
+ return $this->lastRequest;
+ }
+
+ public function getLastException()
+ {
+ return $this->lastException;
+ }
+}
diff --git a/vendor/alibabacloud/tea/src/Helper.php b/vendor/alibabacloud/tea/src/Helper.php
new file mode 100644
index 00000000..f1c0fd4f
--- /dev/null
+++ b/vendor/alibabacloud/tea/src/Helper.php
@@ -0,0 +1,112 @@
+ $ord) {
+ if ($k !== $i) {
+ return false;
+ }
+ if (!\is_int($ord)) {
+ return false;
+ }
+ if ($ord < 0 || $ord > 255) {
+ return false;
+ }
+ ++$i;
+ }
+
+ return true;
+ }
+
+ /**
+ * Convert a bytes to string(utf8).
+ *
+ * @param array $bytes
+ *
+ * @return string the return string
+ */
+ public static function toString($bytes)
+ {
+ $str = '';
+ foreach ($bytes as $ch) {
+ $str .= \chr($ch);
+ }
+
+ return $str;
+ }
+
+ /**
+ * @return array
+ */
+ public static function merge(array $arrays)
+ {
+ $result = [];
+ foreach ($arrays as $array) {
+ foreach ($array as $key => $value) {
+ if (\is_int($key)) {
+ $result[] = $value;
+
+ continue;
+ }
+
+ if (isset($result[$key]) && \is_array($result[$key])) {
+ $result[$key] = self::merge(
+ [$result[$key], $value]
+ );
+
+ continue;
+ }
+
+ $result[$key] = $value;
+ }
+ }
+
+ return $result;
+ }
+}
diff --git a/vendor/alibabacloud/tea/src/Model.php b/vendor/alibabacloud/tea/src/Model.php
new file mode 100644
index 00000000..538b55c5
--- /dev/null
+++ b/vendor/alibabacloud/tea/src/Model.php
@@ -0,0 +1,114 @@
+ $v) {
+ $this->{$k} = $v;
+ }
+ }
+ }
+
+ public function getName($name = null)
+ {
+ if (null === $name) {
+ return $this->_name;
+ }
+
+ return isset($this->_name[$name]) ? $this->_name[$name] : $name;
+ }
+
+ public function toMap()
+ {
+ $map = get_object_vars($this);
+ foreach ($map as $k => $m) {
+ if (0 === strpos($k, '_')) {
+ unset($map[$k]);
+ }
+ }
+ $res = [];
+ foreach ($map as $k => $v) {
+ $name = isset($this->_name[$k]) ? $this->_name[$k] : $k;
+ $res[$name] = $v;
+ }
+
+ return $res;
+ }
+
+ public function validate()
+ {
+ $vars = get_object_vars($this);
+ foreach ($vars as $k => $v) {
+ if (isset($this->_required[$k]) && $this->_required[$k] && empty($v)) {
+ throw new \InvalidArgumentException("{$k} is required.");
+ }
+ }
+ }
+
+ public static function validateRequired($fieldName, $field, $val = null)
+ {
+ if (true === $val && null === $field) {
+ throw new \InvalidArgumentException($fieldName . ' is required');
+ }
+ }
+
+ public static function validateMaxLength($fieldName, $field, $val = null)
+ {
+ if (null !== $field && \strlen($field) > (int) $val) {
+ throw new \InvalidArgumentException($fieldName . ' is exceed max-length: ' . $val);
+ }
+ }
+
+ public static function validateMinLength($fieldName, $field, $val = null)
+ {
+ if (null !== $field && \strlen($field) < (int) $val) {
+ throw new \InvalidArgumentException($fieldName . ' is less than min-length: ' . $val);
+ }
+ }
+
+ public static function validatePattern($fieldName, $field, $regex = '')
+ {
+ if (null !== $field && '' !== $field && !preg_match("/^{$regex}$/", $field)) {
+ throw new \InvalidArgumentException($fieldName . ' is not match ' . $regex);
+ }
+ }
+
+ public static function validateMaximum($fieldName, $field, $val)
+ {
+ if (null !== $field && $field > $val) {
+ throw new \InvalidArgumentException($fieldName . ' cannot be greater than ' . $val);
+ }
+ }
+
+ public static function validateMinimum($fieldName, $field, $val)
+ {
+ if (null !== $field && $field < $val) {
+ throw new \InvalidArgumentException($fieldName . ' cannot be less than ' . $val);
+ }
+ }
+
+ /**
+ * @param array $map
+ * @param Model $model
+ *
+ * @return mixed
+ */
+ public static function toModel($map, $model)
+ {
+ $names = $model->getName();
+ $names = array_flip($names);
+ foreach ($map as $key => $value) {
+ $name = isset($names[$key]) ? $names[$key] : $key;
+ $model->{$name} = $value;
+ }
+
+ return $model;
+ }
+}
diff --git a/vendor/alibabacloud/tea/src/Parameter.php b/vendor/alibabacloud/tea/src/Parameter.php
new file mode 100644
index 00000000..324a95d5
--- /dev/null
+++ b/vendor/alibabacloud/tea/src/Parameter.php
@@ -0,0 +1,50 @@
+toArray());
+ }
+
+ /**
+ * @return array
+ */
+ public function getRealParameters()
+ {
+ $array = [];
+ $obj = new ReflectionObject($this);
+ $properties = $obj->getProperties();
+
+ foreach ($properties as $property) {
+ $docComment = $property->getDocComment();
+ $key = trim(Helper::findFromString($docComment, '@real', "\n"));
+ $value = $property->getValue($this);
+ $array[$key] = $value;
+ }
+
+ return $array;
+ }
+
+ /**
+ * @return array
+ */
+ public function toArray()
+ {
+ return $this->getRealParameters();
+ }
+}
diff --git a/vendor/alibabacloud/tea/src/Request.php b/vendor/alibabacloud/tea/src/Request.php
new file mode 100644
index 00000000..db49142e
--- /dev/null
+++ b/vendor/alibabacloud/tea/src/Request.php
@@ -0,0 +1,123 @@
+method = $method;
+ }
+
+ /**
+ * These fields are compatible if you define other fields.
+ * Mainly for compatibility situations where the code generator cannot generate set properties.
+ *
+ * @return PsrRequest
+ */
+ public function getPsrRequest()
+ {
+ $this->assertQuery($this->query);
+
+ $request = clone $this;
+
+ $uri = $request->getUri();
+ if ($this->query) {
+ $uri = $uri->withQuery(http_build_query($this->query));
+ }
+
+ if ($this->port) {
+ $uri = $uri->withPort($this->port);
+ }
+
+ if ($this->protocol) {
+ $uri = $uri->withScheme($this->protocol);
+ }
+
+ if ($this->pathname) {
+ $uri = $uri->withPath($this->pathname);
+ }
+
+ if (isset($this->headers['host'])) {
+ $uri = $uri->withHost($this->headers['host']);
+ }
+
+ $request = $request->withUri($uri);
+ $request = $request->withMethod($this->method);
+
+ if ('' !== $this->body && null !== $this->body) {
+ if ($this->body instanceof StreamInterface) {
+ $request = $request->withBody($this->body);
+ } else {
+ $body = $this->body;
+ if (Helper::isBytes($this->body)) {
+ $body = Helper::toString($this->body);
+ }
+ if (\function_exists('\GuzzleHttp\Psr7\stream_for')) {
+ // @deprecated stream_for will be removed in guzzlehttp/psr7:2.0
+ $request = $request->withBody(\GuzzleHttp\Psr7\stream_for($body));
+ } else {
+ $request = $request->withBody(\GuzzleHttp\Psr7\Utils::streamFor($body));
+ }
+ }
+ }
+
+ if ($this->headers) {
+ foreach ($this->headers as $key => $value) {
+ $request = $request->withHeader($key, $value);
+ }
+ }
+
+ return $request;
+ }
+
+ /**
+ * @param array $query
+ */
+ private function assertQuery($query)
+ {
+ if (!\is_array($query) && $query !== null) {
+ throw new InvalidArgumentException('Query must be array.');
+ }
+ }
+}
diff --git a/vendor/alibabacloud/tea/src/Response.php b/vendor/alibabacloud/tea/src/Response.php
new file mode 100644
index 00000000..cb446e74
--- /dev/null
+++ b/vendor/alibabacloud/tea/src/Response.php
@@ -0,0 +1,372 @@
+getStatusCode(),
+ $response->getHeaders(),
+ $response->getBody(),
+ $response->getProtocolVersion(),
+ $response->getReasonPhrase()
+ );
+ $this->headers = $response->getHeaders();
+ $this->body = $response->getBody();
+ $this->statusCode = $response->getStatusCode();
+ if ($this->body->isSeekable()) {
+ $this->body->seek(0);
+ }
+
+ if (Helper::isJson((string) $this->getBody())) {
+ $this->dot = new Dot($this->toArray());
+ } else {
+ $this->dot = new Dot();
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ return (string) $this->getBody();
+ }
+
+ /**
+ * @param string $name
+ *
+ * @return null|mixed
+ */
+ public function __get($name)
+ {
+ $data = $this->dot->all();
+ if (!isset($data[$name])) {
+ return null;
+ }
+
+ return json_decode(json_encode($data))->{$name};
+ }
+
+ /**
+ * @param string $name
+ * @param mixed $value
+ */
+ public function __set($name, $value)
+ {
+ $this->dot->set($name, $value);
+ }
+
+ /**
+ * @param string $name
+ *
+ * @return bool
+ */
+ public function __isset($name)
+ {
+ return $this->dot->has($name);
+ }
+
+ /**
+ * @param $offset
+ */
+ public function __unset($offset)
+ {
+ $this->dot->delete($offset);
+ }
+
+ /**
+ * @return array
+ */
+ public function toArray()
+ {
+ return \GuzzleHttp\json_decode((string) $this->getBody(), true);
+ }
+
+ /**
+ * @param array|int|string $keys
+ * @param mixed $value
+ */
+ public function add($keys, $value = null)
+ {
+ return $this->dot->add($keys, $value);
+ }
+
+ /**
+ * @return array
+ */
+ public function all()
+ {
+ return $this->dot->all();
+ }
+
+ /**
+ * @param null|array|int|string $keys
+ */
+ public function clear($keys = null)
+ {
+ return $this->dot->clear($keys);
+ }
+
+ /**
+ * @param array|int|string $keys
+ */
+ public function delete($keys)
+ {
+ return $this->dot->delete($keys);
+ }
+
+ /**
+ * @param string $delimiter
+ * @param null|array $items
+ * @param string $prepend
+ *
+ * @return array
+ */
+ public function flatten($delimiter = '.', $items = null, $prepend = '')
+ {
+ return $this->dot->flatten($delimiter, $items, $prepend);
+ }
+
+ /**
+ * @param null|int|string $key
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public function get($key = null, $default = null)
+ {
+ return $this->dot->get($key, $default);
+ }
+
+ /**
+ * @param array|int|string $keys
+ *
+ * @return bool
+ */
+ public function has($keys)
+ {
+ return $this->dot->has($keys);
+ }
+
+ /**
+ * @param null|array|int|string $keys
+ *
+ * @return bool
+ */
+ public function isEmpty($keys = null)
+ {
+ return $this->dot->isEmpty($keys);
+ }
+
+ /**
+ * @param array|self|string $key
+ * @param array|self $value
+ */
+ public function merge($key, $value = [])
+ {
+ return $this->dot->merge($key, $value);
+ }
+
+ /**
+ * @param array|self|string $key
+ * @param array|self $value
+ */
+ public function mergeRecursive($key, $value = [])
+ {
+ return $this->dot->mergeRecursive($key, $value);
+ }
+
+ /**
+ * @param array|self|string $key
+ * @param array|self $value
+ */
+ public function mergeRecursiveDistinct($key, $value = [])
+ {
+ return $this->dot->mergeRecursiveDistinct($key, $value);
+ }
+
+ /**
+ * @param null|int|string $key
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public function pull($key = null, $default = null)
+ {
+ return $this->dot->pull($key, $default);
+ }
+
+ /**
+ * @param null|int|string $key
+ * @param mixed $value
+ *
+ * @return mixed
+ */
+ public function push($key = null, $value = null)
+ {
+ return $this->dot->push($key, $value);
+ }
+
+ /**
+ * Replace all values or values within the given key
+ * with an array or Dot object.
+ *
+ * @param array|self|string $key
+ * @param array|self $value
+ */
+ public function replace($key, $value = [])
+ {
+ return $this->dot->replace($key, $value);
+ }
+
+ /**
+ * Set a given key / value pair or pairs.
+ *
+ * @param array|int|string $keys
+ * @param mixed $value
+ */
+ public function set($keys, $value = null)
+ {
+ return $this->dot->set($keys, $value);
+ }
+
+ /**
+ * Replace all items with a given array.
+ *
+ * @param mixed $items
+ */
+ public function setArray($items)
+ {
+ return $this->dot->setArray($items);
+ }
+
+ /**
+ * Replace all items with a given array as a reference.
+ */
+ public function setReference(array &$items)
+ {
+ return $this->dot->setReference($items);
+ }
+
+ /**
+ * Return the value of a given key or all the values as JSON.
+ *
+ * @param mixed $key
+ * @param int $options
+ *
+ * @return string
+ */
+ public function toJson($key = null, $options = 0)
+ {
+ return $this->dot->toJson($key, $options);
+ }
+
+ /**
+ * Retrieve an external iterator.
+ */
+ #[\ReturnTypeWillChange]
+ public function getIterator()
+ {
+ return $this->dot->getIterator();
+ }
+
+ /**
+ * Whether a offset exists.
+ *
+ * @param $offset
+ *
+ * @return bool
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetExists($offset)
+ {
+ return $this->dot->offsetExists($offset);
+ }
+
+ /**
+ * Offset to retrieve.
+ *
+ * @param $offset
+ *
+ * @return mixed
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetGet($offset)
+ {
+ return $this->dot->offsetGet($offset);
+ }
+
+ /**
+ * Offset to set.
+ *
+ * @param $offset
+ * @param $value
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetSet($offset, $value)
+ {
+ $this->dot->offsetSet($offset, $value);
+ }
+
+ /**
+ * Offset to unset.
+ *
+ * @param $offset
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetUnset($offset)
+ {
+ $this->dot->offsetUnset($offset);
+ }
+
+ /**
+ * Count elements of an object.
+ *
+ * @param null $key
+ *
+ * @return int
+ */
+ #[\ReturnTypeWillChange]
+ public function count($key = null)
+ {
+ return $this->dot->count($key);
+ }
+}
diff --git a/vendor/alibabacloud/tea/src/Tea.php b/vendor/alibabacloud/tea/src/Tea.php
new file mode 100644
index 00000000..a138ad9a
--- /dev/null
+++ b/vendor/alibabacloud/tea/src/Tea.php
@@ -0,0 +1,287 @@
+getPsrRequest();
+ }
+
+ $config = self::resolveConfig($config);
+
+ $res = self::client()->send(
+ $request,
+ $config
+ );
+
+ return new Response($res);
+ }
+
+ /**
+ * @return PromiseInterface
+ */
+ public static function sendAsync(RequestInterface $request, array $config = [])
+ {
+ if (method_exists($request, 'getPsrRequest')) {
+ $request = $request->getPsrRequest();
+ }
+
+ $config = self::resolveConfig($config);
+
+ return self::client()->sendAsync(
+ $request,
+ $config
+ );
+ }
+
+ /**
+ * @return Client
+ */
+ public static function client(array $config = [])
+ {
+ if (isset(self::$config['handler'])) {
+ $stack = self::$config['handler'];
+ } else {
+ $stack = HandlerStack::create();
+ $stack->push(Middleware::mapResponse(static function (ResponseInterface $response) {
+ return new Response($response);
+ }));
+ }
+
+ self::$config['handler'] = $stack;
+
+ if (!isset(self::$config['on_stats'])) {
+ self::$config['on_stats'] = function (TransferStats $stats) {
+ Response::$info = $stats->getHandlerStats();
+ };
+ }
+
+ $new_config = Helper::merge([self::$config, $config]);
+
+ return new Client($new_config);
+ }
+
+ /**
+ * @param string $method
+ * @param string|UriInterface $uri
+ * @param array $options
+ *
+ * @throws GuzzleException
+ *
+ * @return ResponseInterface
+ */
+ public static function request($method, $uri, $options = [])
+ {
+ return self::client()->request($method, $uri, $options);
+ }
+
+ /**
+ * @param string $method
+ * @param string $uri
+ * @param array $options
+ *
+ * @throws GuzzleException
+ *
+ * @return string
+ */
+ public static function string($method, $uri, $options = [])
+ {
+ return (string) self::client()->request($method, $uri, $options)
+ ->getBody();
+ }
+
+ /**
+ * @param string $method
+ * @param string|UriInterface $uri
+ * @param array $options
+ *
+ * @return PromiseInterface
+ */
+ public static function requestAsync($method, $uri, $options = [])
+ {
+ return self::client()->requestAsync($method, $uri, $options);
+ }
+
+ /**
+ * @param string|UriInterface $uri
+ * @param array $options
+ *
+ * @throws GuzzleException
+ *
+ * @return null|mixed
+ */
+ public static function getHeaders($uri, $options = [])
+ {
+ return self::request('HEAD', $uri, $options)->getHeaders();
+ }
+
+ /**
+ * @param string|UriInterface $uri
+ * @param string $key
+ * @param null|mixed $default
+ *
+ * @throws GuzzleException
+ *
+ * @return null|mixed
+ */
+ public static function getHeader($uri, $key, $default = null)
+ {
+ $headers = self::getHeaders($uri);
+
+ return isset($headers[$key][0]) ? $headers[$key][0] : $default;
+ }
+
+ /**
+ * @param int $retryTimes
+ * @param float $now
+ *
+ * @return bool
+ */
+ public static function allowRetry(array $runtime, $retryTimes, $now)
+ {
+ unset($now);
+ if (!isset($retryTimes) || null === $retryTimes || !\is_numeric($retryTimes)) {
+ return false;
+ }
+ if ($retryTimes > 0 && (empty($runtime) || !isset($runtime['retryable']) || !$runtime['retryable'] || !isset($runtime['maxAttempts']))) {
+ return false;
+ }
+ $maxAttempts = $runtime['maxAttempts'];
+ $retry = empty($maxAttempts) ? 0 : (int) $maxAttempts;
+
+ return $retry >= $retryTimes;
+ }
+
+ /**
+ * @param int $retryTimes
+ *
+ * @return int
+ */
+ public static function getBackoffTime(array $runtime, $retryTimes)
+ {
+ $backOffTime = 0;
+ $policy = isset($runtime['policy']) ? $runtime['policy'] : '';
+
+ if (empty($policy) || 'no' == $policy) {
+ return $backOffTime;
+ }
+
+ $period = isset($runtime['period']) ? $runtime['period'] : '';
+ if (null !== $period && '' !== $period) {
+ $backOffTime = (int) $period;
+ if ($backOffTime <= 0) {
+ return $retryTimes;
+ }
+ }
+
+ return $backOffTime;
+ }
+
+ public static function sleep($time)
+ {
+ sleep($time);
+ }
+
+ public static function isRetryable($retry, $retryTimes = 0)
+ {
+ if ($retry instanceof TeaError) {
+ return true;
+ }
+ if (\is_array($retry)) {
+ $max = isset($retry['maxAttempts']) ? (int) ($retry['maxAttempts']) : 3;
+
+ return $retryTimes <= $max;
+ }
+
+ return false;
+ }
+
+ /**
+ * @param mixed|Model[] ...$item
+ *
+ * @return mixed
+ */
+ public static function merge(...$item)
+ {
+ $tmp = [];
+ $n = 0;
+ foreach ($item as $i) {
+ if (\is_object($i)) {
+ if ($i instanceof Model) {
+ $i = $i->toMap();
+ } else {
+ $i = json_decode(json_encode($i), true);
+ }
+ }
+ if (null === $i) {
+ continue;
+ }
+ if (\is_array($i)) {
+ $tmp[$n++] = $i;
+ }
+ }
+
+ if (\count($tmp)) {
+ return \call_user_func_array('array_merge', $tmp);
+ }
+
+ return [];
+ }
+
+ private static function resolveConfig(array $config = [])
+ {
+ $options = new Dot(['http_errors' => false]);
+ if (isset($config['httpProxy']) && !empty($config['httpProxy'])) {
+ $options->set('proxy.http', $config['httpProxy']);
+ }
+ if (isset($config['httpsProxy']) && !empty($config['httpsProxy'])) {
+ $options->set('proxy.https', $config['httpsProxy']);
+ }
+ if (isset($config['noProxy']) && !empty($config['noProxy'])) {
+ $options->set('proxy.no', $config['noProxy']);
+ }
+ if (isset($config['ignoreSSL']) && !empty($config['ignoreSSL'])) {
+ $options->set('verify',!((bool)$config['ignoreSSL']));
+ }
+ // readTimeout&connectTimeout unit is millisecond
+ $read_timeout = isset($config['readTimeout']) && !empty($config['readTimeout']) ? (int) $config['readTimeout'] : 0;
+ $con_timeout = isset($config['connectTimeout']) && !empty($config['connectTimeout']) ? (int) $config['connectTimeout'] : 0;
+ // timeout unit is second
+ $options->set('timeout', ($read_timeout + $con_timeout) / 1000);
+
+ return $options->all();
+ }
+}
diff --git a/vendor/alipaysdk/easysdk/.gitattributes b/vendor/alipaysdk/easysdk/.gitattributes
new file mode 100644
index 00000000..dd68a15b
--- /dev/null
+++ b/vendor/alipaysdk/easysdk/.gitattributes
@@ -0,0 +1 @@
+*.* linguist-language=java
diff --git a/vendor/alipaysdk/easysdk/.gitignore b/vendor/alipaysdk/easysdk/.gitignore
new file mode 100644
index 00000000..c92a6be4
--- /dev/null
+++ b/vendor/alipaysdk/easysdk/.gitignore
@@ -0,0 +1,16 @@
+.DS_Store
+node_modules
+logs
+.libraries.json
+libraries
+
+.idea
+*.iml
+target
+dependency-reduced-pom.xml
+
+bin
+obj
+.vs
+
+publish/alipay-easysdk
diff --git a/vendor/alipaysdk/easysdk/APIDoc.md b/vendor/alipaysdk/easysdk/APIDoc.md
new file mode 100644
index 00000000..52da8450
--- /dev/null
+++ b/vendor/alipaysdk/easysdk/APIDoc.md
@@ -0,0 +1,822 @@
+# 基础能力 Base
+## 用户授权 OAuth
+### 获取授权访问令牌
+* API声明
+
+getToken(code: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| code | string | 是 | 授权码,用户对应用授权后得到 |
+
+* 出参说明
+
+可前往[alipay.system.oauth.token](https://docs.open.alipay.com/api_9/alipay.system.oauth.token)查看更加详细的参数说明。
+
+### 刷新授权访问令牌
+* API声明
+
+refreshToken(refreshToken: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| refreshToken | string | 是 | 刷新令牌,上次换取访问令牌时得到,见出参的refresh_token字段 |
+
+* 出参说明
+
+可前往[alipay.system.oauth.token](https://docs.open.alipay.com/api_9/alipay.system.oauth.token)查看更加详细的参数说明。
+
+---
+
+## 小程序二维码 Qrcode
+### 创建小程序二维码
+* API声明
+
+create(urlParam: string, queryParam: string, describe: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| urlParam | string | 是 | 小程序中能访问到的页面路径,例如:page/component/component-pages/view/view |
+| queryParam | string | 是 | 小程序的启动参数,打开小程序的query ,在小程序 onLaunch的方法中获取 |
+| describe | string | 是 | 二维码描述 |
+
+* 出参说明
+
+可前往[alipay.open.app.qrcode.create](https://docs.open.alipay.com/api_5/alipay.open.app.qrcode.create)查看更加详细的参数说明。
+
+---
+
+## 图片 Image
+### 上传图片
+* API声明
+
+upload(imageName: string, imageFilePath: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| imageName | string | 是 | 图片名称 |
+| imageFilePath | string | 是 | 待上传的本地图片文件路径 |
+
+* 出参说明
+
+可前往[alipay.offline.material.image.upload](https://docs.open.alipay.com/api_3/alipay.offline.material.image.upload)查看更加详细的参数说明。
+
+---
+
+## 视频 Video
+### 上传视频
+* API声明
+
+upload(videoName: string, videoFilePath: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| videoName | string | 是 | 视频名称 |
+| videoFilePath | string | 是 | 待上传的本地视频文件路径 |
+
+* 出参说明
+
+可前往[alipay.offline.material.image.upload](https://docs.open.alipay.com/api_3/alipay.offline.material.image.upload)查看更加详细的参数说明。
+
+---
+
+# 营销能力 Marketing
+## 生活号 OpenLife
+### 创建图文消息内容
+* API声明
+
+createImageTextContent(title: string, cover: string, content: string, contentComment: string, ctype: string, benefit: string, extTags: string, loginIds: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| title | string | 是 | 标题 |
+| cover | string | 是 | 封面图URL, 尺寸为996*450,最大不超过3M,支持.jpg、.png格式,请先调用上传图片接口获得图片URL |
+| content | string | 是 | 消息正文(支持富文本) |
+| contentComment | string | 否 | 是否允许评论,T:允许,F:不允许,默认不允许 |
+| ctype | string | 否 | 图文类型:填activity表示活动图文,不填默认普通图文 |
+| benefit | string | 否 | 活动利益点,图文类型ctype为activity类型时才需要传,最多10个字符 |
+| extTags | string | 否 | 关键词列表,英文逗号分隔,最多不超过5个 |
+| loginIds | string | 否 | 可预览支付宝账号列表,需要预览时才填写, 英文逗号分隔,最多不超过10个 |
+
+* 出参说明
+
+可前往[alipay.open.public.message.content.create](https://docs.open.alipay.com/api_6/alipay.open.public.message.content.create)查看更加详细的参数说明。
+
+### 更新图文消息内容
+* API声明
+
+modifyImageTextContent(contentId: string, title: string, cover: string, content: string, couldComment: string, ctype: string, benefit: string, extTags: string, loginIds: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| contentId | string | 是 | 内容ID,通过创建图文内容消息接口返回 |
+| title | string | 是 | 标题 |
+| cover | string | 是 | 封面图URL, 尺寸为996*450,最大不超过3M,支持.jpg、.png格式,请先调用上传图片接口获得图片URL |
+| content | string | 是 | 消息正文(支持富文本) |
+| contentComment | string | 否 | 是否允许评论,T:允许,F:不允许,默认不允许 |
+| ctype | string | 否 | 图文类型:填activity表示活动图文,不填默认普通图文 |
+| benefit | string | 否 | 活动利益点,图文类型ctype为activity类型时才需要传,最多10个字符 |
+| extTags | string | 否 | 关键词列表,英文逗号分隔,最多不超过5个 |
+| loginIds | string | 否 | 可预览支付宝账号列表,需要预览时才填写, 英文逗号分隔,最多不超过10个 |
+
+* 出参说明
+
+可前往[alipay.open.public.message.content.modify](https://docs.open.alipay.com/api_6/alipay.open.public.message.content.modify)查看更加详细的参数说明。
+
+### 群发本文消息
+* API声明
+
+sendText(text: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| text | string | 是 | 文本消息内容 |
+
+* 出参说明
+
+可前往[alipay.open.public.message.total.send](https://docs.open.alipay.com/api_6/alipay.open.public.message.total.send)查看更加详细的参数说明。
+
+### 群发图文消息
+* API声明
+
+sendImageText(articles: [ Article ])
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| articles | Article数组 | 是 | 图文消息内容 |
+
+Article对象说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| title | string | 否 | 图文消息标题 |
+| desc | string | 是 | 图文消息描述 |
+| imageUrl | string | 特殊可选 | 图片链接,对于多条图文消息的第一条消息,该字段不能为空,请先调用上传图片接口获得图片URL |
+| url | string | 是 | 点击图文消息跳转的链接 |
+| actionName | string | 否 | 链接文字 |
+
+* 出参说明
+
+可前往[alipay.open.public.message.total.send](https://docs.open.alipay.com/api_6/alipay.open.public.message.total.send)查看更加详细的参数说明。
+
+### 单发模板消息
+* API声明
+
+sendSingleMessage(toUserId: string, template: Template)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| toUserId | string | 是 | 消息接收用户的UserId |
+| template | Template | 是 | 消息接收用户的UserId |
+
+Template对象说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| templateId | string | 是 | 消息模板ID |
+| context | Context | 是 | 消息模板上下文,即模板中定义的参数及参数值 |
+
+Context对象说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| headColor | string | 是 | 顶部色条的色值,比如#85be53 |
+| url | string | 是 | 点击消息后承接页的地址 |
+| actionName | string | 是 | 底部链接描述文字,如:“查看详情”,最多能传8个汉字或16个英文字符 |
+| keyword1 | Keyword | 否 | 模板中占位符的值及文字颜色 |
+| keyword2 | Keyword | 否 | 模板中占位符的值及文字颜色 |
+| first | Keyword | 否 | 模板中占位符的值及文字颜色 |
+| remark | Keyword | 否 | 模板中占位符的值及文字颜色 |
+
+Keyword对象说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| color | string | 是 | 当前文字颜色,比如#85be53 |
+| value | string | 是 | 模板中占位符的值 |
+
+* 出参说明
+
+可前往[alipay.open.public.message.single.send](https://docs.open.alipay.com/api_6/alipay.open.public.message.single.send)查看更加详细的参数说明。
+
+### 生活号消息撤回
+* API声明
+
+recallMessage(messageId: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| messageId | string | 是 | 消息ID |
+
+* 出参说明
+
+可前往[alipay.open.public.life.msg.recall](https://docs.open.alipay.com/api_6/alipay.open.public.life.msg.recall)查看更加详细的参数说明。
+
+### 模板消息行业设置
+* API声明
+
+setIndustry(primaryIndustryCode: string, primaryIndustryName: string, secondaryIndustryCode: string, secondaryIndustryName: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| primaryIndustryCode | string | 是 | 服务窗消息模板所属主行业一级编码,查看[行业信息](https://alipay.open.taobao.com/doc2/detail?treeId=197&docType=1&articleId=105043) |
+| primaryIndustryName | string | 是 | 服务窗消息模板所属主行业一级名称 |
+| secondaryIndustryCode | string | 是 | 服务窗消息模板所属主行业二级编码 |
+| secondaryIndustryName | string | 是 | 服务窗消息模板所属主行业二级名称 |
+
+* 出参说明
+
+可前往[alipay.open.public.template.message.industry.modify](https://docs.open.alipay.com/api_6/alipay.open.public.template.message.industry.modify)查看更加详细的参数说明。
+
+### 生活号查询行业设置
+* API声明
+
+getIndustry()
+
+* 入参说明
+
+无
+
+* 出参说明
+
+可前往[alipay.open.public.setting.category.query](https://docs.open.alipay.com/api_6/alipay.open.public.setting.category.query)查看更加详细的参数说明。
+
+---
+
+
+## 支付宝卡包 Pass
+### 卡券模板创建
+* API声明
+
+createTemplate(uniqueId: string, tplContent: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| uniqueId | string | 是 | 商户用于控制模版的唯一性(可以使用时间戳保证唯一性) |
+| tplContent | string | 是 | 模板内容信息,遵循JSON规范,详情参见tpl_content[参数说明](https://doc.open.alipay.com/doc2/detail.htm?treeId=193&articleId=105249&docType=1#tpl_content) |
+
+* 出参说明
+
+可前往[alipay.pass.template.add](https://docs.open.alipay.com/api_24/alipay.pass.template.add)查看更加详细的参数说明。
+
+### 卡券模板更新
+* API声明
+
+updateTemplate(uniqueId: string, tplContent: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| uniqueId | string | 是 | 商户用于控制模版的唯一性(可以使用时间戳保证唯一性) |
+| tplContent | string | 是 | 模板内容信息,遵循JSON规范,详情参见tpl_content[参数说明](https://doc.open.alipay.com/doc2/detail.htm?treeId=193&articleId=105249&docType=1#tpl_content) |
+
+* 出参说明
+
+可前往[alipay.pass.template.update](https://docs.open.alipay.com/api_24/alipay.pass.template.update)查看更加详细的参数说明。
+
+### 卡券实例发放
+* API声明
+
+addInstance(tplId: string, tplParams: string, recognitionType: string, recognitionInfo: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| tplId | string | 是 | 支付宝pass模版ID,即调用模板创建接口时返回的tpl_id |
+| tplParams | string | 是 | 模版动态参数信息,对应模板中$变量名$的动态参数,见模板创建接口返回值中的tpl_params字段。示例: |
+| recognitionType | string | 是 | Alipass添加对象识别类型,填写“1”表示订单信息 |
+| recognitionInfo | string | 是 | 支付宝用户识别信息,参见[UID发券组件对接文档](https://docs.open.alipay.com/199/sy3hs4 ) |
+
+* 出参说明
+
+可前往[alipay.pass.instance.add](https://docs.open.alipay.com/api_24/alipay.pass.instance.add)查看更加详细的参数说明。
+
+### 卡券实例更新
+* API声明
+
+updateInstance(serialNumber: string, channelId: string, tplParams: string, status: string, verifyCode: string, verifyType: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| serialNumber | string | 是 | 商户指定卡券唯一值,卡券JSON模板中fileInfo->serialNumber字段对应的值 |
+| channelId | string | 是 | 代理商代替商户发放卡券后,再代替商户更新卡券时,此值为商户的PID/AppID |
+| tplParams | string | 否 | Alipass添加对象识别类型,填写“1”表示订单信息 |
+| status | string | 否 | 券状态,支持更新为USED、CLOSED两种状态 |
+| verifyCode | string | 否 | 核销码串值(当状态变更为USED时,建议传),该值正常为模板中核销区域(Operation)对应的message值 |
+| verifyType | string | 否 | 核销方式,该值正常为模板中核销区域(Operation)对应的format值,verifyCode和verifyType需同时传入 |
+
+* 出参说明
+
+可前往[alipay.pass.instance.update](https://docs.open.alipay.com/api_24/alipay.pass.instance.update)查看更加详细的参数说明。
+
+---
+
+
+## 小程序模板消息 TemplateMessage
+### 发送模板消息
+* API声明
+
+send(toUserId: string, formId: string, userTemplateId: string, page: string, data: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| toUserId | string | 是 | 发送消息的支付宝账号 |
+| formId | string | 是 | 用户发生的交易行为的交易号,或者用户在小程序产生表单提交的表单号,用于信息发送的校验 |
+| userTemplateId | string | 是 | 用户申请的模板id号,固定的模板id会发送固定的消息 |
+| page | string | 是 | 小程序的跳转页面,用于消息中心用户点击之后详细跳转的小程序页面,例如:page/component/index |
+| data | string | 是 | 开发者需要发送模板消息中的自定义部分来替换模板的占位符,例如:{"keyword1": {"value" : "12:00"},"keyword2": {"value" : "20180808"},"keyword3": {"value" : "支付宝"}} |
+
+* 出参说明
+
+可前往[alipay.open.app.mini.templatemessage.send](https://docs.open.alipay.com/api_5/alipay.open.app.mini.templatemessage.send)查看更加详细的参数说明。
+
+---
+
+
+# 会员能力 Member
+## 支付宝身份认证 Identification
+### 身份认证初始化
+* API声明
+
+init(outerOrderNo: string, bizCode: string, identityParam: IdentityParam, merchantConfig: MerchantConfig)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| outerOrderNo | string | 是 | 商户请求的唯一标识,商户要保证其唯一性,值为32位长度的字母数字组合,建议前面几位字符是商户自定义的简称,中间可以使用一段时间,后段可以使用一个随机或递增序列 |
+| bizCode | string | 是 | 认证场景码,入参支持的认证场景码和商户签约的认证场景相关,可选值有如下,FACE:多因子人脸认证;CERT_PHOTO:多因子证照认证;CERT_PHOTO_FACE:多因子证照和人脸认证;SMART_FACE:多因子快捷认证 |
+| identityParam | IdentityParam | 是 | 需要验证的身份信息参数 |
+| merchantConfig | MerchantConfig | 是 | 商户个性化配置 |
+
+IdentityParam对象说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| identityType | string | 是 | 身份信息参数类型,必须传入CERT_INFO |
+| certType | string | 是 | 证件类型,当前支持身份证,必须传入IDENTITY_CARD |
+| certName | string | 是 | 真实姓名 |
+| certNo | string | 是 | 证件号码 |
+
+MerchantConfig对象说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| returnUrl | string | 是 | 需要回跳的目标URL地址,一般指定为商户业务页面 |
+
+* 出参说明
+
+可前往[alipay.user.certify.open.initialize](https://docs.open.alipay.com/api_2/alipay.user.certify.open.initialize)查看更加详细的参数说明。
+
+### 生成认证链接
+* API声明
+
+certify(certifyId: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| certifyId | string | 是 | 本次申请操作的唯一标识,由身份认证初始化接口调用后生成,后续的操作都需要用到 |
+
+* 出参说明
+
+可前往[alipay.user.certify.open.certify](https://docs.open.alipay.com/api_2/alipay.user.certify.open.certify)查看更加详细的参数说明。
+
+### 身份认证记录查询
+* API声明
+
+query(certifyId: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| certifyId | string | 是 | 身份认证操作的唯一标识,由身份认证初始化接口调用后生成 |
+
+* 出参说明
+
+可前往[alipay.user.certify.open.query](https://docs.open.alipay.com/api_2/alipay.user.certify.open.query)查看更加详细的参数说明。
+
+---
+
+
+# 支付能力 Payment
+## 通用接口 Common
+### 创建交易
+* API声明
+
+create(subject: string, outTradeNo: string, totalAmount: string, buyerId: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| subject | string | 是 | 订单标题 |
+| outTradeNo | string | 是 | 商户订单号,64个字符以内,可包含字母、数字、下划线,需保证在商户端不重复 |
+| totalAmount | string | 是 | 订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000] |
+| buyerId | string | 是 | 买家的支付宝唯一用户号(2088开头的16位纯数字) |
+
+* 出参说明
+
+可前往[alipay.trade.create](https://docs.open.alipay.com/api_1/alipay.trade.create)查看更加详细的参数说明。
+
+### 查询交易
+* API声明
+
+query(outTradeNo: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| outTradeNo | string | 是 | 交易创建时传入的商户订单号 |
+
+* 出参说明
+
+可前往[alipay.trade.query](https://docs.open.alipay.com/api_1/alipay.trade.query)查看更加详细的参数说明。
+
+### 交易退款
+* API声明
+
+refund(outTradeNo: string, refundAmount: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| outTradeNo | string | 是 | 交易创建时传入的商户订单号 |
+| refundAmount | string | 是 | 需要退款的金额,该金额不能大于订单金额,单位为元,支持两位小数 |
+
+* 出参说明
+
+可前往[alipay.trade.refund](https://docs.open.alipay.com/api_1/alipay.trade.refund)查看更加详细的参数说明。
+
+### 关闭交易
+* API声明
+
+close(outTradeNo: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| outTradeNo | string | 是 | 交易创建时传入的商户订单号 |
+
+* 出参说明
+
+可前往[alipay.trade.close](https://docs.open.alipay.com/api_1/alipay.trade.close)查看更加详细的参数说明。
+
+### 撤销交易
+* API声明
+
+cancel(outTradeNo: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| outTradeNo | string | 是 | 交易创建时传入的商户订单号 |
+
+* 出参说明
+
+可前往[alipay.trade.cancel](https://docs.open.alipay.com/api_1/alipay.trade.cancel)查看更加详细的参数说明。
+
+### 交易退款查询
+* API声明
+
+queryRefund(outTradeNo: string, outRequestNo: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| outTradeNo | string | 是 | 交易创建时传入的商户订单号 |
+| outRequestNo | string | 是 | 请求退款接口时,传入的退款请求号,如果在退款请求时未传入,则该值为创建交易时的外部交易号 |
+
+* 出参说明
+
+可前往[alipay.trade.fastpay.refund.query](https://opendocs.alipay.com/apis/api_1/alipay.trade.fastpay.refund.query)查看更加详细的参数说明。
+
+
+### 查询对账单下载地址
+* API声明
+
+downloadBill(billType: string, billDate: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| billType | string | 是 | 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单 |
+| billDate | string | 是 | 账单时间:日账单格式为yyyy-MM-dd,最早可下载2016年1月1日开始的日账单;月账单格式为yyyy-MM,最早可下载2016年1月开始的月账单 |
+
+* 出参说明
+
+可前往[alipay.data.dataservice.bill.downloadurl.query](https://opendocs.alipay.com/apis/api_15/alipay.data.dataservice.bill.downloadurl.query)查看更加详细的参数说明。
+
+
+### 异步通知验签
+* API声明
+
+verifyNotify(parameters: map[string]string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| parameters | map[string]string | 是 | 异步通知中收到的待验签的所有参数 |
+
+---
+
+## 花呗分期 Huabei
+### 创建花呗分期交易
+* API声明
+
+create(subject: string, outTradeNo: string, totalAmount: string, buyerId: string, extendParams: HuabeiConfig)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| subject | string | 是 | 订单标题 |
+| outTradeNo | string | 是 | 商户订单号,64个字符以内,可包含字母、数字、下划线,需保证在商户端不重复 |
+| totalAmount | string | 是 | 订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000] |
+| buyerId | string | 是 | 买家的支付宝用户ID,如果为空,会从传入的码值信息中获取买家ID |
+| extendParams | HuabeiConfig | 是 | 花呗交易扩展参数 |
+
+HuabeiConfig对象说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| hbFqNum | string | 是 | 花呗分期数,仅支持传入3、6、12 |
+| hbFqSellerPercent | string | 是 | 代表卖家承担收费比例,商家承担手续费传入100,用户承担手续费传入0,仅支持传入100、0两种 |
+
+
+* 出参说明
+
+可前往[alipay.trade.create](https://docs.open.alipay.com/api_1/alipay.trade.create)查看更加详细的参数说明。
+
+---
+
+
+
+## 当面付 FaceToFace
+### 当面付交易付款
+* API声明
+
+pay(subject: string, outTradeNo: string, totalAmount: string, authCode: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| subject | string | 是 | 订单标题 |
+| outTradeNo | string | 是 | 交易创建时传入的商户订单号 |
+| totalAmount | string | 是 | 订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000] |
+| authCode | string | 是 | 支付授权码,即买家的付款码数字 |
+
+* 出参说明
+
+可前往[alipay.trade.pay](https://docs.open.alipay.com/api_1/alipay.trade.pay)查看更加详细的参数说明。
+
+* 返佣说明
+
+ISV对接当面付产品需涉及返佣时,请先阅读[政策详情](https://opendocs.alipay.com/p/00fc2g)与[合作攻略](https://opendocs.alipay.com/open/300/taphxd)。
+
+
+**对接时必须在支付接口的extend_params参数中设置sys_service_provider_id返佣参数 ,参数值为签约返佣协议的PID**,示例代码如下(Java为例):
+
+```java
+Map extendParams = new HashMap<>();
+extendParams.put("sys_service_provider_id", "<--请填写ISV签约协议的PID,比如:2088511833207846-->");
+AlipayTradePayResponse response = Factory.Payment.FaceToFace()
+ .agent("<--请填写商户应用授权后获取到的app_auth_token,比如:ca34ea491e7146cc87d25fca24c4cD11-->")
+ .optional("extend_params", extendParams)
+ .pay("iPhone6 16G", "64628156-f784-4572-9540-485b7c91b850", "0.01", "289821051157962364");
+```
+
+---
+### 交易预创建,生成正扫二维码
+* API声明
+
+precreate(subject: string, outTradeNo: string, totalAmount: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| subject | string | 是 | 订单标题 |
+| outTradeNo | string | 是 | 交易创建时传入的商户订单号 |
+| totalAmount | string | 是 | 订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000] |
+
+* 出参说明
+
+可前往[alipay.trade.precreate](https://docs.open.alipay.com/api_1/alipay.trade.precreate)查看更加详细的参数说明。
+
+* 返佣说明
+
+ISV对接当面付产品需涉及返佣时,请先阅读[政策详情](https://opendocs.alipay.com/p/00fc2g)与[合作攻略](https://opendocs.alipay.com/open/300/taphxd)。
+
+
+**对接时必须在支付接口的extend_params参数中设置sys_service_provider_id返佣参数 ,参数值为签约返佣协议的PID**,示例代码如下(Java为例):
+
+```java
+Map extendParams = new HashMap<>();
+extendParams.put("sys_service_provider_id", "<--请填写ISV签约协议的PID,比如:2088511833207846-->");
+AlipayTradePrecreateResponse response = Payment.FaceToFace()
+ .agent("<--请填写商户应用授权后获取到的app_auth_token,比如:ca34ea491e7146cc87d25fca24c4cD11-->")
+ .optional("extend_params", extendParams)
+ .preCreate("iPhone6 16G", "64628156-f784-4572-9540-485b7c91b850", "0.01");
+```
+
+---
+## 电脑网站 Page
+### 电脑网站支付
+* API声明
+
+pay(subject: string, outTradeNo: string, totalAmount: string, returnUrl: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| subject | string | 是 | 订单标题 |
+| outTradeNo | string | 是 | 交易创建时传入的商户订单号 |
+| totalAmount | string | 是 | 订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000] |
+| returnUrl | string | 否 | 支付成功后同步跳转的页面,是一个http/https开头的字符串 |
+
+* 出参说明
+
+可前往[alipay.trade.page.pay](https://docs.open.alipay.com/api_1/alipay.trade.page.pay)查看更加详细的参数说明。
+
+---
+
+## 手机网站 Wap
+### 手机网站支付
+* API声明
+
+pay(subject: string, outTradeNo: string, totalAmount: string, quitUrl: string, returnUrl: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| subject | string | 是 | 订单标题 |
+| outTradeNo | string | 是 | 交易创建时传入的商户订单号 |
+| totalAmount | string | 是 | 订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000] |
+| quitUrl | string | 是 | 用户付款中途退出返回商户网站的地址 |
+| returnUrl | string | 否 | 支付成功后同步跳转的页面,是一个http/https开头的字符串 |
+
+* 出参说明
+
+可前往[alipay.trade.wap.pay](https://docs.open.alipay.com/api_1/alipay.trade.wap.pay)查看更加详细的参数说明。
+
+---
+
+## App支付 App
+### 手机APP支付
+* API声明
+
+pay(subject: string, outTradeNo: string, totalAmount: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| subject | string | 是 | 订单标题 |
+| outTradeNo | string | 是 | 交易创建时传入的商户订单号 |
+| totalAmount | string | 是 | 订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000] |
+
+* 出参说明
+
+可前往[alipay.trade.app.pay](https://docs.open.alipay.com/api_1/alipay.trade.app.pay)查看更加详细的参数说明。
+
+---
+
+# 安全能力 Security
+## 文本风险识别 TextRisk
+### 检测内容风险
+* API声明
+
+detect(content: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| content | string | 是 | 待检测的文本内容 |
+
+* 出参说明
+
+可前往[alipay.security.risk.content.detect](https://docs.open.alipay.com/api_49/alipay.security.risk.content.detect)查看更加详细的参数说明。
+
+---
+
+# 辅助工具 Util
+
+
+## 加解密 AES
+### AES解密(常用于会员手机号解密)
+* API声明
+
+decrypt(cipherText: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| cipherText | string | 是 | 密文 |
+
+* 出参说明
+
+| 类型 | 说明 |
+|------|----|
+| string | 明文|
+
+### AES加密
+* API声明
+
+encrypt(plainText: string)
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| plainText | string | 是 | 明文 |
+
+* 出参说明
+
+| 类型 | 说明 |
+|------|----|
+| string | 密文|
+
+
+
+
+## 通用接口 Generic
+### 执行OpenAPI调用
+* API声明
+
+execute(method: string, textParams: map[string]string, bizParams: map[string]any): AlipayOpenApiGenericResponse
+
+* 接口说明
+
+对于Alipay Easy SDK尚未支持的Open API,开发者可以通过调用此方法,通过自行拼装请求参数,完成大部分OpenAPI的调用,且调用时可按需设置所有可选参数。本接口同样会自动为您完成请求的加签和响应的验签工作。
+注:本接口不支持文件型字段的上传。
+
+* 入参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| method | string | 是 | OpenAPI的名称,例如:alipay.trade.pay |
+| textParams | map[string]string | 否 | **没有**包装在`biz_content`下的请求参数集合,例如`app_auth_token`等参数 |
+| bizParams | map[string]any | 否 | 被包装在`biz_content`下的请求参数集合 |
+
+* 出参说明
+
+| 字段名 | 类型 | 必填 | 说明 |
+|------|--------|----|----|
+| httpBody | string | 是 | 网关返回的HTTP响应,是一个JSON格式的字符串,开发者可按需从中解析出响应参数,响应示例:{"alipay_trade_create_response":{"code":"10000","msg":"Success","out_trade_no":"4ac9eac...","trade_no":"202003..."},"sign":"AUumfYgGSe7...02MA=="} |
+| code | string | 是 | [网关返回码](https://docs.open.alipay.com/common/105806) |
+| msg | string | 是 | [网关返回码描述](https://docs.open.alipay.com/common/105806) |
+| subCode | string | 否 | 业务返回码,参见具体的API接口文档 |
+| subMsg | string | 否 | 业务返回码描述,参见具体的API接口文档 |
+
+---
+
+
+
+
diff --git a/vendor/alipaysdk/easysdk/CHANGELOG b/vendor/alipaysdk/easysdk/CHANGELOG
new file mode 100644
index 00000000..bb0e7550
--- /dev/null
+++ b/vendor/alipaysdk/easysdk/CHANGELOG
@@ -0,0 +1,182 @@
+最新变更
+java删除Factory.getClient方法
+php删除php中多余的composer包
+
+Java版本
+2022-05-06 Version: 2.2.2
+1. 删除Factory.getClient方法
+
+2021-09-04 Version: 2.2.1
+1. 修复Okhttp Response没有主动关闭的问题。
+
+2021-01-18 Version: 2.2.0
+1. 增加sdkExecute功能。
+2. 增加fileExecute功能。
+3. 增加MultipleFactory多实例调用。
+
+2020-12-11 Version: 2.1.2
+1. 增加可设置ignoreSSL忽略SSL校验功能。
+
+2020-09-23 Version: 2.1.0
+1. 升级Tea版本,降低对OkHttp的特性依赖,提升环境兼容性。
+2. 提供Factory.getClient方法,用于调用SDK扩展包中的方法。
+
+2020-08-18 Version: 2.0.2
+1. 取消shade打包,便于排除冲突依赖。
+
+2020-07-06 Version: 2.0.1
+1. 私钥支持阿里云KMS。
+
+2020-06-09 Version: 2.0.0
+1. 支持可选业务参数的装配。
+2. 支持ISV代调用。
+3. 提供ResponseChecker辅助工具类,帮助校验响应是否成功。
+
+2020-05-06 Version: 1.2.1
+1. 手机网站支付、电脑网站支付接口支持设置return_url同步跳转地址。
+
+2020-04-15 Version: 1.2.0
+1. 扩展支持的支付类OpenAPI接口
+Factory.Payment.Common().queryRefund 查询退款信息
+Factory.Payment.Common().downloadBill 下载对账单
+Factory.Payment.FaceToFace().preCreate 交易预创建,生成正扫二维码
+Factory.Payment.Wap().pay 手机网站支付
+Factory.Payment.Page().pay 电脑网站支付
+Factory.Payment.App().pay 手机APP支付
+2. 支持支付的异步通知及其验签
+初始化Alipay Easy SDK的Config参数中新增notifyUrl参数,用户可以统一配置自己的回调地址。
+提供如下接口,完成支付类异步通知的验签。
+Factory.Payment.Common().verifyNotify
+3. AES加解密功能
+Factory.Util.AES().decrypt 支持会员手机号AES解密
+Factory.Util.AES().encrypt AES加密
+
+2020-03-31 Version: 1.1.3
+1. 去除SDK内置的logback.xml日志配置文件,以免意外覆盖开发者项目主体工程的日志配置。
+
+2020-03-27 Version: 1.1.2
+1. 修复返回的响应中存在数组类型字段时,反序列化成Response对象可能抛异常的问题。
+
+2020-03-16 Version: 1.1.1
+1. 修复证书路径不支持从CLASS_PATH中加载的问题。
+
+2020-03-10 Version: 1.1.0
+1. 添加兜底通用接口,支持通过自行拼接请求参数完成几乎所有OpenAPI的调用。
+
+2020-02-26 Version: 1.0.0
+1. 首次发布。
+
+
+
+C#版本
+2020-12-11 Version: 2.1.3
+1. 修复OpenAPI响应对象特殊情况下可能反序列化失败的异常。
+
+2020-12-11 Version: 2.1.2
+1. 增加可设置ignoreSSL忽略SSL校验功能。
+
+2020-12-09 Version: 2.1.1
+1. 增加httpProxy功能。
+
+2020-09-23 Version: 2.1.0
+1. 升级Tea版本。
+2. 提供Factory.getClient方法,用于调用SDK扩展包中的方法。
+
+2020-08-18 Version: 2.0.1
+1. 修复证书模式下异步验签异常的问题。
+
+2020-06-09 Version: 2.0.0
+1. 支持可选业务参数的装配。
+2. 支持ISV代调用。
+3. 提供ResponseChecker辅助工具类,帮助校验响应是否成功。
+
+2020-05-06 Version: 1.2.1
+1. 手机网站支付、电脑网站支付接口支持设置return_url同步跳转地址。
+
+2020-04-15 Version: 1.2.0
+1. 扩展支持的支付类OpenAPI接口
+Factory.Payment.Common().QueryRefund 查询退款信息
+Factory.Payment.Common().DownloadBill 下载对账单
+Factory.Payment.FaceToFace().PreCreate 交易预创建,生成正扫二维码
+Factory.Payment.Wap().Pay 手机网站支付
+Factory.Payment.Page().Pay 电脑网站支付
+Factory.Payment.App().Pay 手机APP支付
+2. 支持支付的异步通知及其验签
+初始化Alipay Easy SDK的Config参数中新增notifyUrl参数,用户可以统一配置自己的回调地址。
+提供如下接口,完成支付类异步通知的验签。
+Factory.Payment.Common().verifyNotify
+3. AES加解密功能
+Factory.Util.AES().Decrypt 支持会员手机号AES解密
+Factory.Util.AES().Encrypt AES加密
+
+2020-03-10 Version: 1.1.0
+1. 添加兜底通用接口,支持通过自行拼接请求参数完成几乎所有OpenAPI的调用。
+
+2020-02-26 Version: 1.0.0
+1. 首次发布。
+
+
+
+PHP版本
+2022-11-28 Version: 2.2.3
+1、去掉多余引号
+2、toMultipartRequestBody方法中,PHP8时 0 != $readLength 永远为true导致while产生死循环
+
+2022-05-06 Version: 2.2.2
+1. php删除php中多余的composer包
+
+2021-09-24 Version: 2.2.1
+1. 修复PHP7.4及其以上环境下,根证书解析报错的问题。
+
+2021-01-18 Version: 2.2.0
+1. 增加sdkExecute功能。
+2. 增加fileExecute功能。
+3. 增加MultipleFactory多实例调用。
+
+
+2020-12-11 Version: 2.0.3
+1. 增加可设置ignoreSSL忽略SSL校验功能。
+
+2020-12-09 Version: 2.0.2
+1. 增加httpProxy功能。
+2. 修复agent不生效问题。
+
+2020-07-06 Version: 2.0.0
+1. 支持可选业务参数的装配。
+2. 支持ISV代调用。
+3. 提供ResponseChecker辅助工具类,帮助校验响应是否成功。
+
+2020-05-06 Version: 1.2.1
+1. 手机网站支付、电脑网站支付接口支持设置return_url同步跳转地址。
+
+2020-04-15 Version: 1.2.0
+1. 扩展支持的支付类OpenAPI接口
+Factory::payment()->common()->queryRefund 查询退款信息
+Factory::payment()->common()->downloadBill 下载对账单
+Factory::payment()->faceToFace()->preCreate 交易预创建,生成正扫二维码
+Factory::payment()->wap()->pay 手机网站支付
+Factory::payment()->page()->pay 电脑网站支付
+Factory::payment()->app()->pay 手机APP支付
+2. 支持支付的异步通知及其验签
+初始化Alipay Easy SDK的Config参数中新增notifyUrl参数,用户可以统一配置自己的回调地址。
+提供如下接口,完成支付类异步通知的验签。
+Factory::payment()->common()->verifyNotify
+3. AES加解密功能
+Factory::util()->aes()->decrypt 支持会员手机号AES解密
+Factory::util()->aes()->encrypt AES加密
+4. 重构api的respone模型,返回格式与Java、Net保持一致
+
+2020-03-27 Version: 1.1.0
+1. 修复大小写路径敏感问题。
+
+2020-03-20 Version: 1.0.0
+1. 首次发布。
+
+
+PHP版本
+2021-09-24 Version: 2.0.1
+1.修复PHP7.4及其以上环境下,根证书解析报错的问题。
+
+2021-01-18 Version: 1.0.0
+1. 首次发布。
+
diff --git a/vendor/alipaysdk/easysdk/LICENSE b/vendor/alipaysdk/easysdk/LICENSE
new file mode 100644
index 00000000..8d8fa6ec
--- /dev/null
+++ b/vendor/alipaysdk/easysdk/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 Ant Small and Micro Financial Services Group Co., Ltd.
+
+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.
diff --git a/vendor/alipaysdk/easysdk/README.md b/vendor/alipaysdk/easysdk/README.md
new file mode 100644
index 00000000..08989036
--- /dev/null
+++ b/vendor/alipaysdk/easysdk/README.md
@@ -0,0 +1,203 @@
+[](https://app.fossa.com/projects/git%2Bgithub.com%2Falipay%2Falipay-easysdk?ref=badge_shield)
+[](https://mvnrepository.com/artifact/com.alipay.sdk/alipay-easysdk)
+[](https://badge.fury.io/nu/AlipayEasySDK)
+[](https://packagist.org/packages/alipaysdk/easysdk)
+
+欢迎使用 Alipay **Easy** SDK。
+
+打造**最好用**的支付宝开放平台**服务端SDK**,Alipay Easy SDK让您享受**极简编程**体验,快速访问支付宝开放平台开放的各项**核心能力**。
+
+## 设计理念
+不同于原有的Alipay SDK通用而全面的设计理念,Alipay Easy SDK对开放能力的API进行了更加贴近高频场景的精心设计与裁剪,简化了服务端调用方式,让调用API像使用语言内置的函数一样简便。
+
+同时,您也不必担心面向高频场景提炼的API可能无法完全契合自己的个性化场景,Alipay Easy SDK支持灵活的[动态扩展](#extension)方式,同样可以满足低频参数、低频API的使用需求。
+
+Alipay Easy SDK提供了与[能力地图](https://opendocs.alipay.com/mini/00am3f)相对应的代码组织结构,让开发者可以快速找到不同能力对应的API。
+
+Alipay Easy SDK主要目标是提升开发者在**服务端**集成支付宝开放平台开放的各类核心能力的效率。
+
+### 化繁为简
+
+| Alipay Easy SDK | Alipay SDK |
+|------------------|----------------------------------------------------------------|
+| 极简代码风格,更贴近自然语言阅读习惯 | 传统代码风格,需要多行代码完成一个接口的调用 |
+| Factory单例全局任何地方都可直接引用 | AlipayClient实例需自行创建并在上下文中传递 |
+| API中只保留高频场景下的必备参数,同时提供低频可选参数的装配能力 | 没有区分高低频参数,单API最多可达数十个入参,对普通开发者的干扰较大 |
+
+
+* Alipay Easy SDK :smiley:
+
+```java
+Factory.Payment.Common().create("Iphone6 16G", "202003019443", "0.10", "2088002656718920");
+```
+
+* Alipay SDK :confused:
+
+```java
+AlipayTradeCreateRequest request = new AlipayTradeCreateRequest();
+
+AlipayTradeCreateModel model = new AlipayTradeCreateModel();
+model.setSubject("Iphone6 16G");
+model.setOutTradeNo("202003019443");
+model.setTotalAmount("0.10");
+model.setBuyerId("2088002656718920");
+...
+
+request.setBizModel(model);
+...
+
+alipayClient.execute(request);
+```
+
+### 如何切换
+* 无论是Alipay Easy SDK还是Alipay SDK,本质都是发送HTTP请求访问Open API网关,所以只需将原来通过Alipay SDK调用Open API的代码,替换为Alipay Easy SDK中对应API的调用即可。Alipay Easy SDK和Alipay SDK并无冲突,可以共存。
+
+* 如果您所需对接的开放平台能力,Alipay Easy SDK尚未提炼出API支持([已支持的API列表](#apiList)),您可以通过[通用接口](./APIDoc.md#generic)完成调用。
+
+* 我们会持续挖掘高频场景,不断丰富Alipay Easy SDK支持的API,让您在绝大多数常见场景下,都能享受Alipay Easy SDK带来的便捷。
+
+## 技术特点
+### 纯语言开发
+所有Alipay Easy SDK的具体编程语言的实现,均只采用纯编程语言进行开发,不引入任何重量级框架,减少潜在的框架冲突,让SDK可以自由集成进任何代码环境中。
+
+### 结构清晰
+我们按照能力类别和场景类别对API进行了归类,结构更加清晰,一目了然。
+> 更多信息请参见[API组织规范](#spec)。
+
+### 参数精简
+Alipay Easy SDK对每个API都精心打磨,剔除了`Open API`中不常用的可选参数,减少普通用户的无效选择,提升开发效率。
+
+
+
+### 灵活扩展
+开发者可以通过Fluent风格的API链式调用,在为高频场景打造的API基础上,不断扩展自己的个性化场景需求。
+
+```java
+// 通过调用agent方法,扩展支持ISV代调用场景
+Factory.Payment.FaceToFace().agent("ca34ea491e7146cc87d25fca24c4cD11").preCreate(...)
+
+// 通过调用optional方法,扩展支持个性化可选参数
+Factory.Payment.FaceToFace().optional("extend_params", extendParams).preCreate(...)
+
+// 多种扩展可灵活搭配,不同扩展方法功能详细说明请前往各语言主页中的“快速开始-扩展调用”栏目中查看
+Factory.Payment.FaceToFace()
+ .agent(...)
+ .optionalArgs(...)
+ .auth(...)
+ .asyncNotify(...)
+ .preCreate(...)
+```
+
+### 测试/示例完备
+每个API都有对应的单元测试进行覆盖,良好的单元测试天生就是最好的示例。
+
+同时您也可以前往[API Doc](./APIDoc.md)查看每个API的详细使用说明。
+
+> 注:单元测试中使用到的私钥均进行了脱敏处理,会导致单元测试无法直接执行。您可以自行更改单元测试项目中的`TestAccout类`和`privateKey.json`文件中的相关账号与私钥配置后再执行单元测试。
+
+### 多语言
+Alipay Easy SDK基于阿里集团研发的[`Darabonba`](https://github.com/aliyun/darabonba)进行架构,通过DSL中间语言定义API模型,再基于DSL语言自动生成不同编程语言(Java、C#、PHP、TS等)实现的SDK,极大地提升了SDK能力的扩展效率和适用范围,同时也保证了相同的`Easy API`在不同语言生态中体验的一致性。
+
+API模型的DSL描述可以进入[tea](./tea)目录查看。
+
+### 快速集成
+各语言SDK均会在各自的中央仓库(Maven、NuGet、Composer、NPM etc.)中同步发布,让您使用各语言主流依赖管理工具即可一键安装集成SDK。
+
+## 语言支持情况
+Alipay Easy SDK首发暂只支持`Java`、`C#`、`PHP`编程语言,更多编程语言支持正在积极新增中,敬请期待。
+
+各语言具体的**使用说明**和**详细介绍**请点击如下链接进入各语言主目录查看。
+
+[Java](./java)
+
+[C#](./csharp)
+
+[PHP](./php)
+
+
+
+## API组织规范
+
+在Alipay Easy SDK中,API的引用路径与能力地图的组织层次一致,遵循如下规范
+
+> Factory.能力类别.场景类别.接口方法名称( ... )
+
+比如,如果您想要使用[能力地图](https://opendocs.alipay.com/mini/00am3f)中`营销能力`下的`模板消息`场景中的`小程序发送模板消息`,只需按如下形式编写调用代码即可(不同编程语言的连接符号可能不同)。
+
+`Factory.Marketing.TemplateMessage().send( ... )`
+
+其中,接口方法名称通常是对其依赖的OpenAPI功能的一个最简概况,接口方法的出入参与OpenAPI中同名参数含义一致,可参照OpenAPI相关参数的使用说明。
+
+Alipay Easy SDK将致力于保持良好的API命名,以符合开发者的编程直觉。
+
+
+
+## 已支持的API列表
+
+| 能力类别 | 场景类别 | 接口方法名称 | 调用的OpenAPI名称 |
+|-----------|-----------------|------------------------|-----------------------------------------------------------|
+| Base
基础能力 | OAuth
用户授权 | getToken
获取授权访问令牌和用户user_id | alipay\.system\.oauth\.token |
+| Base
基础能力 | OAuth
用户授权 | refreshToken
刷新授权访问令牌 | alipay\.system\.oauth\.token |
+| Base
基础能力 | Qrcode
小程序二维码 | create
创建小程序二维码 | alipay\.open\.app\.qrcode\.create |
+| Base
基础能力 | Image
图片 | upload
上传门店照片 | alipay\.offline\.material\.image\.upload |
+| Base
基础能力 | Video
视频 | upload
上传门店视频 | alipay\.offline\.material\.image\.upload |
+| Member
会员能力 | Identification
支付宝身份认证 | init
身份认证初始化 | alipay\.user\.certify\.open\.initialize |
+| Member
会员能力 | Identification
支付宝身份认证 | certify
生成认证链接 | alipay\.user\.certify\.open\.certify |
+| Member
会员能力 | Identification
支付宝身份认证 | query
身份认证记录查询 | alipay\.user\.certify\.open\.query |
+| Payment
支付能力 | Common
通用 | create
创建交易 | alipay\.trade\.create |
+| Payment
支付能力 | Common
通用 | query
查询交易 | alipay\.trade\.query |
+| Payment
支付能力 | Common
通用 | refund
交易退款 | alipay\.trade\.refund |
+| Payment
支付能力 | Common
通用 | close
关闭交易 | alipay\.trade\.close |
+| Payment
支付能力 | Common
通用 | cancel
撤销交易 | alipay\.trade\.cancel |
+| Payment
支付能力 | Common
通用 | queryRefund
交易退款查询 | alipay\.trade\.fastpay\.refund\.query |
+| Payment
支付能力 | Common
通用 | downloadBill
查询对账单下载地址 | alipay\.data\.dataservice\.bill\.downloadurl\.query |
+| Payment
支付能力 | Common
通用 | verifyNotify
异步通知验签 | - |
+| Payment
支付能力 | Huabei
花呗分期 | create
创建花呗分期交易 | alipay\.trade\.create |
+| Payment
支付能力 | FaceToFace
当面付 | pay
扫用户出示的付款码,完成付款 | alipay\.trade\.pay |
+| Payment
支付能力 | FaceToFace
当面付 | precreate
生成交易付款码,待用户扫码付款 | alipay\.trade\.precreate |
+| Payment
支付能力 | App
手机APP | pay
生成订单串,再使用客户端 SDK 凭此串唤起支付宝收银台 | alipay\.trade\.app\.pay |
+| Payment
支付能力 | Page
电脑网站 | pay
生成交易表单,渲染后自动跳转支付宝网站引导用户完成支付 | alipay\.trade\.page\.pay |
+| Payment
支付能力 | Wap
手机网站 | pay
生成交易表单,渲染后自动跳转支付宝网站引导用户完成支付 | alipay\.trade\.wap\.pay |
+| Security
安全能力 | TextRisk
文本内容安全 | detect
检测内容风险 | alipay\.security\.risk\.content\.detect |
+| Marketing
营销能力 | Pass
支付宝卡包 | createTemplate
卡券模板创建 | alipay\.pass\.template\.add |
+| Marketing
营销能力 | Pass
支付宝卡包 | updateTemplate
卡券模板更新 | alipay\.pass\.template\.update |
+| Marketing
营销能力 | Pass
支付宝卡包 | addInstance
卡券实例发放 | alipay\.pass\.instance\.add |
+| Marketing
营销能力 | Pass
支付宝卡包 | updateInstance
卡券实例更新 | alipay\.pass\.instance\.update |
+| Marketing
营销能力 | TemplateMessage
小程序模板消息 | send
发送模板消息| alipay\.open\.app\.mini\.templatemessage\.send |
+| Marketing
营销能力 | OpenLife
生活号 | createImageTextContent
创建图文消息内容 | alipay\.open\.public\.message\.content\.create |
+| Marketing
营销能力 | OpenLife
生活号 | modifyImageTextContent
更新图文消息内容 | alipay\.open\.public\.message\.content\.modify |
+| Marketing
营销能力 | OpenLife
生活号 | sendText
群发本文消息 | alipay\.open\.public\.message\.total\.send |
+| Marketing
营销能力 | OpenLife
生活号 | sendImageText
群发图文消息 | alipay\.open\.public\.message\.total\.send |
+| Marketing
营销能力 | OpenLife
生活号 | sendSingleMessage
单发模板消息 | alipay\.open\.public\.message\.single\.send |
+| Marketing
营销能力 | OpenLife
生活号 | recallMessage
生活号消息撤回 | alipay\.open\.public\.life\.msg\.recall |
+| Marketing
营销能力 | OpenLife
生活号 | setIndustry
模板消息行业设置 | alipay\.open\.public\.template\.message\.industry\.modify |
+| Marketing
营销能力 | OpenLife
生活号 | getIndustry
生活号查询行业设置 | alipay\.open\.public\.setting\.category\.query |
+| Util
辅助工具 | AES
加解密 | decrypt
解密,常用于会员手机号解密 | - |
+| Util
辅助工具 | AES
加解密 | encrypt
加密 | - |
+| Util
辅助工具 | Generic
通用接口 | execute
自行拼接参数,执行OpenAPI调用 | - |
+
+> 注:更多高频场景的API持续更新中,敬请期待。
+
+您还可以前往[API Doc](./APIDoc.md)查看每个API的详细使用说明。
+
+# 变更日志
+每个版本的详细更改记录在[变更日志](./CHANGELOG)中。
+
+> 版本号最末一位修订号的增加(比如从`1.0.0`升级为`1.0.1`),意味着SDK的功能没有发生任何变化,仅仅是修复了部分Bug。该类升级可能不会记录在变更日志中。
+
+> 版本号中间一位次版本号的增加(比如从`1.0.0`升级为`1.1.0`),意味着SDK的功能发生了可向下兼容的新增或修改。
+
+> 版本号首位主版本号的增加(比如从`1.0.0`升级为`2.0.0`),意味着SDK的功能可能发生了不向下兼容的较大调整,升级主版本号后请注意做好相关的回归测试工作。
+
+# 相关
+* [支付宝开放平台](https://open.alipay.com/platform/home.htm)
+* [支付宝开放平台文档中心](https://docs.open.alipay.com/catalog)
+* [最新源码](https://github.com/alipay/alipay-easysdk)
+
+# 许可证
+[](https://app.fossa.com/projects/git%2Bgithub.com%2Falipay%2Falipay-easysdk?ref=badge_large)
+
+# 交流与技术支持
+不管您在使用Alipay Easy SDK的过程中遇到任何问题,欢迎前往 [支付宝开放社区](https://forum.alipay.com/mini-app/channel/1100001) 发帖与支付宝工作人员和其他开发者一起交流。
+
+注:为了提高开发者问题的响应时效,github本身的issue功能已关闭,支付宝开放社区中发帖的问题,通常会在2小时内响应。
diff --git a/vendor/alipaysdk/easysdk/composer.json b/vendor/alipaysdk/easysdk/composer.json
new file mode 100755
index 00000000..660cb502
--- /dev/null
+++ b/vendor/alipaysdk/easysdk/composer.json
@@ -0,0 +1,49 @@
+{
+ "name":"alipaysdk/easysdk",
+ "description":"支付宝官方 Alipay Easy SDK",
+ "type":"library",
+ "version":"2.2.3",
+ "authors":[
+ {
+ "name":"junying.wjy",
+ "email":"junying.wjy@antfin.com"
+ }
+ ],
+ "autoload":{
+ "psr-4":{
+ "Alipay\\EasySDK\\":"php/src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Alipay\\EasySDK\\Test\\": "php/test/"
+ }
+ },
+ "license":"Apache-2.0",
+ "minimum-stability":"dev",
+ "require":{
+ "php": ">=7.0",
+ "ext-curl":"*",
+ "ext-ctype":"*",
+ "ext-dom":"*",
+ "ext-fileinfo": "*",
+ "ext-json":"*",
+ "ext-libxml":"*",
+ "ext-simplexml":"*",
+ "ext-mbstring":"*",
+ "ext-openssl":"*",
+ "ext-xmlwriter": "*",
+ "guzzlehttp/guzzle":">=6.3",
+ "alibabacloud/tea": "^3.1",
+ "alibabacloud/tea-fileform": "^0.3.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^7.5"
+ },
+ "repositories":{
+ "packagist":{
+ "type":"composer",
+ "url":"https://mirrors.aliyun.com/composer/"
+ }
+ }
+}
diff --git a/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK.sln b/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK.sln
new file mode 100644
index 00000000..117bda96
--- /dev/null
+++ b/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK.sln
@@ -0,0 +1,23 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlipayEasySDK", "AlipayEasySDK\AlipayEasySDK.csproj", "{14B089B9-C61C-46E6-BD93-5DFBBB77E2B2}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTest", "UnitTest\UnitTest.csproj", "{79DE080D-34C1-485E-996D-435A8515766D}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {14B089B9-C61C-46E6-BD93-5DFBBB77E2B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {14B089B9-C61C-46E6-BD93-5DFBBB77E2B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {14B089B9-C61C-46E6-BD93-5DFBBB77E2B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {14B089B9-C61C-46E6-BD93-5DFBBB77E2B2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {79DE080D-34C1-485E-996D-435A8515766D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {79DE080D-34C1-485E-996D-435A8515766D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {79DE080D-34C1-485E-996D-435A8515766D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {79DE080D-34C1-485E-996D-435A8515766D}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/AlipayEasySDK.csproj b/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/AlipayEasySDK.csproj
new file mode 100644
index 00000000..266452d2
--- /dev/null
+++ b/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/AlipayEasySDK.csproj
@@ -0,0 +1,32 @@
+
+
+
+ netstandard2.0;net461
+ true
+ 2.1.3
+ antopen
+ zh
+ https://github.com/alipay/alipay-easysdk/blob/master/LICENSE
+ Alipay Easy SDK for .NET allows you to enjoy a minimalist programming experience and quickly access the various high-frequency capabilities of the Alipay Open Platform.
+ AlipayEasySDK
+ antopen
+ https://github.com/alipay/alipay-easysdk/tree/master/csharp
+ Alipay Easy SDK for .NET allows you to enjoy a minimalist programming experience and quickly access the various high-frequency capabilities of the Alipay Open Platform.
+ Alipay Easy SDK
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Base/Image/Client.cs b/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Base/Image/Client.cs
new file mode 100644
index 00000000..a2ad6585
--- /dev/null
+++ b/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Base/Image/Client.cs
@@ -0,0 +1,324 @@
+// This file is auto-generated, don't edit it. Thanks.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+
+using Tea;
+using Tea.Utils;
+
+using Alipay.EasySDK.Base.Image.Models;
+
+namespace Alipay.EasySDK.Base.Image
+{
+ public class Client
+ {
+ protected Alipay.EasySDK.Kernel.Client _kernel;
+
+ public Client(Alipay.EasySDK.Kernel.Client kernel)
+ {
+ this._kernel = kernel;
+ }
+
+ public AlipayOfflineMaterialImageUploadResponse Upload(string imageName, string imageFilePath)
+ {
+ Dictionary runtime_ = new Dictionary
+ {
+ {"ignoreSSL", this._kernel.GetConfig("ignoreSSL")},
+ {"httpProxy", this._kernel.GetConfig("httpProxy")},
+ {"connectTimeout", 100000},
+ {"readTimeout", 100000},
+ {"retry", new Dictionary
+ {
+ {"maxAttempts", 0},
+ }},
+ };
+
+ TeaRequest _lastRequest = null;
+ Exception _lastException = null;
+ long _now = System.DateTime.Now.Millisecond;
+ int _retryTimes = 0;
+ while (TeaCore.AllowRetry((IDictionary) runtime_["retry"], _retryTimes, _now))
+ {
+ if (_retryTimes > 0)
+ {
+ int backoffTime = TeaCore.GetBackoffTime((IDictionary)runtime_["backoff"], _retryTimes);
+ if (backoffTime > 0)
+ {
+ TeaCore.Sleep(backoffTime);
+ }
+ }
+ _retryTimes = _retryTimes + 1;
+ try
+ {
+ TeaRequest request_ = new TeaRequest();
+ Dictionary systemParams = new Dictionary
+ {
+ {"method", "alipay.offline.material.image.upload"},
+ {"app_id", this._kernel.GetConfig("appId")},
+ {"timestamp", this._kernel.GetTimestamp()},
+ {"format", "json"},
+ {"version", "1.0"},
+ {"alipay_sdk", this._kernel.GetSdkVersion()},
+ {"charset", "UTF-8"},
+ {"sign_type", this._kernel.GetConfig("signType")},
+ {"app_cert_sn", this._kernel.GetMerchantCertSN()},
+ {"alipay_root_cert_sn", this._kernel.GetAlipayRootCertSN()},
+ };
+ Dictionary bizParams = new Dictionary(){};
+ Dictionary textParams = new Dictionary
+ {
+ {"image_type", "jpg"},
+ {"image_name", imageName},
+ };
+ Dictionary fileParams = new Dictionary
+ {
+ {"image_content", imageFilePath},
+ };
+ string boundary = this._kernel.GetRandomBoundary();
+ request_.Protocol = this._kernel.GetConfig("protocol");
+ request_.Method = "POST";
+ request_.Pathname = "/gateway.do";
+ request_.Headers = new Dictionary
+ {
+ {"host", this._kernel.GetConfig("gatewayHost")},
+ {"content-type", this._kernel.ConcatStr("multipart/form-data;charset=utf-8;boundary=", boundary)},
+ };
+ request_.Query = this._kernel.SortMap(TeaConverter.merge
+ (
+ new Dictionary()
+ {
+ {"sign", this._kernel.Sign(systemParams, bizParams, textParams, this._kernel.GetConfig("merchantPrivateKey"))},
+ },
+ systemParams
+ ));
+ request_.Body = this._kernel.ToMultipartRequestBody(textParams, fileParams, boundary);
+ _lastRequest = request_;
+ TeaResponse response_ = TeaCore.DoAction(request_, runtime_);
+
+ Dictionary respMap = this._kernel.ReadAsJson(response_, "alipay.offline.material.image.upload");
+ if (this._kernel.IsCertMode())
+ {
+ if (this._kernel.Verify(respMap, this._kernel.ExtractAlipayPublicKey(this._kernel.GetAlipayCertSN(respMap))))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ else
+ {
+ if (this._kernel.Verify(respMap, this._kernel.GetConfig("alipayPublicKey")))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ throw new TeaException(new Dictionary
+ {
+ {"message", "验签失败,请检查支付宝公钥设置是否正确。"},
+ });
+ }
+ catch (Exception e)
+ {
+ if (TeaCore.IsRetryable(e))
+ {
+ _lastException = e;
+ continue;
+ }
+ throw e;
+ }
+ }
+
+ throw new TeaUnretryableException(_lastRequest, _lastException);
+ }
+
+ public async Task UploadAsync(string imageName, string imageFilePath)
+ {
+ Dictionary runtime_ = new Dictionary
+ {
+ {"ignoreSSL", this._kernel.GetConfig("ignoreSSL")},
+ {"httpProxy", this._kernel.GetConfig("httpProxy")},
+ {"connectTimeout", 100000},
+ {"readTimeout", 100000},
+ {"retry", new Dictionary
+ {
+ {"maxAttempts", 0},
+ }},
+ };
+
+ TeaRequest _lastRequest = null;
+ Exception _lastException = null;
+ long _now = System.DateTime.Now.Millisecond;
+ int _retryTimes = 0;
+ while (TeaCore.AllowRetry((IDictionary) runtime_["retry"], _retryTimes, _now))
+ {
+ if (_retryTimes > 0)
+ {
+ int backoffTime = TeaCore.GetBackoffTime((IDictionary)runtime_["backoff"], _retryTimes);
+ if (backoffTime > 0)
+ {
+ TeaCore.Sleep(backoffTime);
+ }
+ }
+ _retryTimes = _retryTimes + 1;
+ try
+ {
+ TeaRequest request_ = new TeaRequest();
+ Dictionary systemParams = new Dictionary
+ {
+ {"method", "alipay.offline.material.image.upload"},
+ {"app_id", this._kernel.GetConfig("appId")},
+ {"timestamp", this._kernel.GetTimestamp()},
+ {"format", "json"},
+ {"version", "1.0"},
+ {"alipay_sdk", this._kernel.GetSdkVersion()},
+ {"charset", "UTF-8"},
+ {"sign_type", this._kernel.GetConfig("signType")},
+ {"app_cert_sn", this._kernel.GetMerchantCertSN()},
+ {"alipay_root_cert_sn", this._kernel.GetAlipayRootCertSN()},
+ };
+ Dictionary bizParams = new Dictionary(){};
+ Dictionary textParams = new Dictionary
+ {
+ {"image_type", "jpg"},
+ {"image_name", imageName},
+ };
+ Dictionary fileParams = new Dictionary
+ {
+ {"image_content", imageFilePath},
+ };
+ string boundary = this._kernel.GetRandomBoundary();
+ request_.Protocol = this._kernel.GetConfig("protocol");
+ request_.Method = "POST";
+ request_.Pathname = "/gateway.do";
+ request_.Headers = new Dictionary
+ {
+ {"host", this._kernel.GetConfig("gatewayHost")},
+ {"content-type", this._kernel.ConcatStr("multipart/form-data;charset=utf-8;boundary=", boundary)},
+ };
+ request_.Query = this._kernel.SortMap(TeaConverter.merge
+ (
+ new Dictionary()
+ {
+ {"sign", this._kernel.Sign(systemParams, bizParams, textParams, this._kernel.GetConfig("merchantPrivateKey"))},
+ },
+ systemParams
+ ));
+ request_.Body = this._kernel.ToMultipartRequestBody(textParams, fileParams, boundary);
+ _lastRequest = request_;
+ TeaResponse response_ = await TeaCore.DoActionAsync(request_, runtime_);
+
+ Dictionary respMap = await this._kernel.ReadAsJsonAsync(response_, "alipay.offline.material.image.upload");
+ if (this._kernel.IsCertMode())
+ {
+ if (this._kernel.Verify(respMap, this._kernel.ExtractAlipayPublicKey(this._kernel.GetAlipayCertSN(respMap))))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ else
+ {
+ if (this._kernel.Verify(respMap, this._kernel.GetConfig("alipayPublicKey")))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ throw new TeaException(new Dictionary
+ {
+ {"message", "验签失败,请检查支付宝公钥设置是否正确。"},
+ });
+ }
+ catch (Exception e)
+ {
+ if (TeaCore.IsRetryable(e))
+ {
+ _lastException = e;
+ continue;
+ }
+ throw e;
+ }
+ }
+
+ throw new TeaUnretryableException(_lastRequest, _lastException);
+ }
+
+
+ ///
+ /// ISV代商户代用,指定appAuthToken
+ ///
+ /// 代调用token
+ /// 本客户端,便于链式调用
+ public Client Agent(string appAuthToken)
+ {
+ _kernel.InjectTextParam("app_auth_token", appAuthToken);
+ return this;
+ }
+
+ ///
+ /// 用户授权调用,指定authToken
+ ///
+ /// 用户授权token
+ /// 本客户端,便于链式调用
+ public Client Auth(string authToken)
+ {
+ _kernel.InjectTextParam("auth_token", authToken);
+ return this;
+ }
+
+ ///
+ /// 设置异步通知回调地址,此处设置将在本调用中覆盖Config中的全局配置
+ ///
+ /// 异步通知回调地址,例如:https://www.test.com/callback
+ /// 本客户端,便于链式调用
+ public Client AsyncNotify(string url)
+ {
+ _kernel.InjectTextParam("notify_url", url);
+ return this;
+ }
+
+ ///
+ /// 将本次调用强制路由到后端系统的测试地址上,常用于线下环境内外联调,沙箱与线上环境设置无效
+ ///
+ /// 后端系统测试地址
+ /// 本客户端,便于链式调用
+ public Client Route(string testUrl)
+ {
+ _kernel.InjectTextParam("ws_service_url", testUrl);
+ return this;
+ }
+
+ ///
+ /// 设置API入参中没有的其他可选业务请求参数(biz_content下的字段)
+ ///
+ /// 业务请求参数名称(biz_content下的字段名,比如timeout_express)
+ ///
+ /// 业务请求参数的值,一个可以序列化成JSON的对象
+ /// 如果该字段是一个字符串类型(String、Price、Date在SDK中都是字符串),请使用string储存
+ /// 如果该字段是一个数值型类型(比如:Number),请使用long储存
+ /// 如果该字段是一个复杂类型,请使用嵌套的Dictionary指定各下级字段的值
+ /// 如果该字段是一个数组,请使用List储存各个值
+ /// 对于更复杂的情况,也支持Dictionary和List的各种组合嵌套,比如参数是值是个List,List中的每种类型是一个复杂对象
+ ///
+ /// 本客户端,便于链式调用
+ public Client Optional(string key, object value)
+ {
+ _kernel.InjectBizParam(key, value);
+ return this;
+ }
+
+ ///
+ /// 批量设置API入参中没有的其他可选业务请求参数(biz_content下的字段)
+ /// optional方法的批量版本
+ ///
+ /// 可选参数集合,每个参数由key和value组成,key和value的格式请参见optional方法的注释
+ /// 本客户端,便于链式调用
+ public Client BatchOptional(Dictionary optionalArgs)
+ {
+ foreach (var pair in optionalArgs)
+ {
+ _kernel.InjectBizParam(pair.Key, pair.Value);
+ }
+ return this;
+ }
+ }
+}
\ No newline at end of file
diff --git a/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Base/Image/Models/AlipayOfflineMaterialImageUploadResponse.cs b/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Base/Image/Models/AlipayOfflineMaterialImageUploadResponse.cs
new file mode 100644
index 00000000..c62512a1
--- /dev/null
+++ b/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Base/Image/Models/AlipayOfflineMaterialImageUploadResponse.cs
@@ -0,0 +1,45 @@
+// This file is auto-generated, don't edit it. Thanks.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+using Tea;
+
+namespace Alipay.EasySDK.Base.Image.Models
+{
+ public class AlipayOfflineMaterialImageUploadResponse : TeaModel {
+ ///
+ /// 响应原始字符串
+ ///
+ [NameInMap("http_body")]
+ [Validation(Required=true)]
+ public string HttpBody { get; set; }
+
+ [NameInMap("code")]
+ [Validation(Required=true)]
+ public string Code { get; set; }
+
+ [NameInMap("msg")]
+ [Validation(Required=true)]
+ public string Msg { get; set; }
+
+ [NameInMap("sub_code")]
+ [Validation(Required=true)]
+ public string SubCode { get; set; }
+
+ [NameInMap("sub_msg")]
+ [Validation(Required=true)]
+ public string SubMsg { get; set; }
+
+ [NameInMap("image_id")]
+ [Validation(Required=true)]
+ public string ImageId { get; set; }
+
+ [NameInMap("image_url")]
+ [Validation(Required=true)]
+ public string ImageUrl { get; set; }
+
+ }
+
+}
diff --git a/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Base/OAuth/Client.cs b/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Base/OAuth/Client.cs
new file mode 100644
index 00000000..fc0973f5
--- /dev/null
+++ b/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Base/OAuth/Client.cs
@@ -0,0 +1,528 @@
+// This file is auto-generated, don't edit it. Thanks.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+
+using Tea;
+using Tea.Utils;
+
+using Alipay.EasySDK.Base.OAuth.Models;
+
+namespace Alipay.EasySDK.Base.OAuth
+{
+ public class Client
+ {
+ protected Alipay.EasySDK.Kernel.Client _kernel;
+
+ public Client(Alipay.EasySDK.Kernel.Client kernel)
+ {
+ this._kernel = kernel;
+ }
+
+ public AlipaySystemOauthTokenResponse GetToken(string code)
+ {
+ Dictionary runtime_ = new Dictionary
+ {
+ {"ignoreSSL", this._kernel.GetConfig("ignoreSSL")},
+ {"httpProxy", this._kernel.GetConfig("httpProxy")},
+ {"connectTimeout", 15000},
+ {"readTimeout", 15000},
+ {"retry", new Dictionary
+ {
+ {"maxAttempts", 0},
+ }},
+ };
+
+ TeaRequest _lastRequest = null;
+ Exception _lastException = null;
+ long _now = System.DateTime.Now.Millisecond;
+ int _retryTimes = 0;
+ while (TeaCore.AllowRetry((IDictionary) runtime_["retry"], _retryTimes, _now))
+ {
+ if (_retryTimes > 0)
+ {
+ int backoffTime = TeaCore.GetBackoffTime((IDictionary)runtime_["backoff"], _retryTimes);
+ if (backoffTime > 0)
+ {
+ TeaCore.Sleep(backoffTime);
+ }
+ }
+ _retryTimes = _retryTimes + 1;
+ try
+ {
+ TeaRequest request_ = new TeaRequest();
+ Dictionary systemParams = new Dictionary
+ {
+ {"method", "alipay.system.oauth.token"},
+ {"app_id", this._kernel.GetConfig("appId")},
+ {"timestamp", this._kernel.GetTimestamp()},
+ {"format", "json"},
+ {"version", "1.0"},
+ {"alipay_sdk", this._kernel.GetSdkVersion()},
+ {"charset", "UTF-8"},
+ {"sign_type", this._kernel.GetConfig("signType")},
+ {"app_cert_sn", this._kernel.GetMerchantCertSN()},
+ {"alipay_root_cert_sn", this._kernel.GetAlipayRootCertSN()},
+ };
+ Dictionary bizParams = new Dictionary(){};
+ Dictionary textParams = new Dictionary
+ {
+ {"grant_type", "authorization_code"},
+ {"code", code},
+ };
+ request_.Protocol = this._kernel.GetConfig("protocol");
+ request_.Method = "POST";
+ request_.Pathname = "/gateway.do";
+ request_.Headers = new Dictionary
+ {
+ {"host", this._kernel.GetConfig("gatewayHost")},
+ {"content-type", "application/x-www-form-urlencoded;charset=utf-8"},
+ };
+ request_.Query = this._kernel.SortMap(TeaConverter.merge
+ (
+ new Dictionary()
+ {
+ {"sign", this._kernel.Sign(systemParams, bizParams, textParams, this._kernel.GetConfig("merchantPrivateKey"))},
+ },
+ systemParams,
+ textParams
+ ));
+ request_.Body = TeaCore.BytesReadable(this._kernel.ToUrlEncodedRequestBody(bizParams));
+ _lastRequest = request_;
+ TeaResponse response_ = TeaCore.DoAction(request_, runtime_);
+
+ Dictionary respMap = this._kernel.ReadAsJson(response_, "alipay.system.oauth.token");
+ if (this._kernel.IsCertMode())
+ {
+ if (this._kernel.Verify(respMap, this._kernel.ExtractAlipayPublicKey(this._kernel.GetAlipayCertSN(respMap))))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ else
+ {
+ if (this._kernel.Verify(respMap, this._kernel.GetConfig("alipayPublicKey")))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ throw new TeaException(new Dictionary
+ {
+ {"message", "验签失败,请检查支付宝公钥设置是否正确。"},
+ });
+ }
+ catch (Exception e)
+ {
+ if (TeaCore.IsRetryable(e))
+ {
+ _lastException = e;
+ continue;
+ }
+ throw e;
+ }
+ }
+
+ throw new TeaUnretryableException(_lastRequest, _lastException);
+ }
+
+ public async Task GetTokenAsync(string code)
+ {
+ Dictionary runtime_ = new Dictionary
+ {
+ {"ignoreSSL", this._kernel.GetConfig("ignoreSSL")},
+ {"httpProxy", this._kernel.GetConfig("httpProxy")},
+ {"connectTimeout", 15000},
+ {"readTimeout", 15000},
+ {"retry", new Dictionary
+ {
+ {"maxAttempts", 0},
+ }},
+ };
+
+ TeaRequest _lastRequest = null;
+ Exception _lastException = null;
+ long _now = System.DateTime.Now.Millisecond;
+ int _retryTimes = 0;
+ while (TeaCore.AllowRetry((IDictionary) runtime_["retry"], _retryTimes, _now))
+ {
+ if (_retryTimes > 0)
+ {
+ int backoffTime = TeaCore.GetBackoffTime((IDictionary)runtime_["backoff"], _retryTimes);
+ if (backoffTime > 0)
+ {
+ TeaCore.Sleep(backoffTime);
+ }
+ }
+ _retryTimes = _retryTimes + 1;
+ try
+ {
+ TeaRequest request_ = new TeaRequest();
+ Dictionary systemParams = new Dictionary
+ {
+ {"method", "alipay.system.oauth.token"},
+ {"app_id", this._kernel.GetConfig("appId")},
+ {"timestamp", this._kernel.GetTimestamp()},
+ {"format", "json"},
+ {"version", "1.0"},
+ {"alipay_sdk", this._kernel.GetSdkVersion()},
+ {"charset", "UTF-8"},
+ {"sign_type", this._kernel.GetConfig("signType")},
+ {"app_cert_sn", this._kernel.GetMerchantCertSN()},
+ {"alipay_root_cert_sn", this._kernel.GetAlipayRootCertSN()},
+ };
+ Dictionary bizParams = new Dictionary(){};
+ Dictionary textParams = new Dictionary
+ {
+ {"grant_type", "authorization_code"},
+ {"code", code},
+ };
+ request_.Protocol = this._kernel.GetConfig("protocol");
+ request_.Method = "POST";
+ request_.Pathname = "/gateway.do";
+ request_.Headers = new Dictionary
+ {
+ {"host", this._kernel.GetConfig("gatewayHost")},
+ {"content-type", "application/x-www-form-urlencoded;charset=utf-8"},
+ };
+ request_.Query = this._kernel.SortMap(TeaConverter.merge
+ (
+ new Dictionary()
+ {
+ {"sign", this._kernel.Sign(systemParams, bizParams, textParams, this._kernel.GetConfig("merchantPrivateKey"))},
+ },
+ systemParams,
+ textParams
+ ));
+ request_.Body = TeaCore.BytesReadable(this._kernel.ToUrlEncodedRequestBody(bizParams));
+ _lastRequest = request_;
+ TeaResponse response_ = await TeaCore.DoActionAsync(request_, runtime_);
+
+ Dictionary respMap = await this._kernel.ReadAsJsonAsync(response_, "alipay.system.oauth.token");
+ if (this._kernel.IsCertMode())
+ {
+ if (this._kernel.Verify(respMap, this._kernel.ExtractAlipayPublicKey(this._kernel.GetAlipayCertSN(respMap))))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ else
+ {
+ if (this._kernel.Verify(respMap, this._kernel.GetConfig("alipayPublicKey")))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ throw new TeaException(new Dictionary
+ {
+ {"message", "验签失败,请检查支付宝公钥设置是否正确。"},
+ });
+ }
+ catch (Exception e)
+ {
+ if (TeaCore.IsRetryable(e))
+ {
+ _lastException = e;
+ continue;
+ }
+ throw e;
+ }
+ }
+
+ throw new TeaUnretryableException(_lastRequest, _lastException);
+ }
+
+ public AlipaySystemOauthTokenResponse RefreshToken(string refreshToken)
+ {
+ Dictionary runtime_ = new Dictionary
+ {
+ {"ignoreSSL", this._kernel.GetConfig("ignoreSSL")},
+ {"httpProxy", this._kernel.GetConfig("httpProxy")},
+ {"connectTimeout", 15000},
+ {"readTimeout", 15000},
+ {"retry", new Dictionary
+ {
+ {"maxAttempts", 0},
+ }},
+ };
+
+ TeaRequest _lastRequest = null;
+ Exception _lastException = null;
+ long _now = System.DateTime.Now.Millisecond;
+ int _retryTimes = 0;
+ while (TeaCore.AllowRetry((IDictionary) runtime_["retry"], _retryTimes, _now))
+ {
+ if (_retryTimes > 0)
+ {
+ int backoffTime = TeaCore.GetBackoffTime((IDictionary)runtime_["backoff"], _retryTimes);
+ if (backoffTime > 0)
+ {
+ TeaCore.Sleep(backoffTime);
+ }
+ }
+ _retryTimes = _retryTimes + 1;
+ try
+ {
+ TeaRequest request_ = new TeaRequest();
+ Dictionary systemParams = new Dictionary
+ {
+ {"method", "alipay.system.oauth.token"},
+ {"app_id", this._kernel.GetConfig("appId")},
+ {"timestamp", this._kernel.GetTimestamp()},
+ {"format", "json"},
+ {"version", "1.0"},
+ {"alipay_sdk", this._kernel.GetSdkVersion()},
+ {"charset", "UTF-8"},
+ {"sign_type", this._kernel.GetConfig("signType")},
+ {"app_cert_sn", this._kernel.GetMerchantCertSN()},
+ {"alipay_root_cert_sn", this._kernel.GetAlipayRootCertSN()},
+ };
+ Dictionary bizParams = new Dictionary(){};
+ Dictionary textParams = new Dictionary
+ {
+ {"grant_type", "refresh_token"},
+ {"refresh_token", refreshToken},
+ };
+ request_.Protocol = this._kernel.GetConfig("protocol");
+ request_.Method = "POST";
+ request_.Pathname = "/gateway.do";
+ request_.Headers = new Dictionary
+ {
+ {"host", this._kernel.GetConfig("gatewayHost")},
+ {"content-type", "application/x-www-form-urlencoded;charset=utf-8"},
+ };
+ request_.Query = this._kernel.SortMap(TeaConverter.merge
+ (
+ new Dictionary()
+ {
+ {"sign", this._kernel.Sign(systemParams, bizParams, textParams, this._kernel.GetConfig("merchantPrivateKey"))},
+ },
+ systemParams,
+ textParams
+ ));
+ request_.Body = TeaCore.BytesReadable(this._kernel.ToUrlEncodedRequestBody(bizParams));
+ _lastRequest = request_;
+ TeaResponse response_ = TeaCore.DoAction(request_, runtime_);
+
+ Dictionary respMap = this._kernel.ReadAsJson(response_, "alipay.system.oauth.token");
+ if (this._kernel.IsCertMode())
+ {
+ if (this._kernel.Verify(respMap, this._kernel.ExtractAlipayPublicKey(this._kernel.GetAlipayCertSN(respMap))))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ else
+ {
+ if (this._kernel.Verify(respMap, this._kernel.GetConfig("alipayPublicKey")))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ throw new TeaException(new Dictionary
+ {
+ {"message", "验签失败,请检查支付宝公钥设置是否正确。"},
+ });
+ }
+ catch (Exception e)
+ {
+ if (TeaCore.IsRetryable(e))
+ {
+ _lastException = e;
+ continue;
+ }
+ throw e;
+ }
+ }
+
+ throw new TeaUnretryableException(_lastRequest, _lastException);
+ }
+
+ public async Task RefreshTokenAsync(string refreshToken)
+ {
+ Dictionary runtime_ = new Dictionary
+ {
+ {"ignoreSSL", this._kernel.GetConfig("ignoreSSL")},
+ {"httpProxy", this._kernel.GetConfig("httpProxy")},
+ {"connectTimeout", 15000},
+ {"readTimeout", 15000},
+ {"retry", new Dictionary
+ {
+ {"maxAttempts", 0},
+ }},
+ };
+
+ TeaRequest _lastRequest = null;
+ Exception _lastException = null;
+ long _now = System.DateTime.Now.Millisecond;
+ int _retryTimes = 0;
+ while (TeaCore.AllowRetry((IDictionary) runtime_["retry"], _retryTimes, _now))
+ {
+ if (_retryTimes > 0)
+ {
+ int backoffTime = TeaCore.GetBackoffTime((IDictionary)runtime_["backoff"], _retryTimes);
+ if (backoffTime > 0)
+ {
+ TeaCore.Sleep(backoffTime);
+ }
+ }
+ _retryTimes = _retryTimes + 1;
+ try
+ {
+ TeaRequest request_ = new TeaRequest();
+ Dictionary systemParams = new Dictionary
+ {
+ {"method", "alipay.system.oauth.token"},
+ {"app_id", this._kernel.GetConfig("appId")},
+ {"timestamp", this._kernel.GetTimestamp()},
+ {"format", "json"},
+ {"version", "1.0"},
+ {"alipay_sdk", this._kernel.GetSdkVersion()},
+ {"charset", "UTF-8"},
+ {"sign_type", this._kernel.GetConfig("signType")},
+ {"app_cert_sn", this._kernel.GetMerchantCertSN()},
+ {"alipay_root_cert_sn", this._kernel.GetAlipayRootCertSN()},
+ };
+ Dictionary bizParams = new Dictionary(){};
+ Dictionary textParams = new Dictionary
+ {
+ {"grant_type", "refresh_token"},
+ {"refresh_token", refreshToken},
+ };
+ request_.Protocol = this._kernel.GetConfig("protocol");
+ request_.Method = "POST";
+ request_.Pathname = "/gateway.do";
+ request_.Headers = new Dictionary
+ {
+ {"host", this._kernel.GetConfig("gatewayHost")},
+ {"content-type", "application/x-www-form-urlencoded;charset=utf-8"},
+ };
+ request_.Query = this._kernel.SortMap(TeaConverter.merge
+ (
+ new Dictionary()
+ {
+ {"sign", this._kernel.Sign(systemParams, bizParams, textParams, this._kernel.GetConfig("merchantPrivateKey"))},
+ },
+ systemParams,
+ textParams
+ ));
+ request_.Body = TeaCore.BytesReadable(this._kernel.ToUrlEncodedRequestBody(bizParams));
+ _lastRequest = request_;
+ TeaResponse response_ = await TeaCore.DoActionAsync(request_, runtime_);
+
+ Dictionary respMap = await this._kernel.ReadAsJsonAsync(response_, "alipay.system.oauth.token");
+ if (this._kernel.IsCertMode())
+ {
+ if (this._kernel.Verify(respMap, this._kernel.ExtractAlipayPublicKey(this._kernel.GetAlipayCertSN(respMap))))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ else
+ {
+ if (this._kernel.Verify(respMap, this._kernel.GetConfig("alipayPublicKey")))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ throw new TeaException(new Dictionary
+ {
+ {"message", "验签失败,请检查支付宝公钥设置是否正确。"},
+ });
+ }
+ catch (Exception e)
+ {
+ if (TeaCore.IsRetryable(e))
+ {
+ _lastException = e;
+ continue;
+ }
+ throw e;
+ }
+ }
+
+ throw new TeaUnretryableException(_lastRequest, _lastException);
+ }
+
+
+ ///
+ /// ISV代商户代用,指定appAuthToken
+ ///
+ /// 代调用token
+ /// 本客户端,便于链式调用
+ public Client Agent(string appAuthToken)
+ {
+ _kernel.InjectTextParam("app_auth_token", appAuthToken);
+ return this;
+ }
+
+ ///
+ /// 用户授权调用,指定authToken
+ ///
+ /// 用户授权token
+ /// 本客户端,便于链式调用
+ public Client Auth(string authToken)
+ {
+ _kernel.InjectTextParam("auth_token", authToken);
+ return this;
+ }
+
+ ///
+ /// 设置异步通知回调地址,此处设置将在本调用中覆盖Config中的全局配置
+ ///
+ /// 异步通知回调地址,例如:https://www.test.com/callback
+ /// 本客户端,便于链式调用
+ public Client AsyncNotify(string url)
+ {
+ _kernel.InjectTextParam("notify_url", url);
+ return this;
+ }
+
+ ///
+ /// 将本次调用强制路由到后端系统的测试地址上,常用于线下环境内外联调,沙箱与线上环境设置无效
+ ///
+ /// 后端系统测试地址
+ /// 本客户端,便于链式调用
+ public Client Route(string testUrl)
+ {
+ _kernel.InjectTextParam("ws_service_url", testUrl);
+ return this;
+ }
+
+ ///
+ /// 设置API入参中没有的其他可选业务请求参数(biz_content下的字段)
+ ///
+ /// 业务请求参数名称(biz_content下的字段名,比如timeout_express)
+ ///
+ /// 业务请求参数的值,一个可以序列化成JSON的对象
+ /// 如果该字段是一个字符串类型(String、Price、Date在SDK中都是字符串),请使用string储存
+ /// 如果该字段是一个数值型类型(比如:Number),请使用long储存
+ /// 如果该字段是一个复杂类型,请使用嵌套的Dictionary指定各下级字段的值
+ /// 如果该字段是一个数组,请使用List储存各个值
+ /// 对于更复杂的情况,也支持Dictionary和List的各种组合嵌套,比如参数是值是个List,List中的每种类型是一个复杂对象
+ ///
+ /// 本客户端,便于链式调用
+ public Client Optional(string key, object value)
+ {
+ _kernel.InjectBizParam(key, value);
+ return this;
+ }
+
+ ///
+ /// 批量设置API入参中没有的其他可选业务请求参数(biz_content下的字段)
+ /// optional方法的批量版本
+ ///
+ /// 可选参数集合,每个参数由key和value组成,key和value的格式请参见optional方法的注释
+ /// 本客户端,便于链式调用
+ public Client BatchOptional(Dictionary optionalArgs)
+ {
+ foreach (var pair in optionalArgs)
+ {
+ _kernel.InjectBizParam(pair.Key, pair.Value);
+ }
+ return this;
+ }
+ }
+}
\ No newline at end of file
diff --git a/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Base/OAuth/Models/AlipaySystemOauthTokenResponse.cs b/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Base/OAuth/Models/AlipaySystemOauthTokenResponse.cs
new file mode 100644
index 00000000..29aa6bb5
--- /dev/null
+++ b/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Base/OAuth/Models/AlipaySystemOauthTokenResponse.cs
@@ -0,0 +1,57 @@
+// This file is auto-generated, don't edit it. Thanks.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+using Tea;
+
+namespace Alipay.EasySDK.Base.OAuth.Models
+{
+ public class AlipaySystemOauthTokenResponse : TeaModel {
+ ///
+ /// 响应原始字符串
+ ///
+ [NameInMap("http_body")]
+ [Validation(Required=true)]
+ public string HttpBody { get; set; }
+
+ [NameInMap("code")]
+ [Validation(Required=true)]
+ public string Code { get; set; }
+
+ [NameInMap("msg")]
+ [Validation(Required=true)]
+ public string Msg { get; set; }
+
+ [NameInMap("sub_code")]
+ [Validation(Required=true)]
+ public string SubCode { get; set; }
+
+ [NameInMap("sub_msg")]
+ [Validation(Required=true)]
+ public string SubMsg { get; set; }
+
+ [NameInMap("user_id")]
+ [Validation(Required=true)]
+ public string UserId { get; set; }
+
+ [NameInMap("access_token")]
+ [Validation(Required=true)]
+ public string AccessToken { get; set; }
+
+ [NameInMap("expires_in")]
+ [Validation(Required=true)]
+ public long ExpiresIn { get; set; }
+
+ [NameInMap("refresh_token")]
+ [Validation(Required=true)]
+ public string RefreshToken { get; set; }
+
+ [NameInMap("re_expires_in")]
+ [Validation(Required=true)]
+ public long ReExpiresIn { get; set; }
+
+ }
+
+}
diff --git a/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Base/Qrcode/Client.cs b/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Base/Qrcode/Client.cs
new file mode 100644
index 00000000..459dadd9
--- /dev/null
+++ b/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Base/Qrcode/Client.cs
@@ -0,0 +1,318 @@
+// This file is auto-generated, don't edit it. Thanks.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+
+using Tea;
+using Tea.Utils;
+
+using Alipay.EasySDK.Base.Qrcode.Models;
+
+namespace Alipay.EasySDK.Base.Qrcode
+{
+ public class Client
+ {
+ protected Alipay.EasySDK.Kernel.Client _kernel;
+
+ public Client(Alipay.EasySDK.Kernel.Client kernel)
+ {
+ this._kernel = kernel;
+ }
+
+ public AlipayOpenAppQrcodeCreateResponse Create(string urlParam, string queryParam, string describe)
+ {
+ Dictionary runtime_ = new Dictionary
+ {
+ {"ignoreSSL", this._kernel.GetConfig("ignoreSSL")},
+ {"httpProxy", this._kernel.GetConfig("httpProxy")},
+ {"connectTimeout", 15000},
+ {"readTimeout", 15000},
+ {"retry", new Dictionary
+ {
+ {"maxAttempts", 0},
+ }},
+ };
+
+ TeaRequest _lastRequest = null;
+ Exception _lastException = null;
+ long _now = System.DateTime.Now.Millisecond;
+ int _retryTimes = 0;
+ while (TeaCore.AllowRetry((IDictionary) runtime_["retry"], _retryTimes, _now))
+ {
+ if (_retryTimes > 0)
+ {
+ int backoffTime = TeaCore.GetBackoffTime((IDictionary)runtime_["backoff"], _retryTimes);
+ if (backoffTime > 0)
+ {
+ TeaCore.Sleep(backoffTime);
+ }
+ }
+ _retryTimes = _retryTimes + 1;
+ try
+ {
+ TeaRequest request_ = new TeaRequest();
+ Dictionary systemParams = new Dictionary
+ {
+ {"method", "alipay.open.app.qrcode.create"},
+ {"app_id", this._kernel.GetConfig("appId")},
+ {"timestamp", this._kernel.GetTimestamp()},
+ {"format", "json"},
+ {"version", "1.0"},
+ {"alipay_sdk", this._kernel.GetSdkVersion()},
+ {"charset", "UTF-8"},
+ {"sign_type", this._kernel.GetConfig("signType")},
+ {"app_cert_sn", this._kernel.GetMerchantCertSN()},
+ {"alipay_root_cert_sn", this._kernel.GetAlipayRootCertSN()},
+ };
+ Dictionary bizParams = new Dictionary
+ {
+ {"url_param", urlParam},
+ {"query_param", queryParam},
+ {"describe", describe},
+ };
+ Dictionary textParams = new Dictionary(){};
+ request_.Protocol = this._kernel.GetConfig("protocol");
+ request_.Method = "POST";
+ request_.Pathname = "/gateway.do";
+ request_.Headers = new Dictionary
+ {
+ {"host", this._kernel.GetConfig("gatewayHost")},
+ {"content-type", "application/x-www-form-urlencoded;charset=utf-8"},
+ };
+ request_.Query = this._kernel.SortMap(TeaConverter.merge
+ (
+ new Dictionary()
+ {
+ {"sign", this._kernel.Sign(systemParams, bizParams, textParams, this._kernel.GetConfig("merchantPrivateKey"))},
+ },
+ systemParams,
+ textParams
+ ));
+ request_.Body = TeaCore.BytesReadable(this._kernel.ToUrlEncodedRequestBody(bizParams));
+ _lastRequest = request_;
+ TeaResponse response_ = TeaCore.DoAction(request_, runtime_);
+
+ Dictionary respMap = this._kernel.ReadAsJson(response_, "alipay.open.app.qrcode.create");
+ if (this._kernel.IsCertMode())
+ {
+ if (this._kernel.Verify(respMap, this._kernel.ExtractAlipayPublicKey(this._kernel.GetAlipayCertSN(respMap))))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ else
+ {
+ if (this._kernel.Verify(respMap, this._kernel.GetConfig("alipayPublicKey")))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ throw new TeaException(new Dictionary
+ {
+ {"message", "验签失败,请检查支付宝公钥设置是否正确。"},
+ });
+ }
+ catch (Exception e)
+ {
+ if (TeaCore.IsRetryable(e))
+ {
+ _lastException = e;
+ continue;
+ }
+ throw e;
+ }
+ }
+
+ throw new TeaUnretryableException(_lastRequest, _lastException);
+ }
+
+ public async Task CreateAsync(string urlParam, string queryParam, string describe)
+ {
+ Dictionary runtime_ = new Dictionary
+ {
+ {"ignoreSSL", this._kernel.GetConfig("ignoreSSL")},
+ {"httpProxy", this._kernel.GetConfig("httpProxy")},
+ {"connectTimeout", 15000},
+ {"readTimeout", 15000},
+ {"retry", new Dictionary
+ {
+ {"maxAttempts", 0},
+ }},
+ };
+
+ TeaRequest _lastRequest = null;
+ Exception _lastException = null;
+ long _now = System.DateTime.Now.Millisecond;
+ int _retryTimes = 0;
+ while (TeaCore.AllowRetry((IDictionary) runtime_["retry"], _retryTimes, _now))
+ {
+ if (_retryTimes > 0)
+ {
+ int backoffTime = TeaCore.GetBackoffTime((IDictionary)runtime_["backoff"], _retryTimes);
+ if (backoffTime > 0)
+ {
+ TeaCore.Sleep(backoffTime);
+ }
+ }
+ _retryTimes = _retryTimes + 1;
+ try
+ {
+ TeaRequest request_ = new TeaRequest();
+ Dictionary systemParams = new Dictionary
+ {
+ {"method", "alipay.open.app.qrcode.create"},
+ {"app_id", this._kernel.GetConfig("appId")},
+ {"timestamp", this._kernel.GetTimestamp()},
+ {"format", "json"},
+ {"version", "1.0"},
+ {"alipay_sdk", this._kernel.GetSdkVersion()},
+ {"charset", "UTF-8"},
+ {"sign_type", this._kernel.GetConfig("signType")},
+ {"app_cert_sn", this._kernel.GetMerchantCertSN()},
+ {"alipay_root_cert_sn", this._kernel.GetAlipayRootCertSN()},
+ };
+ Dictionary bizParams = new Dictionary
+ {
+ {"url_param", urlParam},
+ {"query_param", queryParam},
+ {"describe", describe},
+ };
+ Dictionary textParams = new Dictionary(){};
+ request_.Protocol = this._kernel.GetConfig("protocol");
+ request_.Method = "POST";
+ request_.Pathname = "/gateway.do";
+ request_.Headers = new Dictionary
+ {
+ {"host", this._kernel.GetConfig("gatewayHost")},
+ {"content-type", "application/x-www-form-urlencoded;charset=utf-8"},
+ };
+ request_.Query = this._kernel.SortMap(TeaConverter.merge
+ (
+ new Dictionary()
+ {
+ {"sign", this._kernel.Sign(systemParams, bizParams, textParams, this._kernel.GetConfig("merchantPrivateKey"))},
+ },
+ systemParams,
+ textParams
+ ));
+ request_.Body = TeaCore.BytesReadable(this._kernel.ToUrlEncodedRequestBody(bizParams));
+ _lastRequest = request_;
+ TeaResponse response_ = await TeaCore.DoActionAsync(request_, runtime_);
+
+ Dictionary respMap = await this._kernel.ReadAsJsonAsync(response_, "alipay.open.app.qrcode.create");
+ if (this._kernel.IsCertMode())
+ {
+ if (this._kernel.Verify(respMap, this._kernel.ExtractAlipayPublicKey(this._kernel.GetAlipayCertSN(respMap))))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ else
+ {
+ if (this._kernel.Verify(respMap, this._kernel.GetConfig("alipayPublicKey")))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ throw new TeaException(new Dictionary
+ {
+ {"message", "验签失败,请检查支付宝公钥设置是否正确。"},
+ });
+ }
+ catch (Exception e)
+ {
+ if (TeaCore.IsRetryable(e))
+ {
+ _lastException = e;
+ continue;
+ }
+ throw e;
+ }
+ }
+
+ throw new TeaUnretryableException(_lastRequest, _lastException);
+ }
+
+
+ ///
+ /// ISV代商户代用,指定appAuthToken
+ ///
+ /// 代调用token
+ /// 本客户端,便于链式调用
+ public Client Agent(string appAuthToken)
+ {
+ _kernel.InjectTextParam("app_auth_token", appAuthToken);
+ return this;
+ }
+
+ ///
+ /// 用户授权调用,指定authToken
+ ///
+ /// 用户授权token
+ /// 本客户端,便于链式调用
+ public Client Auth(string authToken)
+ {
+ _kernel.InjectTextParam("auth_token", authToken);
+ return this;
+ }
+
+ ///
+ /// 设置异步通知回调地址,此处设置将在本调用中覆盖Config中的全局配置
+ ///
+ /// 异步通知回调地址,例如:https://www.test.com/callback
+ /// 本客户端,便于链式调用
+ public Client AsyncNotify(string url)
+ {
+ _kernel.InjectTextParam("notify_url", url);
+ return this;
+ }
+
+ ///
+ /// 将本次调用强制路由到后端系统的测试地址上,常用于线下环境内外联调,沙箱与线上环境设置无效
+ ///
+ /// 后端系统测试地址
+ /// 本客户端,便于链式调用
+ public Client Route(string testUrl)
+ {
+ _kernel.InjectTextParam("ws_service_url", testUrl);
+ return this;
+ }
+
+ ///
+ /// 设置API入参中没有的其他可选业务请求参数(biz_content下的字段)
+ ///
+ /// 业务请求参数名称(biz_content下的字段名,比如timeout_express)
+ ///
+ /// 业务请求参数的值,一个可以序列化成JSON的对象
+ /// 如果该字段是一个字符串类型(String、Price、Date在SDK中都是字符串),请使用string储存
+ /// 如果该字段是一个数值型类型(比如:Number),请使用long储存
+ /// 如果该字段是一个复杂类型,请使用嵌套的Dictionary指定各下级字段的值
+ /// 如果该字段是一个数组,请使用List储存各个值
+ /// 对于更复杂的情况,也支持Dictionary和List的各种组合嵌套,比如参数是值是个List,List中的每种类型是一个复杂对象
+ ///
+ /// 本客户端,便于链式调用
+ public Client Optional(string key, object value)
+ {
+ _kernel.InjectBizParam(key, value);
+ return this;
+ }
+
+ ///
+ /// 批量设置API入参中没有的其他可选业务请求参数(biz_content下的字段)
+ /// optional方法的批量版本
+ ///
+ /// 可选参数集合,每个参数由key和value组成,key和value的格式请参见optional方法的注释
+ /// 本客户端,便于链式调用
+ public Client BatchOptional(Dictionary optionalArgs)
+ {
+ foreach (var pair in optionalArgs)
+ {
+ _kernel.InjectBizParam(pair.Key, pair.Value);
+ }
+ return this;
+ }
+ }
+}
\ No newline at end of file
diff --git a/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Base/Qrcode/Models/AlipayOpenAppQrcodeCreateResponse.cs b/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Base/Qrcode/Models/AlipayOpenAppQrcodeCreateResponse.cs
new file mode 100644
index 00000000..3302d3b5
--- /dev/null
+++ b/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Base/Qrcode/Models/AlipayOpenAppQrcodeCreateResponse.cs
@@ -0,0 +1,41 @@
+// This file is auto-generated, don't edit it. Thanks.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+using Tea;
+
+namespace Alipay.EasySDK.Base.Qrcode.Models
+{
+ public class AlipayOpenAppQrcodeCreateResponse : TeaModel {
+ ///
+ /// 响应原始字符串
+ ///
+ [NameInMap("http_body")]
+ [Validation(Required=true)]
+ public string HttpBody { get; set; }
+
+ [NameInMap("code")]
+ [Validation(Required=true)]
+ public string Code { get; set; }
+
+ [NameInMap("msg")]
+ [Validation(Required=true)]
+ public string Msg { get; set; }
+
+ [NameInMap("sub_code")]
+ [Validation(Required=true)]
+ public string SubCode { get; set; }
+
+ [NameInMap("sub_msg")]
+ [Validation(Required=true)]
+ public string SubMsg { get; set; }
+
+ [NameInMap("qr_code_url")]
+ [Validation(Required=true)]
+ public string QrCodeUrl { get; set; }
+
+ }
+
+}
diff --git a/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Base/Video/Client.cs b/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Base/Video/Client.cs
new file mode 100644
index 00000000..51ec320d
--- /dev/null
+++ b/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Base/Video/Client.cs
@@ -0,0 +1,324 @@
+// This file is auto-generated, don't edit it. Thanks.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+
+using Tea;
+using Tea.Utils;
+
+using Alipay.EasySDK.Base.Video.Models;
+
+namespace Alipay.EasySDK.Base.Video
+{
+ public class Client
+ {
+ protected Alipay.EasySDK.Kernel.Client _kernel;
+
+ public Client(Alipay.EasySDK.Kernel.Client kernel)
+ {
+ this._kernel = kernel;
+ }
+
+ public AlipayOfflineMaterialImageUploadResponse Upload(string videoName, string videoFilePath)
+ {
+ Dictionary runtime_ = new Dictionary
+ {
+ {"ignoreSSL", this._kernel.GetConfig("ignoreSSL")},
+ {"httpProxy", this._kernel.GetConfig("httpProxy")},
+ {"connectTimeout", 100000},
+ {"readTimeout", 100000},
+ {"retry", new Dictionary
+ {
+ {"maxAttempts", 0},
+ }},
+ };
+
+ TeaRequest _lastRequest = null;
+ Exception _lastException = null;
+ long _now = System.DateTime.Now.Millisecond;
+ int _retryTimes = 0;
+ while (TeaCore.AllowRetry((IDictionary) runtime_["retry"], _retryTimes, _now))
+ {
+ if (_retryTimes > 0)
+ {
+ int backoffTime = TeaCore.GetBackoffTime((IDictionary)runtime_["backoff"], _retryTimes);
+ if (backoffTime > 0)
+ {
+ TeaCore.Sleep(backoffTime);
+ }
+ }
+ _retryTimes = _retryTimes + 1;
+ try
+ {
+ TeaRequest request_ = new TeaRequest();
+ Dictionary systemParams = new Dictionary
+ {
+ {"method", "alipay.offline.material.image.upload"},
+ {"app_id", this._kernel.GetConfig("appId")},
+ {"timestamp", this._kernel.GetTimestamp()},
+ {"format", "json"},
+ {"version", "1.0"},
+ {"alipay_sdk", this._kernel.GetSdkVersion()},
+ {"charset", "UTF-8"},
+ {"sign_type", this._kernel.GetConfig("signType")},
+ {"app_cert_sn", this._kernel.GetMerchantCertSN()},
+ {"alipay_root_cert_sn", this._kernel.GetAlipayRootCertSN()},
+ };
+ Dictionary bizParams = new Dictionary(){};
+ Dictionary textParams = new Dictionary
+ {
+ {"image_type", "mp4"},
+ {"image_name", videoName},
+ };
+ Dictionary fileParams = new Dictionary
+ {
+ {"image_content", videoFilePath},
+ };
+ string boundary = this._kernel.GetRandomBoundary();
+ request_.Protocol = this._kernel.GetConfig("protocol");
+ request_.Method = "POST";
+ request_.Pathname = "/gateway.do";
+ request_.Headers = new Dictionary
+ {
+ {"host", this._kernel.GetConfig("gatewayHost")},
+ {"content-type", this._kernel.ConcatStr("multipart/form-data;charset=utf-8;boundary=", boundary)},
+ };
+ request_.Query = this._kernel.SortMap(TeaConverter.merge
+ (
+ new Dictionary()
+ {
+ {"sign", this._kernel.Sign(systemParams, bizParams, textParams, this._kernel.GetConfig("merchantPrivateKey"))},
+ },
+ systemParams
+ ));
+ request_.Body = this._kernel.ToMultipartRequestBody(textParams, fileParams, boundary);
+ _lastRequest = request_;
+ TeaResponse response_ = TeaCore.DoAction(request_, runtime_);
+
+ Dictionary respMap = this._kernel.ReadAsJson(response_, "alipay.offline.material.image.upload");
+ if (this._kernel.IsCertMode())
+ {
+ if (this._kernel.Verify(respMap, this._kernel.ExtractAlipayPublicKey(this._kernel.GetAlipayCertSN(respMap))))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ else
+ {
+ if (this._kernel.Verify(respMap, this._kernel.GetConfig("alipayPublicKey")))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ throw new TeaException(new Dictionary
+ {
+ {"message", "验签失败,请检查支付宝公钥设置是否正确。"},
+ });
+ }
+ catch (Exception e)
+ {
+ if (TeaCore.IsRetryable(e))
+ {
+ _lastException = e;
+ continue;
+ }
+ throw e;
+ }
+ }
+
+ throw new TeaUnretryableException(_lastRequest, _lastException);
+ }
+
+ public async Task UploadAsync(string videoName, string videoFilePath)
+ {
+ Dictionary runtime_ = new Dictionary
+ {
+ {"ignoreSSL", this._kernel.GetConfig("ignoreSSL")},
+ {"httpProxy", this._kernel.GetConfig("httpProxy")},
+ {"connectTimeout", 100000},
+ {"readTimeout", 100000},
+ {"retry", new Dictionary
+ {
+ {"maxAttempts", 0},
+ }},
+ };
+
+ TeaRequest _lastRequest = null;
+ Exception _lastException = null;
+ long _now = System.DateTime.Now.Millisecond;
+ int _retryTimes = 0;
+ while (TeaCore.AllowRetry((IDictionary) runtime_["retry"], _retryTimes, _now))
+ {
+ if (_retryTimes > 0)
+ {
+ int backoffTime = TeaCore.GetBackoffTime((IDictionary)runtime_["backoff"], _retryTimes);
+ if (backoffTime > 0)
+ {
+ TeaCore.Sleep(backoffTime);
+ }
+ }
+ _retryTimes = _retryTimes + 1;
+ try
+ {
+ TeaRequest request_ = new TeaRequest();
+ Dictionary systemParams = new Dictionary
+ {
+ {"method", "alipay.offline.material.image.upload"},
+ {"app_id", this._kernel.GetConfig("appId")},
+ {"timestamp", this._kernel.GetTimestamp()},
+ {"format", "json"},
+ {"version", "1.0"},
+ {"alipay_sdk", this._kernel.GetSdkVersion()},
+ {"charset", "UTF-8"},
+ {"sign_type", this._kernel.GetConfig("signType")},
+ {"app_cert_sn", this._kernel.GetMerchantCertSN()},
+ {"alipay_root_cert_sn", this._kernel.GetAlipayRootCertSN()},
+ };
+ Dictionary bizParams = new Dictionary(){};
+ Dictionary textParams = new Dictionary
+ {
+ {"image_type", "mp4"},
+ {"image_name", videoName},
+ };
+ Dictionary fileParams = new Dictionary
+ {
+ {"image_content", videoFilePath},
+ };
+ string boundary = this._kernel.GetRandomBoundary();
+ request_.Protocol = this._kernel.GetConfig("protocol");
+ request_.Method = "POST";
+ request_.Pathname = "/gateway.do";
+ request_.Headers = new Dictionary
+ {
+ {"host", this._kernel.GetConfig("gatewayHost")},
+ {"content-type", this._kernel.ConcatStr("multipart/form-data;charset=utf-8;boundary=", boundary)},
+ };
+ request_.Query = this._kernel.SortMap(TeaConverter.merge
+ (
+ new Dictionary()
+ {
+ {"sign", this._kernel.Sign(systemParams, bizParams, textParams, this._kernel.GetConfig("merchantPrivateKey"))},
+ },
+ systemParams
+ ));
+ request_.Body = this._kernel.ToMultipartRequestBody(textParams, fileParams, boundary);
+ _lastRequest = request_;
+ TeaResponse response_ = await TeaCore.DoActionAsync(request_, runtime_);
+
+ Dictionary respMap = await this._kernel.ReadAsJsonAsync(response_, "alipay.offline.material.image.upload");
+ if (this._kernel.IsCertMode())
+ {
+ if (this._kernel.Verify(respMap, this._kernel.ExtractAlipayPublicKey(this._kernel.GetAlipayCertSN(respMap))))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ else
+ {
+ if (this._kernel.Verify(respMap, this._kernel.GetConfig("alipayPublicKey")))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ throw new TeaException(new Dictionary
+ {
+ {"message", "验签失败,请检查支付宝公钥设置是否正确。"},
+ });
+ }
+ catch (Exception e)
+ {
+ if (TeaCore.IsRetryable(e))
+ {
+ _lastException = e;
+ continue;
+ }
+ throw e;
+ }
+ }
+
+ throw new TeaUnretryableException(_lastRequest, _lastException);
+ }
+
+
+ ///
+ /// ISV代商户代用,指定appAuthToken
+ ///
+ /// 代调用token
+ /// 本客户端,便于链式调用
+ public Client Agent(string appAuthToken)
+ {
+ _kernel.InjectTextParam("app_auth_token", appAuthToken);
+ return this;
+ }
+
+ ///
+ /// 用户授权调用,指定authToken
+ ///
+ /// 用户授权token
+ /// 本客户端,便于链式调用
+ public Client Auth(string authToken)
+ {
+ _kernel.InjectTextParam("auth_token", authToken);
+ return this;
+ }
+
+ ///
+ /// 设置异步通知回调地址,此处设置将在本调用中覆盖Config中的全局配置
+ ///
+ /// 异步通知回调地址,例如:https://www.test.com/callback
+ /// 本客户端,便于链式调用
+ public Client AsyncNotify(string url)
+ {
+ _kernel.InjectTextParam("notify_url", url);
+ return this;
+ }
+
+ ///
+ /// 将本次调用强制路由到后端系统的测试地址上,常用于线下环境内外联调,沙箱与线上环境设置无效
+ ///
+ /// 后端系统测试地址
+ /// 本客户端,便于链式调用
+ public Client Route(string testUrl)
+ {
+ _kernel.InjectTextParam("ws_service_url", testUrl);
+ return this;
+ }
+
+ ///
+ /// 设置API入参中没有的其他可选业务请求参数(biz_content下的字段)
+ ///
+ /// 业务请求参数名称(biz_content下的字段名,比如timeout_express)
+ ///
+ /// 业务请求参数的值,一个可以序列化成JSON的对象
+ /// 如果该字段是一个字符串类型(String、Price、Date在SDK中都是字符串),请使用string储存
+ /// 如果该字段是一个数值型类型(比如:Number),请使用long储存
+ /// 如果该字段是一个复杂类型,请使用嵌套的Dictionary指定各下级字段的值
+ /// 如果该字段是一个数组,请使用List储存各个值
+ /// 对于更复杂的情况,也支持Dictionary和List的各种组合嵌套,比如参数是值是个List,List中的每种类型是一个复杂对象
+ ///
+ /// 本客户端,便于链式调用
+ public Client Optional(string key, object value)
+ {
+ _kernel.InjectBizParam(key, value);
+ return this;
+ }
+
+ ///
+ /// 批量设置API入参中没有的其他可选业务请求参数(biz_content下的字段)
+ /// optional方法的批量版本
+ ///
+ /// 可选参数集合,每个参数由key和value组成,key和value的格式请参见optional方法的注释
+ /// 本客户端,便于链式调用
+ public Client BatchOptional(Dictionary optionalArgs)
+ {
+ foreach (var pair in optionalArgs)
+ {
+ _kernel.InjectBizParam(pair.Key, pair.Value);
+ }
+ return this;
+ }
+ }
+}
\ No newline at end of file
diff --git a/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Base/Video/Models/AlipayOfflineMaterialImageUploadResponse.cs b/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Base/Video/Models/AlipayOfflineMaterialImageUploadResponse.cs
new file mode 100644
index 00000000..1ccfcac1
--- /dev/null
+++ b/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Base/Video/Models/AlipayOfflineMaterialImageUploadResponse.cs
@@ -0,0 +1,45 @@
+// This file is auto-generated, don't edit it. Thanks.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+using Tea;
+
+namespace Alipay.EasySDK.Base.Video.Models
+{
+ public class AlipayOfflineMaterialImageUploadResponse : TeaModel {
+ ///
+ /// 响应原始字符串
+ ///
+ [NameInMap("http_body")]
+ [Validation(Required=true)]
+ public string HttpBody { get; set; }
+
+ [NameInMap("code")]
+ [Validation(Required=true)]
+ public string Code { get; set; }
+
+ [NameInMap("msg")]
+ [Validation(Required=true)]
+ public string Msg { get; set; }
+
+ [NameInMap("sub_code")]
+ [Validation(Required=true)]
+ public string SubCode { get; set; }
+
+ [NameInMap("sub_msg")]
+ [Validation(Required=true)]
+ public string SubMsg { get; set; }
+
+ [NameInMap("image_id")]
+ [Validation(Required=true)]
+ public string ImageId { get; set; }
+
+ [NameInMap("image_url")]
+ [Validation(Required=true)]
+ public string ImageUrl { get; set; }
+
+ }
+
+}
diff --git a/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Factory/Factory.cs b/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Factory/Factory.cs
new file mode 100644
index 00000000..f2b25b4d
--- /dev/null
+++ b/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Factory/Factory.cs
@@ -0,0 +1,244 @@
+using System;
+using Alipay.EasySDK.Kernel;
+using System.Reflection;
+
+namespace Alipay.EasySDK.Factory
+{
+ ///
+ /// 客户端工厂,用于快速配置和访问各种场景下的API Client
+ ///
+ /// 注:该Factory获取的Client不可储存重复使用,请每次均通过Factory完成调用
+ ///
+ public static class Factory
+ {
+ public const string SDK_VERSION = "alipay-easysdk-net-2.1.0";
+
+ ///
+ /// 将一些初始化耗时较多的信息缓存在上下文中
+ ///
+ private static Context context;
+
+ ///
+ /// 设置客户端参数,只需设置一次,即可反复使用各种场景下的API Client
+ ///
+ /// 客户端参数对象
+ public static void SetOptions(Config options)
+ {
+ context = new Context(options, SDK_VERSION);
+ }
+
+ ///
+ /// 获取调用OpenAPI所需的客户端实例
+ /// 本方法用于调用SDK扩展包中的API Client下的方法
+ ///
+ /// 注:返回的实例不可重复使用,只可用于单次调用
+ ///
+ /// 泛型参数
+ /// API Client的类型对象
+ /// client实例,用于发起单次调用
+ public static T GetClient()
+ {
+ Type type = typeof(T);
+ ConstructorInfo constructor = type.GetConstructor(new Type[] { typeof(Client) });
+ context.SdkVersion = GetSdkVersion(type);
+ return (T)constructor.Invoke(new object[] { new Client(context) });
+ }
+
+ private static string GetSdkVersion(Type client)
+ {
+ return context.SdkVersion + "-" + client.FullName
+ .Replace("EasySDK.", "")
+ .Replace(".Client", "")
+ .Replace(".", "-");
+ }
+
+ ///
+ /// 基础能力相关
+ ///
+ public static class Base
+ {
+ ///
+ /// 获取图片相关API Client
+ ///
+ /// 图片相关API Client
+ public static EasySDK.Base.Image.Client Image()
+ {
+ return new EasySDK.Base.Image.Client(new Client(context));
+ }
+
+ ///
+ /// 获取视频相关API Client
+ ///
+ /// 视频相关API Client
+ public static EasySDK.Base.Video.Client Video()
+ {
+ return new EasySDK.Base.Video.Client(new Client(context));
+ }
+
+ ///
+ /// 获取OAuth认证相关API Client
+ ///
+ /// OAuth认证相关API Client
+ public static EasySDK.Base.OAuth.Client OAuth()
+ {
+ return new EasySDK.Base.OAuth.Client(new Client(context));
+ }
+
+ ///
+ /// 获取小程序二维码相关API Client
+ ///
+ /// 小程序二维码相关API Client
+ public static EasySDK.Base.Qrcode.Client Qrcode()
+ {
+ return new EasySDK.Base.Qrcode.Client(new Client(context));
+ }
+ }
+
+ ///
+ /// 营销能力相关
+ ///
+ public static class Marketing
+ {
+ ///
+ /// 获取生活号相关API Client
+ ///
+ /// 生活号相关API Client
+ public static EasySDK.Marketing.OpenLife.Client OpenLife()
+ {
+ return new EasySDK.Marketing.OpenLife.Client(new Client(context));
+ }
+
+ ///
+ /// 获取支付宝卡包相关API Client
+ ///
+ /// 支付宝卡包相关API Client
+ public static EasySDK.Marketing.Pass.Client Pass()
+ {
+ return new EasySDK.Marketing.Pass.Client(new Client(context));
+ }
+
+ ///
+ /// 获取小程序模板消息相关API Client
+ ///
+ /// 小程序模板消息相关API Client
+ public static EasySDK.Marketing.TemplateMessage.Client TemplateMessage()
+ {
+ return new EasySDK.Marketing.TemplateMessage.Client(new Client(context));
+ }
+ }
+
+ ///
+ /// 会员能力相关
+ ///
+ public static class Member
+ {
+ ///
+ /// 获取支付宝身份认证相关API Client
+ ///
+ /// 支付宝身份认证相关API Client
+ public static EasySDK.Member.Identification.Client Identification()
+ {
+ return new EasySDK.Member.Identification.Client(new Client(context));
+ }
+ }
+
+ ///
+ /// 支付能力相关
+ ///
+ public static class Payment
+ {
+ ///
+ /// 获取支付通用API Client
+ ///
+ /// 支付通用API Client
+ public static EasySDK.Payment.Common.Client Common()
+ {
+ return new EasySDK.Payment.Common.Client(new Client(context));
+ }
+
+ ///
+ /// 获取当面付API Client
+ ///
+ /// 当面付API Client
+ public static EasySDK.Payment.FaceToFace.Client FaceToFace()
+ {
+ return new EasySDK.Payment.FaceToFace.Client(new Client(context));
+ }
+
+ ///
+ /// 获取花呗API Client
+ ///
+ /// 花呗API Client
+ public static EasySDK.Payment.Huabei.Client Huabei()
+ {
+ return new EasySDK.Payment.Huabei.Client(new Client(context));
+ }
+
+ ///
+ /// 获取手机APP支付API Client
+ ///
+ /// 手机APP支付API Client
+ public static EasySDK.Payment.App.Client App()
+ {
+ return new EasySDK.Payment.App.Client(new Client(context));
+ }
+
+ ///
+ /// 获取电脑网站支付API Client
+ ///
+ /// 电脑网站支付API
+ public static EasySDK.Payment.Page.Client Page()
+ {
+ return new EasySDK.Payment.Page.Client(new Client(context));
+ }
+
+ ///
+ /// 获取手机网站支付API Client
+ ///
+ /// 手机网站支付API
+ public static EasySDK.Payment.Wap.Client Wap()
+ {
+ return new EasySDK.Payment.Wap.Client(new Client(context));
+ }
+ }
+
+ ///
+ /// 安全能力相关
+ ///
+ public static class Security
+ {
+ ///
+ /// 获取文本风险识别相关API Client
+ ///
+ /// 文本风险识别相关API Client
+ public static EasySDK.Security.TextRisk.Client TextRisk()
+ {
+ return new EasySDK.Security.TextRisk.Client(new Client(context));
+ }
+ }
+
+ ///
+ /// 辅助工具
+ ///
+ public static class Util
+ {
+ ///
+ /// 获取OpenAPI通用接口,可通过自行拼装参数,调用几乎所有OpenAPI
+ ///
+ /// OpenAPI通用接口
+ public static EasySDK.Util.Generic.Client Generic()
+ {
+ return new EasySDK.Util.Generic.Client(new Client(context));
+ }
+
+ ///
+ /// 获取AES128加解密相关API Client,常用于会员手机号的解密
+ ///
+ /// AES128加解密相关API Client
+ public static EasySDK.Util.AES.Client AES()
+ {
+ return new EasySDK.Util.AES.Client(new Client(context));
+ }
+ }
+ }
+}
diff --git a/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Marketing/OpenLife/Client.cs b/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Marketing/OpenLife/Client.cs
new file mode 100644
index 00000000..eb8daab1
--- /dev/null
+++ b/vendor/alipaysdk/easysdk/csharp/AlipayEasySDK/Marketing/OpenLife/Client.cs
@@ -0,0 +1,1832 @@
+// This file is auto-generated, don't edit it. Thanks.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+
+using Tea;
+using Tea.Utils;
+
+using Alipay.EasySDK.Marketing.OpenLife.Models;
+
+namespace Alipay.EasySDK.Marketing.OpenLife
+{
+ public class Client
+ {
+ protected Alipay.EasySDK.Kernel.Client _kernel;
+
+ public Client(Alipay.EasySDK.Kernel.Client kernel)
+ {
+ this._kernel = kernel;
+ }
+
+ public AlipayOpenPublicMessageContentCreateResponse CreateImageTextContent(string title, string cover, string content, string contentComment, string ctype, string benefit, string extTags, string loginIds)
+ {
+ Dictionary runtime_ = new Dictionary
+ {
+ {"ignoreSSL", this._kernel.GetConfig("ignoreSSL")},
+ {"httpProxy", this._kernel.GetConfig("httpProxy")},
+ {"connectTimeout", 15000},
+ {"readTimeout", 15000},
+ {"retry", new Dictionary
+ {
+ {"maxAttempts", 0},
+ }},
+ };
+
+ TeaRequest _lastRequest = null;
+ Exception _lastException = null;
+ long _now = System.DateTime.Now.Millisecond;
+ int _retryTimes = 0;
+ while (TeaCore.AllowRetry((IDictionary) runtime_["retry"], _retryTimes, _now))
+ {
+ if (_retryTimes > 0)
+ {
+ int backoffTime = TeaCore.GetBackoffTime((IDictionary)runtime_["backoff"], _retryTimes);
+ if (backoffTime > 0)
+ {
+ TeaCore.Sleep(backoffTime);
+ }
+ }
+ _retryTimes = _retryTimes + 1;
+ try
+ {
+ TeaRequest request_ = new TeaRequest();
+ Dictionary systemParams = new Dictionary
+ {
+ {"method", "alipay.open.public.message.content.create"},
+ {"app_id", this._kernel.GetConfig("appId")},
+ {"timestamp", this._kernel.GetTimestamp()},
+ {"format", "json"},
+ {"version", "1.0"},
+ {"alipay_sdk", this._kernel.GetSdkVersion()},
+ {"charset", "UTF-8"},
+ {"sign_type", this._kernel.GetConfig("signType")},
+ {"app_cert_sn", this._kernel.GetMerchantCertSN()},
+ {"alipay_root_cert_sn", this._kernel.GetAlipayRootCertSN()},
+ };
+ Dictionary bizParams = new Dictionary
+ {
+ {"title", title},
+ {"cover", cover},
+ {"content", content},
+ {"could_comment", contentComment},
+ {"ctype", ctype},
+ {"benefit", benefit},
+ {"ext_tags", extTags},
+ {"login_ids", loginIds},
+ };
+ Dictionary textParams = new Dictionary(){};
+ request_.Protocol = this._kernel.GetConfig("protocol");
+ request_.Method = "POST";
+ request_.Pathname = "/gateway.do";
+ request_.Headers = new Dictionary
+ {
+ {"host", this._kernel.GetConfig("gatewayHost")},
+ {"content-type", "application/x-www-form-urlencoded;charset=utf-8"},
+ };
+ request_.Query = this._kernel.SortMap(TeaConverter.merge
+ (
+ new Dictionary()
+ {
+ {"sign", this._kernel.Sign(systemParams, bizParams, textParams, this._kernel.GetConfig("merchantPrivateKey"))},
+ },
+ systemParams,
+ textParams
+ ));
+ request_.Body = TeaCore.BytesReadable(this._kernel.ToUrlEncodedRequestBody(bizParams));
+ _lastRequest = request_;
+ TeaResponse response_ = TeaCore.DoAction(request_, runtime_);
+
+ Dictionary respMap = this._kernel.ReadAsJson(response_, "alipay.open.public.message.content.create");
+ if (this._kernel.IsCertMode())
+ {
+ if (this._kernel.Verify(respMap, this._kernel.ExtractAlipayPublicKey(this._kernel.GetAlipayCertSN(respMap))))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ else
+ {
+ if (this._kernel.Verify(respMap, this._kernel.GetConfig("alipayPublicKey")))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ throw new TeaException(new Dictionary
+ {
+ {"message", "验签失败,请检查支付宝公钥设置是否正确。"},
+ });
+ }
+ catch (Exception e)
+ {
+ if (TeaCore.IsRetryable(e))
+ {
+ _lastException = e;
+ continue;
+ }
+ throw e;
+ }
+ }
+
+ throw new TeaUnretryableException(_lastRequest, _lastException);
+ }
+
+ public async Task CreateImageTextContentAsync(string title, string cover, string content, string contentComment, string ctype, string benefit, string extTags, string loginIds)
+ {
+ Dictionary runtime_ = new Dictionary
+ {
+ {"ignoreSSL", this._kernel.GetConfig("ignoreSSL")},
+ {"httpProxy", this._kernel.GetConfig("httpProxy")},
+ {"connectTimeout", 15000},
+ {"readTimeout", 15000},
+ {"retry", new Dictionary
+ {
+ {"maxAttempts", 0},
+ }},
+ };
+
+ TeaRequest _lastRequest = null;
+ Exception _lastException = null;
+ long _now = System.DateTime.Now.Millisecond;
+ int _retryTimes = 0;
+ while (TeaCore.AllowRetry((IDictionary) runtime_["retry"], _retryTimes, _now))
+ {
+ if (_retryTimes > 0)
+ {
+ int backoffTime = TeaCore.GetBackoffTime((IDictionary)runtime_["backoff"], _retryTimes);
+ if (backoffTime > 0)
+ {
+ TeaCore.Sleep(backoffTime);
+ }
+ }
+ _retryTimes = _retryTimes + 1;
+ try
+ {
+ TeaRequest request_ = new TeaRequest();
+ Dictionary systemParams = new Dictionary
+ {
+ {"method", "alipay.open.public.message.content.create"},
+ {"app_id", this._kernel.GetConfig("appId")},
+ {"timestamp", this._kernel.GetTimestamp()},
+ {"format", "json"},
+ {"version", "1.0"},
+ {"alipay_sdk", this._kernel.GetSdkVersion()},
+ {"charset", "UTF-8"},
+ {"sign_type", this._kernel.GetConfig("signType")},
+ {"app_cert_sn", this._kernel.GetMerchantCertSN()},
+ {"alipay_root_cert_sn", this._kernel.GetAlipayRootCertSN()},
+ };
+ Dictionary bizParams = new Dictionary
+ {
+ {"title", title},
+ {"cover", cover},
+ {"content", content},
+ {"could_comment", contentComment},
+ {"ctype", ctype},
+ {"benefit", benefit},
+ {"ext_tags", extTags},
+ {"login_ids", loginIds},
+ };
+ Dictionary textParams = new Dictionary(){};
+ request_.Protocol = this._kernel.GetConfig("protocol");
+ request_.Method = "POST";
+ request_.Pathname = "/gateway.do";
+ request_.Headers = new Dictionary
+ {
+ {"host", this._kernel.GetConfig("gatewayHost")},
+ {"content-type", "application/x-www-form-urlencoded;charset=utf-8"},
+ };
+ request_.Query = this._kernel.SortMap(TeaConverter.merge
+ (
+ new Dictionary()
+ {
+ {"sign", this._kernel.Sign(systemParams, bizParams, textParams, this._kernel.GetConfig("merchantPrivateKey"))},
+ },
+ systemParams,
+ textParams
+ ));
+ request_.Body = TeaCore.BytesReadable(this._kernel.ToUrlEncodedRequestBody(bizParams));
+ _lastRequest = request_;
+ TeaResponse response_ = await TeaCore.DoActionAsync(request_, runtime_);
+
+ Dictionary respMap = await this._kernel.ReadAsJsonAsync(response_, "alipay.open.public.message.content.create");
+ if (this._kernel.IsCertMode())
+ {
+ if (this._kernel.Verify(respMap, this._kernel.ExtractAlipayPublicKey(this._kernel.GetAlipayCertSN(respMap))))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ else
+ {
+ if (this._kernel.Verify(respMap, this._kernel.GetConfig("alipayPublicKey")))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ throw new TeaException(new Dictionary
+ {
+ {"message", "验签失败,请检查支付宝公钥设置是否正确。"},
+ });
+ }
+ catch (Exception e)
+ {
+ if (TeaCore.IsRetryable(e))
+ {
+ _lastException = e;
+ continue;
+ }
+ throw e;
+ }
+ }
+
+ throw new TeaUnretryableException(_lastRequest, _lastException);
+ }
+
+ public AlipayOpenPublicMessageContentModifyResponse ModifyImageTextContent(string contentId, string title, string cover, string content, string couldComment, string ctype, string benefit, string extTags, string loginIds)
+ {
+ Dictionary runtime_ = new Dictionary
+ {
+ {"ignoreSSL", this._kernel.GetConfig("ignoreSSL")},
+ {"httpProxy", this._kernel.GetConfig("httpProxy")},
+ {"connectTimeout", 15000},
+ {"readTimeout", 15000},
+ {"retry", new Dictionary
+ {
+ {"maxAttempts", 0},
+ }},
+ };
+
+ TeaRequest _lastRequest = null;
+ Exception _lastException = null;
+ long _now = System.DateTime.Now.Millisecond;
+ int _retryTimes = 0;
+ while (TeaCore.AllowRetry((IDictionary) runtime_["retry"], _retryTimes, _now))
+ {
+ if (_retryTimes > 0)
+ {
+ int backoffTime = TeaCore.GetBackoffTime((IDictionary)runtime_["backoff"], _retryTimes);
+ if (backoffTime > 0)
+ {
+ TeaCore.Sleep(backoffTime);
+ }
+ }
+ _retryTimes = _retryTimes + 1;
+ try
+ {
+ TeaRequest request_ = new TeaRequest();
+ Dictionary systemParams = new Dictionary
+ {
+ {"method", "alipay.open.public.message.content.modify"},
+ {"app_id", this._kernel.GetConfig("appId")},
+ {"timestamp", this._kernel.GetTimestamp()},
+ {"format", "json"},
+ {"version", "1.0"},
+ {"alipay_sdk", this._kernel.GetSdkVersion()},
+ {"charset", "UTF-8"},
+ {"sign_type", this._kernel.GetConfig("signType")},
+ {"app_cert_sn", this._kernel.GetMerchantCertSN()},
+ {"alipay_root_cert_sn", this._kernel.GetAlipayRootCertSN()},
+ };
+ Dictionary bizParams = new Dictionary
+ {
+ {"content_id", contentId},
+ {"title", title},
+ {"cover", cover},
+ {"content", content},
+ {"could_comment", couldComment},
+ {"ctype", ctype},
+ {"benefit", benefit},
+ {"ext_tags", extTags},
+ {"login_ids", loginIds},
+ };
+ Dictionary textParams = new Dictionary(){};
+ request_.Protocol = this._kernel.GetConfig("protocol");
+ request_.Method = "POST";
+ request_.Pathname = "/gateway.do";
+ request_.Headers = new Dictionary
+ {
+ {"host", this._kernel.GetConfig("gatewayHost")},
+ {"content-type", "application/x-www-form-urlencoded;charset=utf-8"},
+ };
+ request_.Query = this._kernel.SortMap(TeaConverter.merge
+ (
+ new Dictionary()
+ {
+ {"sign", this._kernel.Sign(systemParams, bizParams, textParams, this._kernel.GetConfig("merchantPrivateKey"))},
+ },
+ systemParams,
+ textParams
+ ));
+ request_.Body = TeaCore.BytesReadable(this._kernel.ToUrlEncodedRequestBody(bizParams));
+ _lastRequest = request_;
+ TeaResponse response_ = TeaCore.DoAction(request_, runtime_);
+
+ Dictionary respMap = this._kernel.ReadAsJson(response_, "alipay.open.public.message.content.modify");
+ if (this._kernel.IsCertMode())
+ {
+ if (this._kernel.Verify(respMap, this._kernel.ExtractAlipayPublicKey(this._kernel.GetAlipayCertSN(respMap))))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ else
+ {
+ if (this._kernel.Verify(respMap, this._kernel.GetConfig("alipayPublicKey")))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ throw new TeaException(new Dictionary
+ {
+ {"message", "验签失败,请检查支付宝公钥设置是否正确。"},
+ });
+ }
+ catch (Exception e)
+ {
+ if (TeaCore.IsRetryable(e))
+ {
+ _lastException = e;
+ continue;
+ }
+ throw e;
+ }
+ }
+
+ throw new TeaUnretryableException(_lastRequest, _lastException);
+ }
+
+ public async Task ModifyImageTextContentAsync(string contentId, string title, string cover, string content, string couldComment, string ctype, string benefit, string extTags, string loginIds)
+ {
+ Dictionary runtime_ = new Dictionary
+ {
+ {"ignoreSSL", this._kernel.GetConfig("ignoreSSL")},
+ {"httpProxy", this._kernel.GetConfig("httpProxy")},
+ {"connectTimeout", 15000},
+ {"readTimeout", 15000},
+ {"retry", new Dictionary
+ {
+ {"maxAttempts", 0},
+ }},
+ };
+
+ TeaRequest _lastRequest = null;
+ Exception _lastException = null;
+ long _now = System.DateTime.Now.Millisecond;
+ int _retryTimes = 0;
+ while (TeaCore.AllowRetry((IDictionary) runtime_["retry"], _retryTimes, _now))
+ {
+ if (_retryTimes > 0)
+ {
+ int backoffTime = TeaCore.GetBackoffTime((IDictionary)runtime_["backoff"], _retryTimes);
+ if (backoffTime > 0)
+ {
+ TeaCore.Sleep(backoffTime);
+ }
+ }
+ _retryTimes = _retryTimes + 1;
+ try
+ {
+ TeaRequest request_ = new TeaRequest();
+ Dictionary systemParams = new Dictionary
+ {
+ {"method", "alipay.open.public.message.content.modify"},
+ {"app_id", this._kernel.GetConfig("appId")},
+ {"timestamp", this._kernel.GetTimestamp()},
+ {"format", "json"},
+ {"version", "1.0"},
+ {"alipay_sdk", this._kernel.GetSdkVersion()},
+ {"charset", "UTF-8"},
+ {"sign_type", this._kernel.GetConfig("signType")},
+ {"app_cert_sn", this._kernel.GetMerchantCertSN()},
+ {"alipay_root_cert_sn", this._kernel.GetAlipayRootCertSN()},
+ };
+ Dictionary bizParams = new Dictionary
+ {
+ {"content_id", contentId},
+ {"title", title},
+ {"cover", cover},
+ {"content", content},
+ {"could_comment", couldComment},
+ {"ctype", ctype},
+ {"benefit", benefit},
+ {"ext_tags", extTags},
+ {"login_ids", loginIds},
+ };
+ Dictionary textParams = new Dictionary(){};
+ request_.Protocol = this._kernel.GetConfig("protocol");
+ request_.Method = "POST";
+ request_.Pathname = "/gateway.do";
+ request_.Headers = new Dictionary
+ {
+ {"host", this._kernel.GetConfig("gatewayHost")},
+ {"content-type", "application/x-www-form-urlencoded;charset=utf-8"},
+ };
+ request_.Query = this._kernel.SortMap(TeaConverter.merge
+ (
+ new Dictionary()
+ {
+ {"sign", this._kernel.Sign(systemParams, bizParams, textParams, this._kernel.GetConfig("merchantPrivateKey"))},
+ },
+ systemParams,
+ textParams
+ ));
+ request_.Body = TeaCore.BytesReadable(this._kernel.ToUrlEncodedRequestBody(bizParams));
+ _lastRequest = request_;
+ TeaResponse response_ = await TeaCore.DoActionAsync(request_, runtime_);
+
+ Dictionary respMap = await this._kernel.ReadAsJsonAsync(response_, "alipay.open.public.message.content.modify");
+ if (this._kernel.IsCertMode())
+ {
+ if (this._kernel.Verify(respMap, this._kernel.ExtractAlipayPublicKey(this._kernel.GetAlipayCertSN(respMap))))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ else
+ {
+ if (this._kernel.Verify(respMap, this._kernel.GetConfig("alipayPublicKey")))
+ {
+ return TeaModel.ToObject(this._kernel.ToRespModel(respMap));
+ }
+ }
+ throw new TeaException(new Dictionary
+ {
+ {"message", "验签失败,请检查支付宝公钥设置是否正确。"},
+ });
+ }
+ catch (Exception e)
+ {
+ if (TeaCore.IsRetryable(e))
+ {
+ _lastException = e;
+ continue;
+ }
+ throw e;
+ }
+ }
+
+ throw new TeaUnretryableException(_lastRequest, _lastException);
+ }
+
+ public AlipayOpenPublicMessageTotalSendResponse SendText(string text)
+ {
+ Dictionary runtime_ = new Dictionary
+ {
+ {"ignoreSSL", this._kernel.GetConfig("ignoreSSL")},
+ {"httpProxy", this._kernel.GetConfig("httpProxy")},
+ {"connectTimeout", 15000},
+ {"readTimeout", 15000},
+ {"retry", new Dictionary
+ {
+ {"maxAttempts", 0},
+ }},
+ };
+
+ TeaRequest _lastRequest = null;
+ Exception _lastException = null;
+ long _now = System.DateTime.Now.Millisecond;
+ int _retryTimes = 0;
+ while (TeaCore.AllowRetry((IDictionary) runtime_["retry"], _retryTimes, _now))
+ {
+ if (_retryTimes > 0)
+ {
+ int backoffTime = TeaCore.GetBackoffTime((IDictionary)runtime_["backoff"], _retryTimes);
+ if (backoffTime > 0)
+ {
+ TeaCore.Sleep(backoffTime);
+ }
+ }
+ _retryTimes = _retryTimes + 1;
+ try
+ {
+ TeaRequest request_ = new TeaRequest();
+ Dictionary systemParams = new Dictionary
+ {
+ {"method", "alipay.open.public.message.total.send"},
+ {"app_id", this._kernel.GetConfig("appId")},
+ {"timestamp", this._kernel.GetTimestamp()},
+ {"format", "json"},
+ {"version", "1.0"},
+ {"alipay_sdk", this._kernel.GetSdkVersion()},
+ {"charset", "UTF-8"},
+ {"sign_type", this._kernel.GetConfig("signType")},
+ {"app_cert_sn", this._kernel.GetMerchantCertSN()},
+ {"alipay_root_cert_sn", this._kernel.GetAlipayRootCertSN()},
+ };
+ Text textObj = new Text
+ {
+ Title = "",
+ Content = text,
+ };
+ Dictionary bizParams = new Dictionary