diff --git a/assets/admin/src/schema/sections/payment/gateway.js b/assets/admin/src/schema/sections/payment/gateway.js index c0f24a0f..f6c79009 100644 --- a/assets/admin/src/schema/sections/payment/gateway.js +++ b/assets/admin/src/schema/sections/payment/gateway.js @@ -22,15 +22,17 @@ export const gatewaySchema = { payment_codepay_enabled: false, payment_stripe_enabled: false, wechat_merchantid: '', - wechat_jsapi: '', - wechat_appsecret: '', + wechat_appid: '', + wechat_payment_type: 'native', + wechat_api_v3_key: '', + wechat_private_key_path: '', wechat_cert_path: '', - wechat_key_path: '', alipay_appid: '', alipay_privatekey: '', - alipay_publickey: '', - alipay_return_url: '', - alipay_notify_url: '', + alipay_payment_type: 'page', + alipay_app_public_cert_path: '', + alipay_public_cert_path: '', + alipay_root_cert_path: '', paypal_client_id: '', paypal_client_secret: '', paypal_currency: 'USD', @@ -94,10 +96,14 @@ export const gatewaySchema = { dependsOn: { key: 'payment_wechat_enabled', value: true }, fields: [ { key: 'wechat_merchantid', label: '商户号', type: 'input', default: '' }, - { key: 'wechat_jsapi', label: 'JSAPI密钥', type: 'input', default: '' }, - { key: 'wechat_appsecret', label: 'AppSecret', type: 'input', default: '' }, - { key: 'wechat_cert_path', label: '证书路径', type: 'input', default: '', help: '退款等接口需要,绝对路径。' }, - { key: 'wechat_key_path', label: '密钥路径', type: 'input', default: '' }, + { key: 'wechat_appid', label: 'AppID', type: 'input', default: '' }, + { key: 'wechat_payment_type', label: '支付方式', type: 'select', default: 'native', options: [ + { label: '扫码支付(Native)', value: 'native' }, + { label: 'H5支付', value: 'h5' }, + ] }, + { key: 'wechat_api_v3_key', label: 'APIv3密钥', type: 'input', default: '', help: '微信商户平台设置的 APIv3 密钥(32位)。' }, + { key: 'wechat_private_key_path', label: '商户私钥路径', type: 'input', default: '', help: 'apiclient_key.pem 绝对路径,用于签名。' }, + { key: 'wechat_cert_path', label: '商户API证书路径', type: 'input', default: '', help: 'apiclient_cert.pem 绝对路径。' }, ], }, { @@ -111,9 +117,14 @@ export const gatewaySchema = { fields: [ { key: 'alipay_appid', label: 'AppID', type: 'input', default: '' }, { key: 'alipay_privatekey', label: '应用私钥', type: 'textarea', rows: 3, default: '' }, - { key: 'alipay_publickey', label: '支付宝公钥', type: 'textarea', rows: 3, default: '' }, - { key: 'alipay_return_url', label: '同步回调地址', type: 'input', default: '' }, - { key: 'alipay_notify_url', label: '异步回调地址', type: 'input', default: '' }, + { key: 'alipay_payment_type', label: '支付方式', type: 'select', default: 'page', options: [ + { label: '电脑网站支付(web)', value: 'page' }, + { label: '手机网站支付(wap)', value: 'wap' }, + ] }, + { key: 'alipay_app_public_cert_path', label: '应用公钥证书路径', type: 'input', default: '', help: 'appCertPublicKey.crt 绝对路径。' }, + { key: 'alipay_public_cert_path', label: '支付宝公钥证书路径', type: 'input', default: '', help: 'alipayCertPublicKey_RSA2.crt 绝对路径。' }, + { key: 'alipay_root_cert_path', label: '支付宝根证书路径', type: 'input', default: '', help: 'alipayRootCert.crt 绝对路径。' }, + { key: 'alipay_notify_help', label: '回调地址说明', type: 'notice', alertType: 'info', alertContent: '异步通知 / 同步返回已固定为 /wp-json/aksrt/v1/payment/alipay/{notify|return},无需在此填写。' }, ], }, { diff --git a/composer.json b/composer.json index 33372bf6..4d863951 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,7 @@ "require": { "php": ">=7.4", "stripe/stripe-php": "^16.1", - "yurunsoft/pay-sdk": "~3.0", + "yansongda/pay": "^3.7", "yurunsoft/yurun-oauth-login": "~3.0" }, "config": { diff --git a/composer.lock b/composer.lock index 70058c82..25d124b6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,337 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8f8c952e2744521268680c9c6f7ad76a", + "content-hash": "fe1258c91a142e623f464f5ddaef58f9", "packages": [ + { + "name": "guzzlehttp/psr7", + "version": "2.13.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "dad89620b7a6edb60c15858442eb2e408b45d8f4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/dad89620b7a6edb60c15858442eb2e408b45d8f4", + "reference": "dad89620b7a6edb60c15858442eb2e408b45d8f4", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0", + "symfony/polyfill-php80": "^1.25" + }, + "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": "1.1.0", + "jshttp/mime-db": "1.54.0.1", + "phpunit/phpunit": "^8.5.52 || ^9.6.34" + }, + "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.13.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": "2026-07-16T22:23:49+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+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", @@ -109,6 +438,50 @@ }, "time": "2024-09-11T13:17:53+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": "stripe/stripe-php", "version": "v16.6.0", @@ -169,45 +542,367 @@ "time": "2025-02-24T22:35:29+00:00" }, { - "name": "yurunsoft/pay-sdk", - "version": "v3.1.7", + "name": "symfony/deprecation-contracts", + "version": "v3.7.1", "source": { "type": "git", - "url": "https://github.com/Yurunsoft/PaySDK.git", - "reference": "e146a056f3aa1b68f64bbc5e261bf137b7723307" + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Yurunsoft/PaySDK/zipball/e146a056f3aa1b68f64bbc5e261bf137b7723307", - "reference": "e146a056f3aa1b68f64bbc5e261bf137b7723307", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/f3202fa1b5097b0af062dc978b32ecf63404e31d", + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d", "shasum": "" }, "require": { - "php": ">=5.5", - "yurunsoft/yurun-http": "~4.0|~5.0" + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-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.7.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-06-05T06:23:12+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-10T16:19:22+00:00" + }, + { + "name": "yansongda/artful", + "version": "v1.1.4", + "source": { + "type": "git", + "url": "https://github.com/yansongda/artful.git", + "reference": "6fb48671fe99c3008566481b16771edcf82ecd0e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yansongda/artful/zipball/6fb48671fe99c3008566481b16771edcf82ecd0e", + "reference": "6fb48671fe99c3008566481b16771edcf82ecd0e", + "shasum": "" + }, + "require": { + "guzzlehttp/psr7": "^2.6", + "php": ">=8.0", + "psr/container": "^1.1 || ^2.0", + "psr/event-dispatcher": "^1.0", + "psr/http-client": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "psr/log": "^1.1 || ^2.0 || ^3.0", + "yansongda/supports": "~4.0.10" }, "require-dev": { - "friendsofphp/php-cs-fixer": "2.18.3" + "friendsofphp/php-cs-fixer": "^3.44", + "guzzlehttp/guzzle": "^7.0", + "hyperf/pimple": "^2.2", + "mockery/mockery": "^1.4", + "monolog/monolog": "^2.2", + "phpstan/phpstan": "^1.0.0 || ^2.0.0", + "phpunit/phpunit": "^9.0", + "symfony/event-dispatcher": "^5.2.0", + "symfony/http-foundation": "^5.2.0", + "symfony/psr-http-message-bridge": "^2.1", + "symfony/var-dumper": "^5.1" + }, + "suggest": { + "hyperf/pimple": "其它/无框架下使用 SDK,请安装,任选其一", + "illuminate/container": "其它/无框架下使用 SDK,请安装,任选其一" }, - "bin": [ - "src/Weixin/Tool/CertificateDownloader.php" - ], "type": "library", "autoload": { + "files": [ + "src/Functions.php" + ], "psr-4": { - "Yurun\\PaySDK\\": "src/" + "Yansongda\\Artful\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "PHP 集成支付 SDK ,集成了支付宝、微信支付的支付接口和其它相关接口的操作。", + "authors": [ + { + "name": "yansongda", + "email": "me@yansongda.cn" + } + ], + "description": "Artful 是一个简单易用的 API 请求框架 PHP Api RequesT Framwork U Like。", + "keywords": [ + "api", + "artful", + "framework", + "request" + ], "support": { - "issues": "https://github.com/Yurunsoft/PaySDK/issues", - "source": "https://github.com/Yurunsoft/PaySDK/tree/v3.1.7" + "homepage": "https://artful.yansongda.cn", + "issues": "https://github.com/yansongda/artful/issues", + "source": "https://github.com/yansongda/artful" }, - "time": "2024-09-02T06:00:28+00:00" + "time": "2026-04-15T06:37:37+00:00" + }, + { + "name": "yansongda/pay", + "version": "v3.7.20", + "source": { + "type": "git", + "url": "https://github.com/yansongda/pay.git", + "reference": "26987ebf789f1e7f0a85febb640986ab4289fd7f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yansongda/pay/zipball/26987ebf789f1e7f0a85febb640986ab4289fd7f", + "reference": "26987ebf789f1e7f0a85febb640986ab4289fd7f", + "shasum": "" + }, + "require": { + "ext-bcmath": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-openssl": "*", + "ext-simplexml": "*", + "php": ">=8.0", + "yansongda/artful": "~1.1.3", + "yansongda/supports": "~4.0.10" + }, + "conflict": { + "hyperf/framework": "<3.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.44", + "guzzlehttp/guzzle": "^7.0", + "hyperf/pimple": "^2.2", + "jetbrains/phpstorm-attributes": "^1.1", + "mockery/mockery": "^1.4", + "monolog/monolog": "^2.2", + "phpstan/phpstan": "^1.0.0 || ^2.0.0", + "phpunit/phpunit": "^9.0", + "symfony/event-dispatcher": "^5.2.0", + "symfony/http-foundation": "^5.2.0", + "symfony/psr-http-message-bridge": "^2.1", + "symfony/var-dumper": "^5.1" + }, + "type": "library", + "autoload": { + "files": [ + "src/Functions.php" + ], + "psr-4": { + "Yansongda\\Pay\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "yansongda", + "email": "me@yansongda.cn" + } + ], + "description": "可能是我用过的最优雅的 Alipay 和 WeChat 的支付 SDK 扩展包了", + "keywords": [ + "alipay", + "pay", + "wechat" + ], + "support": { + "homepage": "https://pay.yansongda.cn", + "issues": "https://github.com/yansongda/pay/issues", + "source": "https://github.com/yansongda/pay" + }, + "time": "2026-03-23T07:33:29+00:00" + }, + { + "name": "yansongda/supports", + "version": "v4.0.12", + "source": { + "type": "git", + "url": "https://github.com/yansongda/supports.git", + "reference": "308de376d20cb1cd4f959644793e0582ccd1ef6d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yansongda/supports/zipball/308de376d20cb1cd4f959644793e0582ccd1ef6d", + "reference": "308de376d20cb1cd4f959644793e0582ccd1ef6d", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-mbstring": "*", + "php": ">=8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.0", + "mockery/mockery": "^1.4", + "phpstan/phpstan": "^1.1.0", + "phpunit/phpunit": "^9.0" + }, + "suggest": { + "monolog/monolog": "Use logger", + "symfony/console": "Use stdout logger" + }, + "type": "library", + "autoload": { + "files": [ + "src/Functions.php" + ], + "psr-4": { + "Yansongda\\Supports\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "yansongda", + "email": "me@yansongda.cn" + } + ], + "description": "common components", + "keywords": [ + "array", + "collection", + "config", + "support" + ], + "support": { + "issues": "https://github.com/yansongda/supports/issues", + "source": "https://github.com/yansongda/supports" + }, + "time": "2025-01-08T08:55:20+00:00" }, { "name": "yurunsoft/yurun-http", diff --git a/src/AKSRT/Admin/Schema/SchemaPayment.php b/src/AKSRT/Admin/Schema/SchemaPayment.php index 5b0fe017..3a23fc44 100644 --- a/src/AKSRT/Admin/Schema/SchemaPayment.php +++ b/src/AKSRT/Admin/Schema/SchemaPayment.php @@ -247,17 +247,26 @@ class SchemaPayment { // Field-level dependsOn kept for contract audit / non-SPA consumers. ['key' => 'wechat', 'title' => '微信支付配置', 'collapsible' => true, 'expandWhen' => 'payment_wechat_enabled', 'dependsOn' => ['key' => 'payment_wechat_enabled', 'value' => true], 'fields' => [ ['key' => 'wechat_merchantid', 'label' => '商户号', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'payment_wechat_enabled', 'value' => true]], - ['key' => 'wechat_jsapi', 'label' => 'JSAPI密钥', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'payment_wechat_enabled', 'value' => true]], - ['key' => 'wechat_appsecret', 'label' => 'AppSecret', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'payment_wechat_enabled', 'value' => true]], - ['key' => 'wechat_cert_path', 'label' => '证书路径', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'payment_wechat_enabled', 'value' => true]], - ['key' => 'wechat_key_path', 'label' => '密钥路径', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'payment_wechat_enabled', 'value' => true]], + ['key' => 'wechat_appid', 'label' => 'AppID', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'payment_wechat_enabled', 'value' => true]], + ['key' => 'wechat_payment_type', 'label' => '支付方式', 'type' => 'select', 'default' => 'native', 'options' => [ + ['value' => 'native', 'label' => '扫码支付(Native)'], + ['value' => 'h5', 'label' => 'H5支付'], + ], 'dependsOn' => ['key' => 'payment_wechat_enabled', 'value' => true]], + ['key' => 'wechat_api_v3_key', 'label' => 'APIv3密钥', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'payment_wechat_enabled', 'value' => true]], + ['key' => 'wechat_private_key_path', 'label' => '商户私钥路径', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'payment_wechat_enabled', 'value' => true]], + ['key' => 'wechat_cert_path', 'label' => '商户API证书路径', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'payment_wechat_enabled', 'value' => true]], ]], ['key' => 'alipay', 'title' => '支付宝配置', 'collapsible' => true, 'expandWhen' => 'payment_alipay_enabled', 'dependsOn' => ['key' => 'payment_alipay_enabled', 'value' => true], 'fields' => [ ['key' => 'alipay_appid', 'label' => 'AppID', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'payment_alipay_enabled', 'value' => true]], ['key' => 'alipay_privatekey', 'label' => '应用私钥', 'type' => 'textarea', 'default' => '', 'dependsOn' => ['key' => 'payment_alipay_enabled', 'value' => true]], - ['key' => 'alipay_publickey', 'label' => '支付宝公钥', 'type' => 'textarea', 'default' => '', 'dependsOn' => ['key' => 'payment_alipay_enabled', 'value' => true]], - ['key' => 'alipay_return_url', 'label' => '同步回调地址', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'payment_alipay_enabled', 'value' => true]], - ['key' => 'alipay_notify_url', 'label' => '异步回调地址', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'payment_alipay_enabled', 'value' => true]], + ['key' => 'alipay_payment_type', 'label' => '支付方式', 'type' => 'select', 'default' => 'page', 'options' => [ + ['value' => 'page', 'label' => '电脑网站支付(web)'], + ['value' => 'wap', 'label' => '手机网站支付(wap)'], + ], 'dependsOn' => ['key' => 'payment_alipay_enabled', 'value' => true]], + ['key' => 'alipay_app_public_cert_path', 'label' => '应用公钥证书路径', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'payment_alipay_enabled', 'value' => true]], + ['key' => 'alipay_public_cert_path', 'label' => '支付宝公钥证书路径', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'payment_alipay_enabled', 'value' => true]], + ['key' => 'alipay_root_cert_path', 'label' => '支付宝根证书路径', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'payment_alipay_enabled', 'value' => true]], + ['key' => 'alipay_notify_help', 'label' => '回调地址说明', 'type' => 'notice', 'alertType' => 'info', 'alertContent' => '异步通知 / 同步返回已固定为 /wp-json/aksrt/v1/payment/alipay/{notify|return},无需在此填写。', 'dependsOn' => ['key' => 'payment_alipay_enabled', 'value' => true]], ]], ['key' => 'paypal', 'title' => 'PayPal配置', 'collapsible' => true, 'expandWhen' => 'payment_paypal_enabled', 'dependsOn' => ['key' => 'payment_paypal_enabled', 'value' => true], 'fields' => [ ['key' => 'paypal_client_id', 'label' => 'Client ID', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'payment_paypal_enabled', 'value' => true]], diff --git a/src/AKSRT/Payment/AfterSale.php b/src/AKSRT/Payment/AfterSale.php index 9a11c216..57f5abb2 100644 --- a/src/AKSRT/Payment/AfterSale.php +++ b/src/AKSRT/Payment/AfterSale.php @@ -328,6 +328,10 @@ class AfterSale { $update['resolved_at'] = current_time('mysql'); } + if ($new_status === self::STATUS_COMPLETED && !$this->complete_refund($record)) { + return false; + } + $updated = (bool) $wpdb->update($this->table(), $update, ['id' => $id]); if ($updated) { $note = $operator_note !== '' ? $operator_note : sprintf(__('售后状态更新为:%s', 'aksrt'), $this->get_status_label($new_status)); @@ -565,6 +569,74 @@ class AfterSale { do_action('aksrt_shop_refund_executed', $order_number, $user_id, $refund_amount, $payment_method); } + /** + * Execute an online-channel refund before completing the after-sale ticket. + */ + private function complete_refund(array $record): bool { + $type = (string) ($record['type'] ?? ''); + if (!in_array($type, [self::TYPE_REFUND, self::TYPE_REFUND_RETURN, self::TYPE_INSURED_PRICE], true)) { + return true; + } + + if (!class_exists('AKSRT\\Payment\\OrderRepository')) { + return false; + } + + $order_number = sanitize_text_field((string) ($record['order_number'] ?? '')); + $order = OrderRepository::get_instance()->get($order_number); + if (!$order) { + return false; + } + + $payment_method = (string) ($order['payment_method'] ?? 'balance'); + if (!in_array($payment_method, ['wechatpay', 'alipay'], true)) { + return true; + } + + $refund_amount = (float) ($record['amount'] ?? 0); + if ($refund_amount <= 0) { + return false; + } + + $request_id = sprintf( + 'AS_%d_%s', + (int) ($record['id'] ?? 0), + substr(hash('sha256', $order_number), 0, 16) + ); + return $this->refund_via_gateway($payment_method, $order_number, $refund_amount, $request_id); + } + + private function refund_via_gateway(string $payment_method, string $order_number, float $refund_amount, string $request_id): bool { + $gateway = Gateway::get_instance()->get_gateway($payment_method); + if (!$gateway || !method_exists($gateway, 'refund')) { + $this->log_refund_error($order_number, $payment_method, 'gateway unavailable'); + return false; + } + + try { + $result = $gateway->refund($order_number, $refund_amount, $request_id); + } catch (\Throwable $e) { + $this->log_refund_error($order_number, $payment_method, $e->getMessage()); + return false; + } + + if (empty($result['success'])) { + $this->log_refund_error($order_number, $payment_method, (string) ($result['message'] ?? 'unknown error')); + return false; + } + + return true; + } + + private function log_refund_error(string $order_number, string $payment_method, string $message): void { + error_log(sprintf( + '[AKSRT AfterSale] gateway refund failed order=%s gateway=%s: %s', + $order_number, + $payment_method, + $message + )); + } + private function sync_order_status(array $record, string $new_status, string $note = ''): void { if (!class_exists('AKSRT\\Payment\\OrderRepository') || !class_exists('AKSRT\\Payment\\Order')) { return; @@ -807,13 +879,20 @@ class AfterSale { return true; } - $wpdb->update($this->table(), [ + if (!$this->complete_refund($record)) { + return false; + } + + $updated = (bool) $wpdb->update($this->table(), [ 'status' => self::STATUS_COMPLETED, 'operator_id' => $operator_id, 'operator_note' => sanitize_textarea_field($note), 'updated_at' => current_time('mysql'), 'resolved_at' => current_time('mysql'), ], ['id' => $ticket_id]); + if (!$updated) { + return false; + } update_post_meta($ticket_id, '_aksrt_as_progress', self::PROGRESS_DONE); $this->sync_order_status($record, self::STATUS_COMPLETED, $note ?: __('售后已完成', 'aksrt')); diff --git a/src/AKSRT/Payment/Gateway.php b/src/AKSRT/Payment/Gateway.php index f956e7a6..fa1ca02e 100644 --- a/src/AKSRT/Payment/Gateway.php +++ b/src/AKSRT/Payment/Gateway.php @@ -114,27 +114,29 @@ class Gateway { return new \WP_REST_Response('gateway not found', 404); } - $verify = $gateway->verify_callback($request); - $verified = !empty($verify['success']); - $paid = $verified && ($verify['status'] ?? '') === 'paid'; + $verify = $gateway->verify_callback($request); + $verified = !empty($verify['success']); + $paid = $verified && ($verify['status'] ?? '') === 'paid'; + $processed = $verified; if ($paid) { - $this->complete_order($gateway_id, $verify); + // Only ACK paid notifications after the order was actually synchronized. + $processed = $this->complete_order($gateway_id, $verify); } // 网关各自要求的 ACK 格式 if ($gateway_id === 'wechatpay' && method_exists($gateway, 'output_callback_response')) { - $gateway->output_callback_response(['success' => $verified]); // exit + $gateway->output_callback_response(['success' => $processed]); // exit } if ($gateway_id === 'alipay') { // 支付宝异步通知要求返回纯文本 success,否则会重复通知 header('Content-Type: text/plain; charset=utf-8'); - echo $verified ? 'success' : 'fail'; + echo $processed ? 'success' : 'fail'; exit; } - // Stripe / PayPal webhook:200 表示已接收;验签失败返回 400 触发重试 - return new \WP_REST_Response(['received' => $verified], $verified ? 200 : 400); + // Stripe / PayPal webhook:200 表示已接收;处理失败返回 400 触发重试 + return new \WP_REST_Response(['received' => $processed], $processed ? 200 : 400); } /** @@ -156,6 +158,20 @@ class Gateway { $order_number = $order_number ?: sanitize_text_field((string) ($verify['order_number'] ?? '')); if (($verify['status'] ?? '') === 'paid' && $this->complete_order($gateway_id, $verify)) { $status = 'success'; + } elseif ($order_number !== '') { + // Alipay synchronous returns generally omit trade_status; query after signature verification. + $query = $gateway->query_status($order_number); + if (!empty($query['success']) && ($query['status'] ?? '') === 'paid') { + $done = $this->complete_order($gateway_id, [ + 'order_number' => $order_number, + 'amount' => 0, + 'transaction_id' => $query['transaction_id'] ?? '', + 'status' => 'paid', + ]); + if ($done) { + $status = 'success'; + } + } } } } elseif ($gateway_id === 'paypal' && method_exists($gateway, 'capture_order')) { diff --git a/src/AKSRT/Payment/Gateways/AlipayGateway.php b/src/AKSRT/Payment/Gateways/AlipayGateway.php index daee0bb7..47ac87cf 100644 --- a/src/AKSRT/Payment/Gateways/AlipayGateway.php +++ b/src/AKSRT/Payment/Gateways/AlipayGateway.php @@ -1,5 +1,7 @@ get_config(); - return !empty($config['app_id']) && !empty($config['private_key']) && !empty($config['alipay_public_key']); - } + public function is_configured(): bool { + if (class_exists('\AKSRT\Settings\PaymentRuntime') + && !\AKSRT\Settings\PaymentRuntime::is_gateway_enabled('alipay')) { + return false; + } + $c = $this->get_config(); + return $c['app_id'] !== '' + && $c['private_key'] !== '' + && $c['app_public_cert'] !== '' + && $c['alipay_public_cert'] !== '' + && $c['alipay_root_cert'] !== ''; + } - public function create_payment(array $order): array { - if (!$this->is_configured()) { - return ['success' => false, 'message' => __('支付宝未配置', 'aksrt')]; - } + public function create_payment(array $order): array { + if (!$this->is_configured()) { + return ['success' => false, 'message' => __('支付宝未配置', 'aksrt')]; + } - $config = $this->get_config(); - $order_number = $order['order_number'] ?? ''; - $amount = (float) ($order['amount'] ?? 0); - $subject = $order['subject'] ?? __('订单支付', 'aksrt'); - $payment_type = $config['payment_type'] ?? 'page'; // page / wap + $order_number = (string) ($order['order_number'] ?? ''); + $amount = (float) ($order['amount'] ?? 0); + $subject = (string) ($order['subject'] ?? __('订单支付', 'aksrt')); + $payment_type = $this->get_config()['payment_type']; - try { - $sdk = $this->create_sdk($config); + if ($order_number === '' || $amount <= 0) { + return ['success' => false, 'message' => __('订单参数无效', 'aksrt')]; + } - if ($payment_type === 'wap') { - // 手机网站支付 - $request = new \Yurun\PaySDK\AlipayApp\Wap\Params\Pay\Request; - $request->notify_url = home_url('/wp-json/aksrt/v1/payment/alipay/notify'); - $request->return_url = home_url('/wp-json/aksrt/v1/payment/alipay/return'); - $request->businessParams->out_trade_no = $order_number; - $request->businessParams->total_amount = number_format($amount, 2, '.', ''); - $request->businessParams->subject = $subject; - } else { - // 电脑网站支付 - $request = new \Yurun\PaySDK\AlipayApp\Page\Params\Pay\Request; - $request->notify_url = home_url('/wp-json/aksrt/v1/payment/alipay/notify'); - $request->return_url = home_url('/wp-json/aksrt/v1/payment/alipay/return'); - $request->businessParams->out_trade_no = $order_number; - $request->businessParams->total_amount = number_format($amount, 2, '.', ''); - $request->businessParams->subject = $subject; - } + try { + YansongdaPayFactory::boot_alipay(); - // 构建支付跳转 URL(prepareExecute 已将查询参数追加到 $url) - $sdk->prepareExecute($request, $url, $data); - $payment_url = $url; + $payload = [ + 'out_trade_no' => $order_number, + 'total_amount' => number_format($amount, 2, '.', ''), + 'subject' => mb_substr($subject, 0, 256), + ]; - return [ - 'success' => true, - 'payment_url' => $payment_url, - 'payment_data' => ['order_number' => $order_number], - 'message' => __('支付宝支付链接已生成', 'aksrt'), - ]; - } catch (\Exception $e) { - return ['success' => false, 'message' => $e->getMessage()]; - } - } + $result = $payment_type === 'wap' + ? Pay::alipay()->wap($payload) + : Pay::alipay()->web($payload); - public function verify_callback(\WP_REST_Request $request): array { - $config = $this->get_config(); + $payment_url = YansongdaPayFactory::extract_payment_url($result); + $html = YansongdaPayFactory::extract_html_body($result); - try { - $sdk = $this->create_sdk($config); - $params = $request->get_params(); + // Prefer Location / action URL; if only auto-submit HTML, expose as payment_data for frontend. + if ($payment_url === '' && $html !== '' && preg_match('#action=[\'"]([^\'"]+)[\'"]#i', $html, $m)) { + $payment_url = html_entity_decode($m[1], ENT_QUOTES); + } - if ($sdk->verifyCallback($params)) { - $trade_status = $params['trade_status'] ?? ''; - return [ - 'success' => true, - 'order_number' => $params['out_trade_no'] ?? '', - 'amount' => (float) ($params['total_amount'] ?? 0), - 'transaction_id' => $params['trade_no'] ?? '', - 'status' => in_array($trade_status, ['TRADE_SUCCESS', 'TRADE_FINISHED']) ? 'paid' : 'pending', - ]; - } + if ($payment_url === '' && $html === '') { + return ['success' => false, 'message' => __('支付宝支付链接生成失败', 'aksrt')]; + } - return ['success' => false, 'message' => __('支付宝回调验证失败', 'aksrt')]; - } catch (\Exception $e) { - return ['success' => false, 'message' => $e->getMessage()]; - } - } + return [ + 'success' => true, + 'payment_url' => $payment_url, + 'payment_data' => array_filter([ + 'order_number' => $order_number, + 'html' => $html !== '' ? $html : null, + ]), + 'message' => __('支付宝支付链接已生成', 'aksrt'), + ]; + } catch (\Throwable $e) { + return ['success' => false, 'message' => $e->getMessage()]; + } + } - public function query_status(string $order_number): array { - if (!$this->is_configured()) { - return ['success' => false, 'message' => __('支付宝未配置', 'aksrt')]; - } + public function verify_callback(\WP_REST_Request $request): array { + try { + YansongdaPayFactory::boot_alipay(); - $config = $this->get_config(); + // Use the unsanitized form/query payload; route params must not enter the signature base. + $callback_input = strtoupper($request->get_method()) === 'POST' + ? $request->get_body_params() + : $request->get_query_params(); + if (!is_array($callback_input)) { + $callback_input = []; + } + unset($callback_input['gateway']); - try { - $sdk = $this->create_sdk($config); + $data_obj = Pay::alipay()->callback($callback_input); + $data = YansongdaPayFactory::to_array($data_obj); - $request = new \Yurun\PaySDK\AlipayApp\Params\Query\Request; - $request->businessParams->out_trade_no = $order_number; + $trade_status = (string) ($data['trade_status'] ?? ''); + $paid = in_array($trade_status, ['TRADE_SUCCESS', 'TRADE_FINISHED'], true); - $result = $sdk->execute($request); + return [ + 'success' => true, + 'order_number' => (string) ($data['out_trade_no'] ?? ''), + 'amount' => (float) ($data['total_amount'] ?? 0), + 'transaction_id' => (string) ($data['trade_no'] ?? ''), + 'status' => $paid ? 'paid' : 'pending', + ]; + } catch (\Throwable $e) { + return ['success' => false, 'message' => $e->getMessage()]; + } + } - if ($sdk->checkResult()) { - $trade_status = $result['trade_status'] ?? ''; - $status = 'pending'; - if (in_array($trade_status, ['TRADE_SUCCESS', 'TRADE_FINISHED'])) { - $status = 'paid'; - } elseif ($trade_status === 'TRADE_CLOSED') { - $status = 'cancelled'; - } + /** + * Alipay async notify expects plain text success/fail (Gateway also handles this). + */ + public function output_callback_response(array $result): void { + try { + if (!empty($result['success'])) { + YansongdaPayFactory::boot_alipay(); + YansongdaPayFactory::emit_response(Pay::alipay()->success(), 'success'); + } + } catch (\Throwable $e) { + // fall through + } + header('Content-Type: text/plain; charset=utf-8'); + echo 'fail'; + exit; + } - return [ - 'success' => true, - 'status' => $status, - 'transaction_id' => $result['trade_no'] ?? '', - ]; - } + public function query_status(string $order_number): array { + if (!$this->is_configured()) { + return ['success' => false, 'message' => __('支付宝未配置', 'aksrt')]; + } - return ['success' => false, 'message' => $sdk->getError() ?: __('查询失败', 'aksrt')]; - } catch (\Exception $e) { - return ['success' => false, 'message' => $e->getMessage()]; - } - } + try { + YansongdaPayFactory::boot_alipay(); + $result = Pay::alipay()->query([ + 'out_trade_no' => $order_number, + ]); + $data = YansongdaPayFactory::to_array($result); - public function refund(string $order_number, float $amount): array { - if (!$this->is_configured()) { - return ['success' => false, 'message' => __('支付宝未配置', 'aksrt')]; - } + $trade_status = (string) ($data['trade_status'] ?? ''); + $status = 'pending'; + if (in_array($trade_status, ['TRADE_SUCCESS', 'TRADE_FINISHED'], true)) { + $status = 'paid'; + } elseif ($trade_status === 'TRADE_CLOSED') { + $status = 'cancelled'; + } - $config = $this->get_config(); + return [ + 'success' => true, + 'status' => $status, + 'transaction_id' => (string) ($data['trade_no'] ?? ''), + ]; + } catch (\Throwable $e) { + return ['success' => false, 'message' => $e->getMessage()]; + } + } - try { - $sdk = $this->create_sdk($config); + public function refund(string $order_number, float $amount, string $request_id = ''): array { + if (!$this->is_configured()) { + return ['success' => false, 'message' => __('支付宝未配置', 'aksrt')]; + } - $request = new \Yurun\PaySDK\AlipayApp\Params\Refund\Request; - $request->businessParams->out_trade_no = $order_number; - $request->businessParams->refund_amount = number_format($amount, 2, '.', ''); - $request->businessParams->refund_reason = __('用户申请退款', 'aksrt'); - $request->businessParams->out_request_no = $order_number . '_refund_' . time(); + try { + YansongdaPayFactory::boot_alipay(); + $refund_request_id = sanitize_key($request_id !== '' ? $request_id : $order_number . '_refund'); + $result = Pay::alipay()->refund([ + 'out_trade_no' => $order_number, + 'refund_amount' => number_format($amount, 2, '.', ''), + 'refund_reason' => __('用户申请退款', 'aksrt'), + 'out_request_no' => $refund_request_id, + ]); + $data = YansongdaPayFactory::to_array($result); - $result = $sdk->execute($request); + return [ + 'success' => true, + 'refund_id' => (string) ($data['trade_no'] ?? $data['out_trade_no'] ?? ''), + 'message' => __('退款成功', 'aksrt'), + ]; + } catch (\Throwable $e) { + return ['success' => false, 'message' => $e->getMessage()]; + } + } - if ($sdk->checkResult()) { - return [ - 'success' => true, - 'refund_id' => $result['trade_no'] ?? '', - 'message' => __('退款成功', 'aksrt'), - ]; - } - - return ['success' => false, 'message' => $sdk->getError() ?: __('退款失败', 'aksrt')]; - } catch (\Exception $e) { - return ['success' => false, 'message' => $e->getMessage()]; - } - } - - private function get_config(): array { - if (!class_exists('\AKSRT\Settings\Settings')) { - return []; - } - $all = \AKSRT\Settings\Settings::get_all(); - return [ - 'app_id' => (string) ($all['alipay_appid'] ?? ''), - 'private_key' => (string) ($all['alipay_privatekey'] ?? ''), - 'alipay_public_key' => (string) ($all['alipay_publickey'] ?? ''), - 'return_url' => (string) ($all['alipay_return_url'] ?? ''), - 'notify_url' => (string) ($all['alipay_notify_url'] ?? ''), - 'payment_type' => (string) ($all['alipay_payment_type'] ?? 'page'), - 'sign_type' => (string) ($all['alipay_sign_type'] ?? 'RSA2'), - ]; - } - - private function create_sdk(array $config): \Yurun\PaySDK\AlipayApp\SDK { - $params = new \Yurun\PaySDK\AlipayApp\Params\PublicParams; - $params->appID = $config['app_id'] ?? ''; - $params->appPrivateKey = $config['private_key'] ?? ''; - $params->appPublicKey = $config['alipay_public_key'] ?? ''; - $params->sign_type = $config['sign_type'] ?? 'RSA2'; - - return new \Yurun\PaySDK\AlipayApp\SDK($params); - } + /** + * @return array{app_id:string,private_key:string,app_public_cert:string,alipay_public_cert:string,alipay_root_cert:string,payment_type:string} + */ + private function get_config(): array { + $all = class_exists('\AKSRT\Settings\Settings') + ? \AKSRT\Settings\Settings::get_all() + : []; + $type = sanitize_key((string) ($all['alipay_payment_type'] ?? 'page')); + if (!in_array($type, ['page', 'wap'], true)) { + $type = 'page'; + } + return [ + 'app_id' => (string) ($all['alipay_appid'] ?? ''), + 'private_key' => (string) ($all['alipay_privatekey'] ?? ''), + 'app_public_cert' => (string) ($all['alipay_app_public_cert_path'] ?? ''), + 'alipay_public_cert' => (string) ($all['alipay_public_cert_path'] ?? ''), + 'alipay_root_cert' => (string) ($all['alipay_root_cert_path'] ?? ''), + 'payment_type' => $type, + ]; + } } diff --git a/src/AKSRT/Payment/Gateways/WechatPayGateway.php b/src/AKSRT/Payment/Gateways/WechatPayGateway.php index 1a1def08..ac414e5f 100644 --- a/src/AKSRT/Payment/Gateways/WechatPayGateway.php +++ b/src/AKSRT/Payment/Gateways/WechatPayGateway.php @@ -1,5 +1,7 @@ get_config(); - return !empty($config['mch_id']) && !empty($config['key']); - } + public function is_configured(): bool { + if (class_exists('\AKSRT\Settings\PaymentRuntime') + && !\AKSRT\Settings\PaymentRuntime::is_gateway_enabled('wechatpay')) { + return false; + } + $c = $this->get_config(); + return $c['mch_id'] !== '' + && $c['appid'] !== '' + && $c['api_v3_key'] !== '' + && $c['private_key'] !== '' + && $c['cert_path'] !== ''; + } - public function create_payment(array $order): array { - if (!$this->is_configured()) { - return ['success' => false, 'message' => __('微信支付未配置', 'aksrt')]; - } + public function create_payment(array $order): array { + if (!$this->is_configured()) { + return ['success' => false, 'message' => __('微信支付未配置', 'aksrt')]; + } - $config = $this->get_config(); - $order_number = $order['order_number'] ?? ''; - $amount = (float) ($order['amount'] ?? 0); - $subject = $order['subject'] ?? __('订单支付', 'aksrt'); - $payment_type = $config['payment_type'] ?? 'native'; // native / jsapi / h5 + $order_number = (string) ($order['order_number'] ?? ''); + $amount = (float) ($order['amount'] ?? 0); + $subject = (string) ($order['subject'] ?? __('订单支付', 'aksrt')); + $payment_type = $this->get_config()['payment_type']; + $total_fen = (int) round($amount * 100); - try { - $sdk = $this->create_sdk($config); - $total_fee = (int) ($amount * 100); // 转为分 + if ($order_number === '' || $total_fen <= 0) { + return ['success' => false, 'message' => __('订单参数无效', 'aksrt')]; + } - switch ($payment_type) { - case 'jsapi': - $request = new \Yurun\PaySDK\Weixin\JSAPI\Params\Pay\Request; - $request->openid = $order['openid'] ?? ''; - break; - case 'h5': - $request = new \Yurun\PaySDK\Weixin\H5\Params\Pay\Request; - break; - case 'native': - default: - $request = new \Yurun\PaySDK\Weixin\Native\Params\Pay\Request; - break; - } + try { + YansongdaPayFactory::boot_wechat(); - $request->body = $subject; - $request->out_trade_no = $order_number; - $request->total_fee = $total_fee; - $request->fee_type = 'CNY'; - $request->notify_url = home_url('/wp-json/aksrt/v1/payment/wechatpay/notify'); - $request->spbill_create_ip = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1'; + $payload = [ + 'out_trade_no' => $order_number, + 'description' => mb_substr($subject, 0, 127), + 'amount' => [ + 'total' => $total_fen, + 'currency' => 'CNY', + ], + ]; - $result = $sdk->execute($request); + switch ($payment_type) { + case 'h5': + $payload['scene_info'] = [ + 'payer_client_ip' => (string) ($_SERVER['REMOTE_ADDR'] ?? '127.0.0.1'), + 'h5_info' => ['type' => 'Wap'], + ]; + $result = Pay::wechat()->h5($payload); + break; + case 'native': + default: + $result = Pay::wechat()->scan($payload); + break; + } - if ($sdk->checkResult()) { - $result_data = [ - 'success' => true, - 'payment_data' => array_merge($result, ['order_number' => $order_number]), - 'message' => __('微信支付订单已创建', 'aksrt'), - ]; + $data = YansongdaPayFactory::to_array($result); + $payment_data = array_merge($data, ['order_number' => $order_number]); - if ($payment_type === 'native' && !empty($result['code_url'])) { - $result_data['payment_url'] = $result['code_url']; - } elseif ($payment_type === 'h5' && !empty($result['h5_url'])) { - $result_data['payment_url'] = $result['h5_url']; - } + $result_data = [ + 'success' => true, + 'payment_data' => $payment_data, + 'message' => __('微信支付订单已创建', 'aksrt'), + ]; - return $result_data; - } + if ($payment_type === 'native' && !empty($data['code_url'])) { + $result_data['payment_url'] = (string) $data['code_url']; + } elseif ($payment_type === 'h5' && !empty($data['h5_url'])) { + $result_data['payment_url'] = (string) $data['h5_url']; + } - return ['success' => false, 'message' => $sdk->getError() ?: __('微信支付创建失败', 'aksrt')]; - } catch (\Exception $e) { - return ['success' => false, 'message' => $e->getMessage()]; - } - } + return $result_data; + } catch (\Throwable $e) { + return ['success' => false, 'message' => $e->getMessage()]; + } + } - public function verify_callback(\WP_REST_Request $request): array { - $config = $this->get_config(); + public function verify_callback(\WP_REST_Request $request): array { + try { + YansongdaPayFactory::boot_wechat(); - try { - $sdk = $this->create_sdk($config); + // Pass raw body + headers so callback works under WP REST (php://input may be drained). + $raw = $request->get_body(); + $headers = [ + 'Content-Type' => (string) ($request->get_header('content-type') ?: 'application/json'), + 'Wechatpay-Serial' => (string) $request->get_header('wechatpay-serial'), + 'Wechatpay-Timestamp' => (string) $request->get_header('wechatpay-timestamp'), + 'Wechatpay-Nonce' => (string) $request->get_header('wechatpay-nonce'), + 'Wechatpay-Signature' => (string) $request->get_header('wechatpay-signature'), + ]; + $callback = Pay::wechat()->callback([ + 'body' => $raw !== '' ? $raw : file_get_contents('php://input'), + 'headers' => $headers, + ]); - // 读取原始请求体 - $xml = file_get_contents('php://input'); - $data = \Yurun\PaySDK\Lib\XML::fromString($xml); + $data = YansongdaPayFactory::to_array($callback); + // Decrypted resource is nested under resource (array) after CallbackPlugin. + $resource = []; + if (isset($data['resource']) && is_array($data['resource'])) { + $resource = $data['resource']; + // ciphertext decrypt may leave plaintext fields at resource root or under ciphertext key. + if (isset($resource['ciphertext']) && is_array($resource['ciphertext'])) { + $resource = $resource['ciphertext']; + } + } - if ($sdk->verifyCallback($data)) { - return [ - 'success' => true, - 'order_number' => $data['out_trade_no'] ?? '', - 'amount' => (($data['total_fee'] ?? 0) / 100), - 'transaction_id' => $data['transaction_id'] ?? '', - 'status' => ($data['result_code'] ?? '') === 'SUCCESS' ? 'paid' : 'pending', - ]; - } + $order_number = (string) ($resource['out_trade_no'] ?? $data['out_trade_no'] ?? ''); + $transaction_id = (string) ($resource['transaction_id'] ?? $data['transaction_id'] ?? ''); + $trade_state = (string) ($resource['trade_state'] ?? $data['trade_state'] ?? ''); + $event_type = (string) ($data['event_type'] ?? ''); - return ['success' => false, 'message' => __('微信支付回调签名验证失败', 'aksrt')]; - } catch (\Exception $e) { - return ['success' => false, 'message' => $e->getMessage()]; - } - } + $total_fen = (int) YansongdaPayFactory::dig($resource, 'amount.total', 0); + if ($total_fen <= 0) { + $total_fen = (int) YansongdaPayFactory::dig($data, 'amount.total', 0); + } - /** - * 输出微信支付回调响应 - */ - public function output_callback_response(array $result): void { - if ($result['success'] ?? false) { - echo ''; - } else { - echo ''; - } - exit; - } + $paid = ($trade_state === 'SUCCESS') || ($event_type === 'TRANSACTION.SUCCESS'); - public function query_status(string $order_number): array { - if (!$this->is_configured()) { - return ['success' => false, 'message' => __('微信支付未配置', 'aksrt')]; - } + // When event is pay success, resource has trade_state SUCCESS. + if ($event_type !== '' && $event_type !== 'TRANSACTION.SUCCESS' && $trade_state !== 'SUCCESS') { + $paid = false; + } - $config = $this->get_config(); + return [ + 'success' => true, + 'order_number' => $order_number, + 'amount' => $total_fen > 0 ? ($total_fen / 100) : 0.0, + 'transaction_id' => $transaction_id, + 'status' => $paid ? 'paid' : 'pending', + ]; + } catch (\Throwable $e) { + return ['success' => false, 'message' => $e->getMessage()]; + } + } - try { - $sdk = $this->create_sdk($config); + /** + * Output WeChat v3 notify ACK (JSON). + */ + public function output_callback_response(array $result): void { + try { + YansongdaPayFactory::boot_wechat(); + if (!empty($result['success'])) { + YansongdaPayFactory::emit_response(Pay::wechat()->success(), '{"code":"SUCCESS","message":"成功"}'); + } + } catch (\Throwable $e) { + // fall through to fail body + } - $request = new \Yurun\PaySDK\Weixin\OrderQuery\Request; - $request->out_trade_no = $order_number; + if (!headers_sent()) { + http_response_code(500); + header('Content-Type: application/json; charset=utf-8'); + } + echo wp_json_encode([ + 'code' => 'FAIL', + 'message' => (string) ($result['message'] ?? 'FAIL'), + ]); + exit; + } - $result = $sdk->execute($request); + public function query_status(string $order_number): array { + if (!$this->is_configured()) { + return ['success' => false, 'message' => __('微信支付未配置', 'aksrt')]; + } - if ($sdk->checkResult()) { - $trade_state = $result['trade_state'] ?? ''; - $status = 'pending'; - if ($trade_state === 'SUCCESS') { - $status = 'paid'; - } elseif (in_array($trade_state, ['CLOSED', 'REVOKED'])) { - $status = 'cancelled'; - } elseif ($trade_state === 'REFUND') { - $status = 'refunded'; - } + try { + YansongdaPayFactory::boot_wechat(); + $action = $this->query_action(); + $result = Pay::wechat()->query([ + 'out_trade_no' => $order_number, + '_action' => $action, + ]); + $data = YansongdaPayFactory::to_array($result); - return [ - 'success' => true, - 'status' => $status, - 'transaction_id' => $result['transaction_id'] ?? '', - ]; - } + $trade_state = (string) ($data['trade_state'] ?? ''); + $status = 'pending'; + if ($trade_state === 'SUCCESS') { + $status = 'paid'; + } elseif (in_array($trade_state, ['CLOSED', 'REVOKED'], true)) { + $status = 'cancelled'; + } elseif ($trade_state === 'REFUND') { + $status = 'refunded'; + } - return ['success' => false, 'message' => $sdk->getError() ?: __('查询失败', 'aksrt')]; - } catch (\Exception $e) { - return ['success' => false, 'message' => $e->getMessage()]; - } - } + return [ + 'success' => true, + 'status' => $status, + 'transaction_id' => (string) ($data['transaction_id'] ?? ''), + ]; + } catch (\Throwable $e) { + return ['success' => false, 'message' => $e->getMessage()]; + } + } - public function refund(string $order_number, float $amount): array { - if (!$this->is_configured()) { - return ['success' => false, 'message' => __('微信支付未配置', 'aksrt')]; - } + public function refund(string $order_number, float $amount, string $request_id = ''): array { + if (!$this->is_configured()) { + return ['success' => false, 'message' => __('微信支付未配置', 'aksrt')]; + } - $config = $this->get_config(); + try { + YansongdaPayFactory::boot_wechat(); + $action = $this->query_action(); - try { - $sdk = $this->create_sdk($config); + $query = Pay::wechat()->query([ + 'out_trade_no' => $order_number, + '_action' => $action, + ]); + $q = YansongdaPayFactory::to_array($query); + $total_fen = (int) YansongdaPayFactory::dig($q, 'amount.total', 0); + $refund_fen = (int) round($amount * 100); + if ($total_fen <= 0) { + $total_fen = $refund_fen; + } + if ($refund_fen <= 0) { + return ['success' => false, 'message' => __('退款金额无效', 'aksrt')]; + } - // 先查询订单获取 transaction_id - $query = new \Yurun\PaySDK\Weixin\OrderQuery\Request; - $query->out_trade_no = $order_number; - $query_result = $sdk->execute($query); + $refund_request_id = sanitize_key($request_id !== '' ? $request_id : 'REF_' . $order_number); + $result = Pay::wechat()->refund([ + '_action' => 'refund_' . $action, + 'out_trade_no' => $order_number, + 'out_refund_no' => $refund_request_id, + 'reason' => __('用户申请退款', 'aksrt'), + 'amount' => [ + 'refund' => $refund_fen, + 'total' => $total_fen, + 'currency' => 'CNY', + ], + ]); + $data = YansongdaPayFactory::to_array($result); - if (!$sdk->checkResult() || empty($query_result['transaction_id'])) { - return ['success' => false, 'message' => __('未找到支付记录', 'aksrt')]; - } + return [ + 'success' => true, + 'refund_id' => (string) ($data['refund_id'] ?? $data['out_refund_no'] ?? ''), + 'message' => __('退款成功', 'aksrt'), + ]; + } catch (\Throwable $e) { + return ['success' => false, 'message' => $e->getMessage()]; + } + } - $refund_fee = (int) ($amount * 100); + /** + * @return array{mch_id:string,appid:string,api_v3_key:string,private_key:string,cert_path:string,payment_type:string} + */ + private function get_config(): array { + $all = class_exists('\AKSRT\Settings\Settings') + ? \AKSRT\Settings\Settings::get_all() + : []; + $type = sanitize_key((string) ($all['wechat_payment_type'] ?? 'native')); + if (!in_array($type, ['native', 'h5'], true)) { + $type = 'native'; + } + return [ + 'mch_id' => (string) ($all['wechat_merchantid'] ?? ''), + 'appid' => (string) ($all['wechat_appid'] ?? ''), + 'api_v3_key' => (string) ($all['wechat_api_v3_key'] ?? ''), + 'private_key' => (string) ($all['wechat_private_key_path'] ?? ''), + 'cert_path' => (string) ($all['wechat_cert_path'] ?? ''), + 'payment_type' => $type, + ]; + } - $request = new \Yurun\PaySDK\Weixin\Refund\Request; - $request->out_trade_no = $order_number; - $request->out_refund_no = 'REF_' . $order_number . '_' . time(); - $request->total_fee = (int) ($query_result['total_fee'] ?? $refund_fee); - $request->refund_fee = $refund_fee; - $request->refund_desc = __('用户申请退款', 'aksrt'); - - $result = $sdk->execute($request); - - if ($sdk->checkResult()) { - return [ - 'success' => true, - 'refund_id' => $result['refund_id'] ?? '', - 'message' => __('退款成功', 'aksrt'), - ]; - } - - return ['success' => false, 'message' => $sdk->getError() ?: __('退款失败', 'aksrt')]; - } catch (\Exception $e) { - return ['success' => false, 'message' => $e->getMessage()]; - } - } - - private function get_config(): array { - if (!class_exists('\AKSRT\Settings\Settings')) { - return []; - } - $all = \AKSRT\Settings\Settings::get_all(); - return [ - 'mch_id' => (string) ($all['wechat_merchantid'] ?? ''), - 'key' => (string) ($all['wechat_jsapi'] ?? ''), - 'appid' => (string) ($all['wechat_appid'] ?? ''), - 'appsecret' => (string) ($all['wechat_appsecret'] ?? ''), - 'cert_path' => (string) ($all['wechat_cert_path'] ?? ''), - 'key_path' => (string) ($all['wechat_key_path'] ?? ''), - 'payment_type' => (string) ($all['wechat_payment_type'] ?? 'native'), - ]; - } - - private function create_sdk(array $config): \Yurun\PaySDK\Weixin\SDK { - $params = new \Yurun\PaySDK\Weixin\Params\PublicParams; - $params->appID = $config['appid'] ?? ''; - $params->mch_id = $config['mch_id'] ?? ''; - $params->key = $config['key'] ?? ''; - $params->sign_type = $config['sign_type'] ?? 'HMAC-SHA256'; - - if (!empty($config['cert_path'])) { - $params->certPath = $config['cert_path']; - } - if (!empty($config['key_path'])) { - $params->keyPath = $config['key_path']; - } - - return new \Yurun\PaySDK\Weixin\SDK($params); - } + private function query_action(): string { + $map = [ + 'native' => 'native', + 'h5' => 'h5', + ]; + $type = $this->get_config()['payment_type']; + return $map[$type] ?? 'native'; + } } diff --git a/src/AKSRT/Payment/Gateways/YansongdaPayFactory.php b/src/AKSRT/Payment/Gateways/YansongdaPayFactory.php new file mode 100644 index 00000000..2e03c46b --- /dev/null +++ b/src/AKSRT/Payment/Gateways/YansongdaPayFactory.php @@ -0,0 +1,275 @@ + $channels Keys: wechat?, alipay? + */ + public static function boot(array $channels): void { + $config = [ + 'logger' => self::logger_config(), + 'http' => [ + 'timeout' => 10.0, + 'connect_timeout' => 5.0, + ], + ]; + + if (!empty($channels['wechat']) && is_array($channels['wechat'])) { + $config['wechat'] = [ + 'default' => $channels['wechat'], + ]; + } + if (!empty($channels['alipay']) && is_array($channels['alipay'])) { + $config['alipay'] = [ + 'default' => $channels['alipay'], + ]; + } + + // Re-config is safe; Pay caches tenant config per process. + Pay::config($config); + } + + /** + * WeChat Pay API v3 merchant config. + * + * @return array + */ + public static function wechat_from_settings(): array { + $all = self::settings(); + $notify = home_url('/wp-json/aksrt/v1/payment/wechatpay/notify'); + + return [ + 'mch_id' => (string) ($all['wechat_merchantid'] ?? ''), + 'mch_secret_key' => (string) ($all['wechat_api_v3_key'] ?? ''), + 'mch_secret_cert' => (string) ($all['wechat_private_key_path'] ?? ''), + 'mch_public_cert_path' => (string) ($all['wechat_cert_path'] ?? ''), + 'mp_app_id' => (string) ($all['wechat_appid'] ?? ''), + 'notify_url' => $notify, + 'mode' => Pay::MODE_NORMAL, + ]; + } + + /** + * Alipay cert-mode config (yansongda/pay 3.x requires cert paths for SN + verify). + * + * app_secret_cert may be PEM string or file path. + * Cert path fields accept absolute file paths (or PEM content via get_public_cert). + * + * @return array + */ + public static function alipay_from_settings(): array { + $all = self::settings(); + $notify = home_url('/wp-json/aksrt/v1/payment/alipay/notify'); + $return = home_url('/wp-json/aksrt/v1/payment/alipay/return'); + + return [ + 'app_id' => (string) ($all['alipay_appid'] ?? ''), + 'app_secret_cert' => (string) ($all['alipay_privatekey'] ?? ''), + 'app_public_cert_path' => (string) ($all['alipay_app_public_cert_path'] ?? ''), + 'alipay_public_cert_path' => (string) ($all['alipay_public_cert_path'] ?? ''), + 'alipay_root_cert_path' => (string) ($all['alipay_root_cert_path'] ?? ''), + 'return_url' => $return, + 'notify_url' => $notify, + 'mode' => Pay::MODE_NORMAL, + ]; + } + + public static function boot_wechat(): void { + self::boot(['wechat' => self::wechat_from_settings()]); + } + + public static function boot_alipay(): void { + self::boot(['alipay' => self::alipay_from_settings()]); + } + + /** + * Convert Collection / ArrayAccess / array to plain array. + * + * @param mixed $data + * @return array + */ + public static function to_array($data): array { + if (is_array($data)) { + return $data; + } + if ($data instanceof \Yansongda\Supports\Collection) { + return $data->all(); + } + if ($data instanceof \ArrayAccess) { + $out = []; + foreach ($data as $k => $v) { + $out[$k] = is_array($v) || $v instanceof \ArrayAccess || $v instanceof \Yansongda\Supports\Collection + ? self::to_array($v) + : $v; + } + return $out; + } + if (is_object($data) && method_exists($data, 'toArray')) { + $arr = $data->toArray(); + return is_array($arr) ? $arr : []; + } + return []; + } + + /** + * Nested value from array using dot path. + * + * @param array $data + * @return mixed + */ + public static function dig(array $data, string $path, $default = null) { + $cur = $data; + foreach (explode('.', $path) as $seg) { + if (!is_array($cur) || !array_key_exists($seg, $cur)) { + return $default; + } + $cur = $cur[$seg]; + } + return $cur; + } + + /** + * Extract a redirect / payment URL from alipay web/wap response. + * + * @param mixed $result + */ + public static function extract_payment_url($result): string { + if (is_string($result) && $result !== '') { + return $result; + } + if ($result instanceof \Psr\Http\Message\ResponseInterface) { + $loc = $result->getHeaderLine('Location'); + if ($loc !== '') { + return $loc; + } + $body = (string) $result->getBody(); + if (preg_match('#action=[\'"]([^\'"]+)[\'"]#i', $body, $m)) { + return html_entity_decode($m[1], ENT_QUOTES); + } + if (preg_match('#https?://[^\s"\'<>]+#', $body, $m)) { + return $m[0]; + } + // Auto-submit HTML form: callers may open as document; store empty payment_url and put HTML in payment_data. + return ''; + } + if (is_object($result)) { + if (method_exists($result, 'getTargetUrl')) { + $url = (string) $result->getTargetUrl(); + if ($url !== '') { + return $url; + } + } + if (method_exists($result, '__toString')) { + $s = (string) $result; + if (strpos($s, 'http') === 0) { + return $s; + } + } + } + $arr = self::to_array($result); + foreach (['payment_url', 'h5_url', 'code_url', 'mweb_url'] as $k) { + if (!empty($arr[$k]) && is_string($arr[$k])) { + return $arr[$k]; + } + } + return ''; + } + + /** + * HTML body from alipay Response (form auto-submit) when no Location header. + * + * @param mixed $result + */ + public static function extract_html_body($result): string { + if ($result instanceof \Psr\Http\Message\ResponseInterface) { + if ($result->getHeaderLine('Location') !== '') { + return ''; + } + return (string) $result->getBody(); + } + return ''; + } + + /** + * Emit SDK success()/fail() ResponseInterface (or string) and exit. + * + * @param mixed $response + */ + public static function emit_response($response, string $fallback_body = 'success'): void { + if ($response instanceof \Psr\Http\Message\ResponseInterface) { + $status = $response->getStatusCode(); + if ($status > 0 && !headers_sent()) { + http_response_code($status); + } + foreach ($response->getHeaders() as $name => $values) { + if (strtolower((string) $name) === 'transfer-encoding') { + continue; + } + foreach ($values as $value) { + header($name . ': ' . $value, false); + } + } + $body = (string) $response->getBody(); + echo $body !== '' ? $body : $fallback_body; + exit; + } + if (is_string($response) && $response !== '') { + echo $response; + exit; + } + echo $fallback_body; + exit; + } + + /** + * @return array + */ + private static function settings(): array { + if (class_exists('\AKSRT\Settings\Settings')) { + return \AKSRT\Settings\Settings::get_all(); + } + return []; + } + + /** + * @return array + */ + private static function logger_config(): array { + $enabled = defined('WP_DEBUG') && WP_DEBUG; + $file = ''; + if ($enabled && function_exists('wp_upload_dir')) { + $uploads = wp_upload_dir(); + if (empty($uploads['error']) && !empty($uploads['basedir'])) { + $dir = trailingslashit((string) $uploads['basedir']) . 'aksrt-pay'; + if (!is_dir($dir)) { + wp_mkdir_p($dir); + } + $file = trailingslashit($dir) . 'pay.log'; + } + } + + return [ + 'enable' => $enabled && $file !== '', + 'file' => $file, + 'level' => 'info', + 'type' => 'single', + 'max_file' => 30, + ]; + } +} diff --git a/src/AKSRT/Settings/PaymentRuntime.php b/src/AKSRT/Settings/PaymentRuntime.php index 85f2a32d..39ee4af0 100644 --- a/src/AKSRT/Settings/PaymentRuntime.php +++ b/src/AKSRT/Settings/PaymentRuntime.php @@ -336,10 +336,6 @@ class PaymentRuntime { 'pid' => sanitize_text_field((string) ($settings['epay_pid'] ?? '')), 'key' => sanitize_text_field((string) ($settings['epay_key'] ?? '')), ], - 'alipay_extra' => [ - 'return_url' => esc_url_raw((string) ($settings['alipay_return_url'] ?? '')), - 'notify_url' => esc_url_raw((string) ($settings['alipay_notify_url'] ?? '')), - ], ]; }