From 872eef4eeed1f35ea72dca45eb341d78cb330e70 Mon Sep 17 00:00:00 2001 From: Anyon Date: Mon, 4 May 2026 13:56:46 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=91=E5=B8=83:=20=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96=20WeChatDeveloper=202.0=20=E5=BC=80=E6=BA=90=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 34 + .github/workflows/release.yml | 77 +- .gitignore | 18 +- AliPay/App.php | 49 - AliPay/Bill.php | 48 - AliPay/Pos.php | 49 - AliPay/Scan.php | 48 - AliPay/Trade.php | 81 -- AliPay/Transfer.php | 79 -- AliPay/Wap.php | 48 - AliPay/Web.php | 48 - license => LICENSE | 0 README.md | 450 ++++++++ We.php | 149 --- WeChat/Card.php | 641 ------------ WeChat/Contracts/BasicAliPay.php | 531 ---------- WeChat/Contracts/BasicPushEvent.php | 242 ----- WeChat/Contracts/BasicWeChat.php | 292 ------ WeChat/Contracts/BasicWePay.php | 280 ----- WeChat/Contracts/BasicWeWork.php | 42 - WeChat/Contracts/DataArray.php | 130 --- WeChat/Contracts/DataError.php | 195 ---- WeChat/Contracts/MyCurlFile.php | 78 -- WeChat/Contracts/Tools.php | 528 ---------- WeChat/Custom.php | 228 ---- WeChat/Draft.php | 123 --- .../Exceptions/InvalidArgumentException.php | 41 - WeChat/Exceptions/InvalidDecryptException.php | 41 - .../Exceptions/InvalidInstanceException.php | 41 - .../Exceptions/InvalidResponseException.php | 42 - WeChat/Exceptions/LocalCacheException.php | 43 - WeChat/Freepublish.php | 95 -- WeChat/Limit.php | 65 -- WeChat/Media.php | 195 ---- WeChat/Menu.php | 103 -- WeChat/Oauth.php | 100 -- WeChat/Pay.php | 232 ----- WeChat/Product.php | 165 --- WeChat/Prpcrypt/ErrorCode.php | 52 - WeChat/Prpcrypt/PKCS7Encoder.php | 45 - WeChat/Prpcrypt/Prpcrypt.php | 97 -- WeChat/Qrcode.php | 75 -- WeChat/Receive.php | 165 --- WeChat/Scan.php | 187 ---- WeChat/Script.php | 116 --- WeChat/Shake.php | 340 ------ WeChat/Tags.php | 119 --- WeChat/Template.php | 104 -- WeChat/User.php | 139 --- WeChat/Wifi.php | 285 ----- WeMini/Crypt.php | 122 --- WeMini/Custom.php | 221 ---- WeMini/Delivery.php | 171 --- WeMini/Guide.php | 527 ---------- WeMini/Image.php | 69 -- WeMini/Insurance.php | 208 ---- WeMini/Live.php | 517 --------- WeMini/Logistics.php | 236 ----- WeMini/Market.php | 54 - WeMini/Media.php | 54 - WeMini/Message.php | 65 -- WeMini/Newtmpl.php | 144 --- WeMini/Ocr.php | 104 -- WeMini/Operation.php | 69 -- WeMini/Plugs.php | 105 -- WeMini/Poi.php | 87 -- WeMini/Qrcode.php | 119 --- WeMini/Scheme.php | 79 -- WeMini/Search.php | 39 - WeMini/Security.php | 67 -- WeMini/Shipping.php | 117 --- WeMini/Shopping.php | 94 -- WeMini/Soter.php | 39 - WeMini/Template.php | 104 -- WeMini/Total.php | 166 --- WeMini/crypt/errorCode.php | 20 - WeMini/crypt/wxBizDataCrypt.php | 57 - WePay/Bill.php | 64 -- WePay/Coupon.php | 66 -- WePay/Custom.php | 68 -- WePay/Order.php | 174 ---- WePay/ProfitSharing.php | 144 --- WePay/Redpack.php | 73 -- WePay/Refund.php | 80 -- WePay/Transfers.php | 62 -- WePay/TransfersBank.php | 125 --- WePayV3/Cert.php | 66 -- WePayV3/Complaints.php | 146 --- WePayV3/Contracts/BasicWePay.php | 535 ---------- WePayV3/Contracts/DecryptAes.php | 81 -- WePayV3/Coupon.php | 155 --- WePayV3/Ecommerce.php | 775 -------------- WePayV3/Order.php | 208 ---- WePayV3/ProfitSharing.php | 114 -- WePayV3/Refund.php | 82 -- WePayV3/Transfers.php | 137 --- _test/alipay-app.php | 46 - _test/alipay-bill.php | 47 - _test/alipay-callapi-test.php | 163 --- _test/alipay-notify.php | 41 - _test/alipay-pos.php | 43 - _test/alipay-refund.php | 41 - _test/alipay-scan.php | 42 - _test/alipay-transfer-account.php | 39 - _test/alipay-transfer-create.php | 46 - _test/alipay-transfer-query.php | 40 - _test/alipay-transfer.php | 45 - _test/alipay-wap.php | 44 - _test/alipay-web.php | 45 - _test/alipay.php | 58 -- _test/alipay/alipayPublicCert.crt | 38 - _test/alipay/alipayRootCert.crt | 88 -- _test/alipay/appPublicCert.crt | 19 - _test/alipay2.php | 58 -- _test/config.php | 52 - _test/mini-login.php | 38 - _test/mini-qrc.php | 39 - _test/pay-callapi-test.php | 169 --- _test/pay-config.php | 54 - _test/pay-download-bill.php | 44 - _test/pay-order-close.php | 41 - _test/pay-order-create.php | 85 -- _test/pay-order-notify.php | 46 - _test/pay-order-query.php | 44 - _test/pay-redpack-create.php | 55 - _test/pay-refund-create.php | 46 - _test/pay-refund-query.php | 46 - _test/pay-transfers-create.php | 50 - _test/pay-transfersbank-create.php | 48 - _test/pay-v3-callapi-test.php | 205 ---- _test/pay-v3-config-cert.php | 31 - _test/pay-v3-config.php | 92 -- _test/pay-v3-order-app.php | 64 -- _test/pay-v3-order-h5.php | 50 - _test/pay-v3-order-jsapi.php | 89 -- _test/pay-v3-order-native.php | 82 -- _test/pay-v3-transfer.php | 48 - _test/wechat-callapi-test.php | 139 --- _test/wechat-jssdk-sign.php | 40 - _test/wechat-menu-get.php | 40 - _test/wechat-qrcode-create.php | 44 - _test/wechat-user-get.php | 47 - _test/work-config.php | 47 - _test/work-department.php | 32 - composer.json | 61 +- helper.php | 25 - include.php | 31 - phpunit.xml | 22 + readme.md | 978 ------------------ src/Client.php | 180 ++++ src/Config/AlipayPaymentConfig.php | 9 + src/Config/AlipayPlatformConfig.php | 51 + src/Config/WechatPaymentConfig.php | 56 + src/Config/WechatPlatformConfig.php | 51 + src/Config/WechatServiceConfig.php | 50 + src/Config/WechatWxappConfig.php | 39 + src/Contract/ConfigInterface.php | 21 + src/Contract/StoreCacheInterface.php | 35 + src/Contract/StoreTokenInterface.php | 23 + src/Exception/ApiException.php | 7 + src/Exception/SignatureException.php | 7 + src/Exception/WechatException.php | 31 + src/Platform/Alipay/PaymentClient.php | 87 ++ src/Platform/Alipay/PlatformClient.php | 159 +++ src/Platform/Wechat/PaymentClient.php | 142 +++ src/Platform/Wechat/PlatformClient.php | 179 ++++ src/Platform/Wechat/ServiceClient.php | 249 +++++ src/Platform/Wechat/WxappClient.php | 123 +++ src/Support/CacheKey.php | 46 + src/Support/FileCacheStore.php | 132 +++ src/Support/JsonClient.php | 52 + src/Support/MessageCrypto.php | 80 ++ src/Support/NullCacheStore.php | 27 + src/Support/PayCrypto.php | 44 + src/Support/PsrSimpleCacheStore.php | 47 + src/Support/Signature.php | 56 + src/Support/TokenCacheKey.php | 57 + src/Support/Xml.php | 62 ++ src/Tests/AccessTokenCacheTest.php | 130 +++ src/Tests/CacheKeyTest.php | 49 + src/Tests/CacheStoreTest.php | 162 +++ src/Tests/ClientTest.php | 98 ++ src/Tests/ConfigInterfaceTest.php | 60 ++ src/Tests/MessageCryptoTest.php | 33 + src/Tests/PayCryptoTest.php | 31 + tests/bootstrap.php | 27 + 186 files changed, 3268 insertions(+), 17575 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 AliPay/App.php delete mode 100644 AliPay/Bill.php delete mode 100644 AliPay/Pos.php delete mode 100644 AliPay/Scan.php delete mode 100644 AliPay/Trade.php delete mode 100644 AliPay/Transfer.php delete mode 100644 AliPay/Wap.php delete mode 100644 AliPay/Web.php rename license => LICENSE (100%) create mode 100644 README.md delete mode 100644 We.php delete mode 100644 WeChat/Card.php delete mode 100644 WeChat/Contracts/BasicAliPay.php delete mode 100644 WeChat/Contracts/BasicPushEvent.php delete mode 100644 WeChat/Contracts/BasicWeChat.php delete mode 100644 WeChat/Contracts/BasicWePay.php delete mode 100644 WeChat/Contracts/BasicWeWork.php delete mode 100644 WeChat/Contracts/DataArray.php delete mode 100644 WeChat/Contracts/DataError.php delete mode 100644 WeChat/Contracts/MyCurlFile.php delete mode 100644 WeChat/Contracts/Tools.php delete mode 100644 WeChat/Custom.php delete mode 100644 WeChat/Draft.php delete mode 100644 WeChat/Exceptions/InvalidArgumentException.php delete mode 100644 WeChat/Exceptions/InvalidDecryptException.php delete mode 100644 WeChat/Exceptions/InvalidInstanceException.php delete mode 100644 WeChat/Exceptions/InvalidResponseException.php delete mode 100644 WeChat/Exceptions/LocalCacheException.php delete mode 100644 WeChat/Freepublish.php delete mode 100644 WeChat/Limit.php delete mode 100644 WeChat/Media.php delete mode 100644 WeChat/Menu.php delete mode 100644 WeChat/Oauth.php delete mode 100644 WeChat/Pay.php delete mode 100644 WeChat/Product.php delete mode 100644 WeChat/Prpcrypt/ErrorCode.php delete mode 100644 WeChat/Prpcrypt/PKCS7Encoder.php delete mode 100644 WeChat/Prpcrypt/Prpcrypt.php delete mode 100644 WeChat/Qrcode.php delete mode 100644 WeChat/Receive.php delete mode 100644 WeChat/Scan.php delete mode 100644 WeChat/Script.php delete mode 100644 WeChat/Shake.php delete mode 100644 WeChat/Tags.php delete mode 100644 WeChat/Template.php delete mode 100644 WeChat/User.php delete mode 100644 WeChat/Wifi.php delete mode 100644 WeMini/Crypt.php delete mode 100644 WeMini/Custom.php delete mode 100644 WeMini/Delivery.php delete mode 100644 WeMini/Guide.php delete mode 100644 WeMini/Image.php delete mode 100644 WeMini/Insurance.php delete mode 100644 WeMini/Live.php delete mode 100644 WeMini/Logistics.php delete mode 100644 WeMini/Market.php delete mode 100644 WeMini/Media.php delete mode 100644 WeMini/Message.php delete mode 100644 WeMini/Newtmpl.php delete mode 100644 WeMini/Ocr.php delete mode 100644 WeMini/Operation.php delete mode 100644 WeMini/Plugs.php delete mode 100644 WeMini/Poi.php delete mode 100644 WeMini/Qrcode.php delete mode 100644 WeMini/Scheme.php delete mode 100644 WeMini/Search.php delete mode 100644 WeMini/Security.php delete mode 100644 WeMini/Shipping.php delete mode 100644 WeMini/Shopping.php delete mode 100644 WeMini/Soter.php delete mode 100644 WeMini/Template.php delete mode 100644 WeMini/Total.php delete mode 100644 WeMini/crypt/errorCode.php delete mode 100644 WeMini/crypt/wxBizDataCrypt.php delete mode 100644 WePay/Bill.php delete mode 100644 WePay/Coupon.php delete mode 100644 WePay/Custom.php delete mode 100644 WePay/Order.php delete mode 100644 WePay/ProfitSharing.php delete mode 100644 WePay/Redpack.php delete mode 100644 WePay/Refund.php delete mode 100644 WePay/Transfers.php delete mode 100644 WePay/TransfersBank.php delete mode 100644 WePayV3/Cert.php delete mode 100644 WePayV3/Complaints.php delete mode 100644 WePayV3/Contracts/BasicWePay.php delete mode 100644 WePayV3/Contracts/DecryptAes.php delete mode 100644 WePayV3/Coupon.php delete mode 100644 WePayV3/Ecommerce.php delete mode 100644 WePayV3/Order.php delete mode 100644 WePayV3/ProfitSharing.php delete mode 100644 WePayV3/Refund.php delete mode 100644 WePayV3/Transfers.php delete mode 100644 _test/alipay-app.php delete mode 100644 _test/alipay-bill.php delete mode 100644 _test/alipay-callapi-test.php delete mode 100644 _test/alipay-notify.php delete mode 100644 _test/alipay-pos.php delete mode 100644 _test/alipay-refund.php delete mode 100644 _test/alipay-scan.php delete mode 100644 _test/alipay-transfer-account.php delete mode 100644 _test/alipay-transfer-create.php delete mode 100644 _test/alipay-transfer-query.php delete mode 100644 _test/alipay-transfer.php delete mode 100644 _test/alipay-wap.php delete mode 100644 _test/alipay-web.php delete mode 100644 _test/alipay.php delete mode 100644 _test/alipay/alipayPublicCert.crt delete mode 100644 _test/alipay/alipayRootCert.crt delete mode 100644 _test/alipay/appPublicCert.crt delete mode 100644 _test/alipay2.php delete mode 100644 _test/config.php delete mode 100644 _test/mini-login.php delete mode 100644 _test/mini-qrc.php delete mode 100644 _test/pay-callapi-test.php delete mode 100644 _test/pay-config.php delete mode 100644 _test/pay-download-bill.php delete mode 100644 _test/pay-order-close.php delete mode 100644 _test/pay-order-create.php delete mode 100644 _test/pay-order-notify.php delete mode 100644 _test/pay-order-query.php delete mode 100644 _test/pay-redpack-create.php delete mode 100644 _test/pay-refund-create.php delete mode 100644 _test/pay-refund-query.php delete mode 100644 _test/pay-transfers-create.php delete mode 100644 _test/pay-transfersbank-create.php delete mode 100644 _test/pay-v3-callapi-test.php delete mode 100644 _test/pay-v3-config-cert.php delete mode 100644 _test/pay-v3-config.php delete mode 100644 _test/pay-v3-order-app.php delete mode 100644 _test/pay-v3-order-h5.php delete mode 100644 _test/pay-v3-order-jsapi.php delete mode 100644 _test/pay-v3-order-native.php delete mode 100644 _test/pay-v3-transfer.php delete mode 100644 _test/wechat-callapi-test.php delete mode 100644 _test/wechat-jssdk-sign.php delete mode 100644 _test/wechat-menu-get.php delete mode 100644 _test/wechat-qrcode-create.php delete mode 100644 _test/wechat-user-get.php delete mode 100644 _test/work-config.php delete mode 100644 _test/work-department.php delete mode 100644 helper.php delete mode 100644 include.php create mode 100644 phpunit.xml delete mode 100644 readme.md create mode 100644 src/Client.php create mode 100644 src/Config/AlipayPaymentConfig.php create mode 100644 src/Config/AlipayPlatformConfig.php create mode 100644 src/Config/WechatPaymentConfig.php create mode 100644 src/Config/WechatPlatformConfig.php create mode 100644 src/Config/WechatServiceConfig.php create mode 100644 src/Config/WechatWxappConfig.php create mode 100644 src/Contract/ConfigInterface.php create mode 100644 src/Contract/StoreCacheInterface.php create mode 100644 src/Contract/StoreTokenInterface.php create mode 100644 src/Exception/ApiException.php create mode 100644 src/Exception/SignatureException.php create mode 100644 src/Exception/WechatException.php create mode 100644 src/Platform/Alipay/PaymentClient.php create mode 100644 src/Platform/Alipay/PlatformClient.php create mode 100644 src/Platform/Wechat/PaymentClient.php create mode 100644 src/Platform/Wechat/PlatformClient.php create mode 100644 src/Platform/Wechat/ServiceClient.php create mode 100644 src/Platform/Wechat/WxappClient.php create mode 100644 src/Support/CacheKey.php create mode 100644 src/Support/FileCacheStore.php create mode 100644 src/Support/JsonClient.php create mode 100644 src/Support/MessageCrypto.php create mode 100644 src/Support/NullCacheStore.php create mode 100644 src/Support/PayCrypto.php create mode 100644 src/Support/PsrSimpleCacheStore.php create mode 100644 src/Support/Signature.php create mode 100644 src/Support/TokenCacheKey.php create mode 100644 src/Support/Xml.php create mode 100644 src/Tests/AccessTokenCacheTest.php create mode 100644 src/Tests/CacheKeyTest.php create mode 100644 src/Tests/CacheStoreTest.php create mode 100644 src/Tests/ClientTest.php create mode 100644 src/Tests/ConfigInterfaceTest.php create mode 100644 src/Tests/MessageCryptoTest.php create mode 100644 src/Tests/PayCryptoTest.php create mode 100644 tests/bootstrap.php diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..cf9807c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,34 @@ +name: CI + +on: + push: + branches: + - master + - '2.0' + pull_request: + +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.4' + extensions: json, openssl, simplexml + coverage: none + + - name: Validate composer metadata + run: composer validate --strict + + - name: Install dependencies + run: composer install --no-interaction --prefer-dist --no-progress + + - name: Run tests + run: composer test diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 20e0bad..5f50da2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,11 +1,12 @@ +name: Create Release + on: push: - # Sequence of patterns matched against refs/tags tags: - - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 + - 'v*' -name: Create Release -permissions: write-all +permissions: + contents: write jobs: release: @@ -16,50 +17,34 @@ jobs: with: fetch-depth: 0 - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 18 - - - name: Install dependencies - run: npm install -g gen-git-log - - - name: Find Last Tag - id: last_tag + - name: Resolve previous tag + id: tags + shell: bash run: | - - # 获取所有标签,按版本排序(降序) - Tags=$(git tag --list --sort=-version:refname) - - # 获取最新的标签(即列表中的第一个) - LATEST_TAG=$(echo "$Tags" | awk 'NR==1 {print $1; exit}') - - # 获取倒数第二个标签(如果存在) - if [[ -n "$Tags" ]]; then - # 使用 tail 获取除了最后一个标签之外的所有标签,然后用 head 获取第一个 - SECOND_LATEST_TAG=$(echo "$Tags" | tail -n +2 | head -n 1) - else - SECOND_LATEST_TAG="" + current="${GITHUB_REF_NAME}" + previous="$(git tag --list 'v*' --sort=-version:refname | grep -v "^${current}$" | head -n 1 || true)" + echo "current=${current}" >> "$GITHUB_OUTPUT" + echo "previous=${previous}" >> "$GITHUB_OUTPUT" + + - name: Build release notes + shell: bash + run: | + current="${{ steps.tags.outputs.current }}" + previous="${{ steps.tags.outputs.previous }}" + if [ -n "$previous" ]; then + git log --pretty='- %s (%h)' "${previous}..${current}" > RELEASE_NOTES.md + else + git log --pretty='- %s (%h)' "${current}" > RELEASE_NOTES.md + fi + if [ ! -s RELEASE_NOTES.md ]; then + echo "- Release ${current}" > RELEASE_NOTES.md fi - - # 设置输出变量 - echo "::set-output name=tag_last::${LATEST_TAG:-v1.0.0}" - echo "::set-output name=tag_second::${SECOND_LATEST_TAG:-v1.0.0}" - - name: Generate Release Notes - run: | - rm -rf log - newTag=${{ steps.last_tag.outputs.tag_last }} - git-log -m tag -f -S ${{ steps.last_tag.outputs.tag_second }} -v ${newTag#v} - - - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Create GitHub release + uses: softprops/action-gh-release@v2 with: - tag_name: ${{ steps.last_tag.outputs.tag_last }} - release_name: Release ${{ steps.last_tag.outputs.tag_last }} - body_path: log/${{steps.last_tag.outputs.tag_last}}.md + tag_name: ${{ steps.tags.outputs.current }} + name: Release ${{ steps.tags.outputs.current }} + body_path: RELEASE_NOTES.md draft: false - prerelease: false \ No newline at end of file + prerelease: false diff --git a/.gitignore b/.gitignore index 7791a86..385f1ac 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,9 @@ -/.git -/.idea -/.DS_Store -/vendor -/Cache -/Test/cert -/nbproject -/composer.lock -/_test/cert \ No newline at end of file +.DS_Store +.idea/ +vendor/ +composer.lock +.phpunit.cache/ +Cache/ +Test/cert/ +_test/cert/ +nbproject/ diff --git a/AliPay/App.php b/AliPay/App.php deleted file mode 100644 index 9cc3c3f..0000000 --- a/AliPay/App.php +++ /dev/null @@ -1,49 +0,0 @@ -options->set('method', 'alipay.trade.app.pay'); - $this->params->set('product_code', 'QUICK_MSECURITY_PAY'); - } - - /** - * 生成APP支付参数字符串 - * @param array $options 订单参数(out_trade_no, total_amount, subject等) - * @return string URL编码后的参数字符串(用于APP调起支付) - */ - public function apply($options) - { - $this->applyData($options); - return http_build_query($this->options->get()); - } -} \ No newline at end of file diff --git a/AliPay/Bill.php b/AliPay/Bill.php deleted file mode 100644 index 507e365..0000000 --- a/AliPay/Bill.php +++ /dev/null @@ -1,48 +0,0 @@ -options->set('method', 'alipay.data.dataservice.bill.downloadurl.query'); - } - - /** - * 获取账单下载地址 - * @param array $options 账单参数(bill_type, bill_date 等) - * @return array 包含 bill_download_url 的响应数据 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function apply($options) - { - return $this->getResult($options); - } -} \ No newline at end of file diff --git a/AliPay/Pos.php b/AliPay/Pos.php deleted file mode 100644 index babd574..0000000 --- a/AliPay/Pos.php +++ /dev/null @@ -1,49 +0,0 @@ -options->set('method', 'alipay.trade.pay'); - $this->params->set('product_code', 'FACE_TO_FACE_PAYMENT'); - } - - /** - * 刷卡支付(条码/声波)下单 - * @param array $options 订单参数(out_trade_no, auth_code, subject, total_amount 等) - * @return array 支付结果 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function apply($options) - { - return $this->getResult($options); - } -} \ No newline at end of file diff --git a/AliPay/Scan.php b/AliPay/Scan.php deleted file mode 100644 index 9a80548..0000000 --- a/AliPay/Scan.php +++ /dev/null @@ -1,48 +0,0 @@ -options->set('method', 'alipay.trade.precreate'); - } - - /** - * 预创建扫码支付订单(返回二维码地址) - * @param array $options 订单参数(out_trade_no, subject, total_amount 等) - * @return array 包含 qr_code 的预下单结果 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function apply($options) - { - return $this->getResult($options); - } -} \ No newline at end of file diff --git a/AliPay/Trade.php b/AliPay/Trade.php deleted file mode 100644 index 0c7b76b..0000000 --- a/AliPay/Trade.php +++ /dev/null @@ -1,81 +0,0 @@ -options->set('method', $method); - return $this; - } - - /** - * 获取当前交易接口方法名 - * @return string - */ - public function getMethod() - { - return $this->options->get('method'); - } - - /** - * 设置公共参数(透传到 options) - * @param array $option key-value 公共参数 - * @return Trade - */ - public function setOption($option = []) - { - foreach ($option as $key => $vo) { - $this->options->set($key, $vo); - } - return $this; - } - - /** - * 获取当前公共参数 - * @return array|string|null - */ - public function getOption() - { - return $this->options->get(); - } - - /** - * 执行当前设置的交易接口 - * @param array $options 业务参数(写入 biz_content) - * @return array 接口返回结果 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function apply($options) - { - return $this->getResult($options); - } -} \ No newline at end of file diff --git a/AliPay/Transfer.php b/AliPay/Transfer.php deleted file mode 100644 index 94d0a6a..0000000 --- a/AliPay/Transfer.php +++ /dev/null @@ -1,79 +0,0 @@ -options->set('method', 'alipay.fund.trans.toaccount.transfer'); - return $this->getResult($options); - } - - /** - * 新版:统一转账接口(uni.transfer) - * @param array $options 转账参数(out_biz_no, trans_amount, product_code, payee_info 等) - * @return array 转账结果 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function create($options = []) - { - $this->options->set('method', 'alipay.fund.trans.uni.transfer'); - return $this->getResult($options); - } - - /** - * 新版:转账业务单据查询 - * @param array $options 查询参数(out_biz_no 或 order_id) - * @return array 查询结果 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function queryResult($options = []) - { - $this->options->set('method', 'alipay.fund.trans.common.query'); - return $this->getResult($options); - } - - /** - * 新版:资金账户余额查询 - * @param array $options 查询参数(alipay_user_id 或 user_id,可选 account_type) - * @return array 账户余额等信息 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function queryAccount($options = []) - { - $this->options->set('method', 'alipay.fund.account.query'); - return $this->getResult($options); - } -} \ No newline at end of file diff --git a/AliPay/Wap.php b/AliPay/Wap.php deleted file mode 100644 index 824e787..0000000 --- a/AliPay/Wap.php +++ /dev/null @@ -1,48 +0,0 @@ -options->set('method', 'alipay.trade.wap.pay'); - $this->params->set('product_code', 'QUICK_WAP_WAY'); - } - - /** - * 生成 WAP 支付表单 HTML - * @param array $options 订单参数(out_trade_no, total_amount, subject, quit_url, return_url 等) - * @return string 自动提交的支付表单 HTML - */ - public function apply($options) - { - parent::applyData($options); - return $this->buildPayHtml(); - } -} \ No newline at end of file diff --git a/AliPay/Web.php b/AliPay/Web.php deleted file mode 100644 index 241160b..0000000 --- a/AliPay/Web.php +++ /dev/null @@ -1,48 +0,0 @@ -options->set('method', 'alipay.trade.page.pay'); - $this->params->set('product_code', 'FAST_INSTANT_TRADE_PAY'); - } - - /** - * 生成网站支付表单HTML - * @param array $options 订单参数(out_trade_no, total_amount, subject等) - * @return string 支付表单HTML(自动提交到支付宝) - */ - public function apply($options) - { - parent::applyData($options); - return $this->buildPayHtml(); - } -} \ No newline at end of file diff --git a/license b/LICENSE similarity index 100% rename from license rename to LICENSE diff --git a/README.md b/README.md new file mode 100644 index 0000000..9086729 --- /dev/null +++ b/README.md @@ -0,0 +1,450 @@ +# WeChatDeveloper + +WeChatDeveloper 是一个面向 **微信** 与 **支付宝** 的轻量 PHP SDK,根命名空间为 `We`。 + +它只维护基础认证、通用 HTTP 调用、签名验签、回调解密和统一异常,不内置业务表结构、不绑定具体框架,也不维护海量接口别名。业务系统按官方文档传入接口 path、参数和配置即可。 + +## 特性 + +- 支持微信公众号、小程序、微信开放平台、微信支付 APIv3。 +- 支持支付宝开放平台与支付网关调用。 +- 统一入口 `We\Client`,按通道创建客户端。 +- 配置对象实现 `ConfigInterface`,构造时完成基础校验。 +- 缓存只依赖 `StoreCacheInterface`,可用于单机文件缓存或集群 Redis 适配。 +- 开放平台授权方 refresh token 通过 `StoreTokenInterface` 由业务系统存取。 +- 返回值默认是数组,失败时抛出 `WechatException` 或其子类。 + +## 环境要求 + +- PHP `>= 8.4` +- `ext-json` +- `ext-openssl` +- `ext-simplexml` +- `guzzlehttp/guzzle` +- `psr/simple-cache` + +## 安装 + +稳定版发布后: + +```bash +composer require zoujingli/wechat-developer:^2.0 +``` + +开发版: + +```bash +composer require zoujingli/wechat-developer:2.0.x-dev +``` + +源码开发: + +```bash +cd WeChatDeveloper +composer install +composer test +``` + +## 快速开始 + +```php +wechatPlatform(new WechatPlatformConfig( + appid: 'wx_appid', + appSecret: 'app_secret', +)); + +$users = $official->get('cgi-bin/user/get', [ + 'next_openid' => '', +]); +``` + +`post()`、`get()`、`call()` 的 path 与官方文档保持一致,通常不需要前导 `/`。 + +```php +$menu = $official->post('cgi-bin/menu/create', [ + 'button' => [ + [ + 'type' => 'click', + 'name' => '今日推荐', + 'key' => 'TODAY', + ], + ], +]); +``` + +## 入口 Client + +```php +use GuzzleHttp\ClientInterface; +use We\Client; +use We\Contract\StoreCacheInterface; +use We\Contract\StoreTokenInterface; + +new Client( + ?StoreCacheInterface $cache = null, + ?StoreTokenInterface $authorizers = null, + ?ClientInterface $http = null, + string $cacheKeyPrefix = Client::DEFAULT_CACHE_KEY_PREFIX, +); +``` + +参数说明: + +| 参数 | 说明 | +|------|------| +| `$cache` | SDK 运行态缓存,主要保存 access token;不传时默认使用 `FileCacheStore`。 | +| `$authorizers` | 微信开放平台代调用时,读取和回写授权方 refresh token。 | +| `$http` | 可注入自定义 Guzzle HTTP 客户端,便于统一超时、代理或单元测试。 | +| `$cacheKeyPrefix` | 缓存键前缀,默认 `wechat_developer`;生产环境建议按项目设置。 | + +也可以用字符串通道创建客户端: + +```php +$official = $client->get('wechat.platform', WechatPlatformConfig::fromArray($config)); +``` + +支持的通道: + +| 通道 | 工厂方法 | 配置对象 | +|------|----------|----------| +| `wechat.platform` | `wechatPlatform()` | `WechatPlatformConfig` | +| `wechat.wxapp` | `wechatWxapp()` | `WechatWxappConfig` | +| `wechat.service` | `wechatService()` | `WechatServiceConfig` | +| `wechat.payment` | `wechatPayment()` | `WechatPaymentConfig` | +| `alipay.platform` | `alipayPlatform()` | `AlipayPlatformConfig` | +| `alipay.payment` | `alipayPayment()` | `AlipayPaymentConfig` | + +## 配置对象 + +所有配置对象都实现 `ConfigInterface`: + +```php +interface ConfigInterface +{ + public static function fromArray(array $data): static; + + public function validate(): void; +} +``` + +构造函数会调用 `validate()`。缺少必填字段时会抛出 `WechatException`。 + +示例: + +```php +use We\Config\WechatWxappConfig; + +$config = WechatWxappConfig::fromArray([ + 'appid' => 'wx_appid', + 'appsecret' => 'app_secret', + 'storage_scope' => 'tenant:1', +]); +``` + +`storage_scope` 是可选的缓存分桶标识。同一个 appid 在多租户或多业务账号下需要隔离 token 时可以设置。 + +## 缓存与锁 + +access token 统一通过 `StoreCacheInterface` 存储: + +```php +interface StoreCacheInterface +{ + public function get(string $key, mixed $default = null): mixed; + + public function set(string $key, mixed $value, int $ttl): void; + + public function del(string $key): void; + + public function lock(string $key, int $ttl, callable $callback): mixed; +} +``` + +内置实现: + +| 类 | 说明 | +|----|------| +| `We\Support\FileCacheStore` | 默认单机文件缓存,使用 JSON 文件保存数据,刷新锁使用 `flock`。 | +| `We\Support\PsrSimpleCacheStore` | PSR-16 缓存适配器,适合 Redis、Memcached 或框架缓存。锁能力需由构造参数注入。 | +| `We\Support\NullCacheStore` | 空缓存,适合测试或明确不缓存的场景。 | + +单机文件缓存: + +```php +use We\Client; +use We\Support\FileCacheStore; + +$client = new Client( + cache: new FileCacheStore(__DIR__ . '/runtime/wechat-cache'), + cacheKeyPrefix: 'my_app', +); +``` + +PSR-16 缓存: + +```php +use Psr\SimpleCache\CacheInterface; +use We\Client; +use We\Support\PsrSimpleCacheStore; + +/** @var CacheInterface $cache */ +$client = new Client( + cache: new PsrSimpleCacheStore( + cache: $cache, + locker: static function (string $key, int $ttl, callable $callback): mixed { + // 在这里接入 Redis SET NX、框架锁或其他原子锁。 + return $callback(); + }, + ), +); +``` + +如果使用 `PsrSimpleCacheStore` 且未配置锁回调,调用 `lock()` 会抛出明确异常。集群部署时必须使用共享缓存和可用的原子锁,避免并发刷新 access token。 + +缓存键固定为: + +```text +{cacheKeyPrefix}:{channel}:{logicalKey} +``` + +例如: + +```text +my_app:wechat.platform:wechat:app:wx123:official:access_token +``` + +完整键由 `CacheKey` 生成,微信 token 逻辑键由 `TokenCacheKey` 生成。 + +## 微信开放平台授权方 Token + +开放平台代调用授权方接口时,SDK 需要读取授权方 refresh token,并在刷新后把新 token 回写业务存储。业务系统实现 `StoreTokenInterface` 即可: + +```php +use We\Contract\StoreTokenInterface; + +final class AuthorizerTokenStore implements StoreTokenInterface +{ + public function refreshToken(string $authorizerAppid): string + { + // 从数据库读取 authorizer_refresh_token。 + return 'authorizer_refresh_token'; + } + + public function saveAuthorizerToken(string $authorizerAppid, array $payload): void + { + // 保存 authorizer_access_token、authorizer_refresh_token、expires_in 等平台返回数据。 + } +} +``` + +注入到根 Client: + +```php +$client = new Client( + cache: $cache, + authorizers: new AuthorizerTokenStore(), +); +``` + +## 微信公众号 + +```php +use We\Config\WechatPlatformConfig; + +$official = $client->wechatPlatform(new WechatPlatformConfig( + appid: 'wx_appid', + appSecret: 'app_secret', + token: 'message_token', + encodingAesKey: 'encoding_aes_key', +)); + +$accessToken = $official->accessToken(); + +$result = $official->post('cgi-bin/message/custom/send', [ + 'touser' => 'openid', + 'msgtype' => 'text', + 'text' => ['content' => 'hello'], +]); +``` + +安全模式消息解密: + +```php +$plain = $official->post('decrypt_message', [ + 'body' => $rawBody, + 'msg_signature' => $signature, + 'timestamp' => $timestamp, + 'nonce' => $nonce, +]); +``` + +## 微信小程序 + +```php +use We\Config\WechatWxappConfig; + +$wxapp = $client->wechatWxapp(new WechatWxappConfig( + appid: 'wx_appid', + appSecret: 'app_secret', +)); + +$result = $wxapp->get('sns/jscode2session', [ + 'js_code' => 'login_code', + 'grant_type' => 'authorization_code', +]); +``` + +## 微信开放平台 + +```php +use We\Config\WechatServiceConfig; + +$service = $client->wechatService(new WechatServiceConfig( + componentAppid: 'component_appid', + componentAppSecret: 'component_secret', + componentToken: 'component_token', + componentEncodingAesKey: 'component_encoding_aes_key', +)); + +$componentToken = $service->componentAccessToken($componentVerifyTicket); +$preAuth = $service->createPreAuthCode($componentToken); +$url = $service->authorizationUrl((string) $preAuth['pre_auth_code'], $redirectUri); +``` + +代授权方调用: + +```php +$result = $service->post('cgi-bin/menu/get', [], [ + 'authorizer_appid' => 'authorizer_appid', + 'component_access_token' => $componentToken, +]); +``` + +## 微信支付 APIv3 + +```php +use We\Config\WechatPaymentConfig; + +$payment = $client->wechatPayment(new WechatPaymentConfig( + appid: 'wx_appid', + mchId: 'mch_id', + apiV3Key: 'api_v3_key', + merchantSerial: 'merchant_serial', + merchantPrivateKey: file_get_contents(__DIR__ . '/apiclient_key.pem'), + platformPublicKey: file_get_contents(__DIR__ . '/wechatpay_public.pem'), + platformSerial: 'platform_serial', +)); + +$refund = $payment->post('v3/refund/domestic/refunds', [ + 'out_trade_no' => 'T202605020001', + 'out_refund_no' => 'R202605020001', + 'amount' => [ + 'refund' => 1, + 'total' => 1, + 'currency' => 'CNY', + ], +]); +``` + +回调验签与解密: + +```php +$data = $payment->post('decrypt_notification', [], [ + 'headers' => $headers, + 'body' => $body, +]); +``` + +## 支付宝 + +```php +use We\Config\AlipayPlatformConfig; + +$alipay = $client->alipayPlatform(new AlipayPlatformConfig( + appid: 'app_id', + privateKey: file_get_contents(__DIR__ . '/merchant_private_key.pem'), + alipayPublicKey: file_get_contents(__DIR__ . '/alipay_public_key.pem'), +)); + +$auth = $alipay->get('auth', [ + 'redirect_uri' => 'https://example.com/callback', + 'scope' => 'auth_user', + 'state' => 'state', +]); + +$response = $alipay->post('alipay.user.info.share', [], [ + 'auth_token' => 'auth_token', +]); +``` + +支付宝支付: + +```php +use We\Config\AlipayPaymentConfig; + +$pay = $client->alipayPayment(AlipayPaymentConfig::fromArray([ + 'appid' => 'app_id', + 'private_key' => file_get_contents(__DIR__ . '/merchant_private_key.pem'), + 'alipay_public_key' => file_get_contents(__DIR__ . '/alipay_public_key.pem'), +])); + +$page = $pay->post('page', [ + 'out_trade_no' => 'P202605020001', + 'total_amount' => '0.01', + 'subject' => 'Test Order', + 'product_code' => 'FAST_INSTANT_TRADE_PAY', +]); +``` + +## 异常处理 + +SDK 抛出的异常基类为: + +```php +We\Exception\WechatException +``` + +签名相关异常使用: + +```php +We\Exception\SignatureException +``` + +建议在业务边界统一捕获并转换成应用自己的响应格式。不要把 app secret、access token、private key、回调密文等敏感内容写入日志。 + +## 测试 + +```bash +cd WeChatDeveloper +composer test +``` + +或在项目根目录: + +```bash +vendor/bin/phpunit -c phpunit.xml +``` + +## 设计边界 + +WeChatDeveloper 只处理协议层和 HTTP 编排: + +- 不提供账号、租户、菜单草稿、订单、授权记录等业务表。 +- 不托管密钥加密存储。 +- 不绑定 Hyperf、Laravel、Symfony 等框架。 +- 不保证覆盖每一个官方接口别名,通用接口通过官方 path 调用。 + +这种边界使 SDK 更适合作为开源底层包,被后台系统、SaaS 平台或命令行工具组合使用。 + +## License + +MIT diff --git a/We.php b/We.php deleted file mode 100644 index 5bcb5d8..0000000 --- a/We.php +++ /dev/null @@ -1,149 +0,0 @@ - - * @date 2018/05/24 13:23 - * - * ----- AliPay ---- - * @method \AliPay\App AliPayApp($options) static 支付宝App支付网关 - * @method \AliPay\Bill AliPayBill($options) static 支付宝电子面单下载 - * @method \AliPay\Pos AliPayPos($options) static 支付宝刷卡支付 - * @method \AliPay\Scan AliPayScan($options) static 支付宝扫码支付 - * @method \AliPay\Trade AliPayTrade($options) static 支付宝标准接口 - * @method \AliPay\Transfer AliPayTransfer($options) static 支付宝转账到账户 - * @method \AliPay\Wap AliPayWap($options) static 支付宝手机网站支付 - * @method \AliPay\Web AliPayWeb($options) static 支付宝网站支付 - * - * ----- WeChat ----- - * @method \WeChat\Card WeChatCard($options = []) static 微信卡券管理 - * @method \WeChat\Custom WeChatCustom($options = []) static 微信客服消息 - * @method \WeChat\Limit WeChatLimit($options = []) static 接口调用频次限制 - * @method \WeChat\Media WeChatMedia($options = []) static 微信素材管理 - * @method \WeChat\Menu WeChatMenu($options = []) static 微信菜单管理 - * @method \WeChat\Oauth WeChatOauth($options = []) static 微信网页授权 - * @method \WeChat\Pay WeChatPay($options = []) static 微信支付商户 - * @method \WeChat\Product WeChatProduct($options = []) static 微信商店管理 - * @method \WeChat\Qrcode WeChatQrcode($options = []) static 微信二维码管理 - * @method \WeChat\Receive WeChatReceive($options = [], $showEchoStr = true) static 微信推送管理 - * @method \WeChat\Scan WeChatScan($options = []) static 微信扫一扫接入管理 - * @method \WeChat\Script WeChatScript($options = []) static 微信前端支持 - * @method \WeChat\Shake WeChatShake($options = []) static 微信揺一揺周边 - * @method \WeChat\Tags WeChatTags($options = []) static 微信用户标签管理 - * @method \WeChat\Template WeChatTemplate($options = []) static 微信模板消息 - * @method \WeChat\User WeChatUser($options = []) static 微信粉丝管理 - * @method \WeChat\Wifi WeChatWifi($options = []) static 微信门店WIFI管理 - * @method \WeChat\Draft WeChatDraft($options = []) static 微信草稿箱 - * @method \WeChat\Freepublish WeChatFreepublish($options = []) static 微信发布能力 - * - * ----- WeMini ----- - * @method \WeMini\Crypt WeMiniCrypt($options = []) static 小程序数据加密处理 - * @method \WeMini\Delivery WeMiniDelivery($options = []) static 小程序即时配送 - * @method \WeMini\Shipping WeMiniShipping($options = []) static 小程序发货信息 - * @method \WeMini\Guide WeMiniGuide($options = []) static 小程序导购助手 - * @method \WeMini\Image WeMiniImage($options = []) static 小程序图像处理 - * @method \WeMini\Live WeMiniLive($options = []) static 小程序直播接口 - * @method \WeMini\Logistics WeMiniLogistics($options = []) static 小程序物流助手 - * @method \WeMini\Message WeMiniMessage($options = []) static 小程序动态消息 - * @method \WeMini\Newtmpl WeMiniNewtmpl($options = []) static 小程序订阅消息 - * @method \WeMini\Ocr WeMiniOcr($options = []) static 小程序ORC服务 - * @method \WeMini\Operation WeMiniOperation($options = []) static 小程序运维中心 - * @method \WeMini\Plugs WeMiniPlugs($options = []) static 小程序插件管理 - * @method \WeMini\Poi WeMiniPoi($options = []) static 小程序地址管理 - * @method \WeMini\Qrcode WeMiniQrcode($options = []) static 小程序二维码管理 - * @method \WeMini\Scheme WeMiniScheme($options = []) static 小程序 URL-Scheme - * @method \WeMini\Search WeMiniSearch($options = []) static 小程序搜索 - * @method \WeMini\Security WeMiniSecurity($options = []) static 小程序内容安全 - * @method \WeMini\Soter WeMiniSoter($options = []) static 小程序生物认证 - * @method \WeMini\Template WeMiniTemplate($options = []) static 小程序模板消息支持 - * @method \WeMini\Total WeMiniTotal($options = []) static 小程序数据接口 - * - * ----- WePay ----- - * @method \WePay\Bill WePayBill($options = []) static 微信商户账单及评论 - * @method \WePay\Order WePayOrder($options = []) static 微信商户订单 - * @method \WePay\Coupon WePayCoupon($options = []) static 微信商户代金券 - * @method \WePay\Custom WePayCustom($options = []) static 微信商户海关 - * @method \WePay\Refund WePayRefund($options = []) static 微信商户退款 - * @method \WePay\Redpack WePayRedpack($options = []) static 微信红包支持 - * @method \WePay\Transfers WePayTransfers($options = []) static 微信商户打款到零钱 - * @method \WePay\TransfersBank WePayTransfersBank($options = []) static 微信商户打款到银行卡 - * @method \WePay\ProfitSharing WePayProfitSharing($options = []) static 微信分账 - */ -class We -{ - /** - * 定义当前版本 - * @var string - */ - const VERSION = '1.2.54'; - - /** - * 静态配置 - * @var DataArray - */ - private static $config; - - /** - * 设置及获取参数 - * @param array $option - * @return array - */ - public static function config($option = null) - { - if (is_array($option)) { - self::$config = new DataArray($option); - } - if (self::$config instanceof DataArray) { - return self::$config->get(); - } - return []; - } - - /** - * 静态魔术加载方法 - * @param string $name 静态类名 - * @param array $arguments 参数集合 - * @return mixed - * @throws InvalidInstanceException - */ - public static function __callStatic($name, $arguments) - { - if (substr($name, 0, 6) === 'WeChat') { - $class = 'WeChat\\' . substr($name, 6); - } elseif (substr($name, 0, 6) === 'WeMini') { - $class = 'WeMini\\' . substr($name, 6); - } elseif (substr($name, 0, 6) === 'AliPay') { - $class = 'AliPay\\' . substr($name, 6); - } elseif (substr($name, 0, 7) === 'WePayV3') { - $class = 'WePayV3\\' . substr($name, 7); - } elseif (substr($name, 0, 5) === 'WePay') { - $class = 'WePay\\' . substr($name, 5); - } - if (!empty($class) && class_exists($class)) { - $option = array_shift($arguments); - $config = is_array($option) ? $option : self::$config->get(); - return new $class($config); - } - throw new InvalidInstanceException("class {$name} not found"); - } - -} diff --git a/WeChat/Card.php b/WeChat/Card.php deleted file mode 100644 index a2224a9..0000000 --- a/WeChat/Card.php +++ /dev/null @@ -1,641 +0,0 @@ -callPostApi($url, $data); - } - - /** - * 设置买单开关 - * @param string $cardId 卡券ID - * @param bool $isOpen 是否开启 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function setPaycell($cardId, $isOpen = true) - { - $url = "https://api.weixin.qq.com/card/paycell/set?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['card_id' => $cardId, 'is_open' => $isOpen]); - } - - /** - * 设置自助核销 - * @param string $cardId 卡券ID - * @param bool $isOpen 是否开启 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function setConsumeCell($cardId, $isOpen = true) - { - $url = "https://api.weixin.qq.com/card/selfconsumecell/set?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['card_id' => $cardId, 'is_open' => $isOpen]); - } - - /** - * 创建卡券二维码 - * @param array $data 二维码参数(action_name, card 等) - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function createQrc(array $data) - { - $url = "https://api.weixin.qq.com/card/qrcode/create?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 创建卡券货架 - * @param array $data 货架参数 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function createLandingPage(array $data) - { - $url = "https://api.weixin.qq.com/card/landingpage/create?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 导入自定义 code - * @param string $cardId 卡券ID - * @param array $code code 列表(最多10万) - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function deposit($cardId, array $code) - { - $url = "https://api.weixin.qq.com/card/code/deposit?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['card_id' => $cardId, 'code' => $code]); - } - - /** - * 查询已导入 code 数量 - * @param string $cardId 卡券ID - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getDepositCount($cardId) - { - $url = "https://api.weixin.qq.com/card/code/getdepositcount?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['card_id' => $cardId]); - } - - /** - * 校验导入的 code - * @param string $cardId 卡券ID - * @param array $code 自定义 code 列表(<=100) - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function checkCode($cardId, array $code) - { - $url = "https://api.weixin.qq.com/card/code/checkcode?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['card_id' => $cardId, 'code' => $code]); - } - - /** - * 获取图文群发卡券的 HTML - * @param string $cardId 卡券ID - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getNewsHtml($cardId) - { - $url = "https://api.weixin.qq.com/card/mpnews/gethtml?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['card_id' => $cardId]); - } - - /** - * 设置测试白名单 - * @param array $openids 测试 openid 列表 - * @param array $usernames 测试微信号列表 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function setTestWhiteList($openids = [], $usernames = []) - { - $url = "https://api.weixin.qq.com/card/testwhitelist/set?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['openid' => $openids, 'username' => $usernames]); - } - - /** - * 查询 Code 状态 - * @param string $code 卡券 code - * @param string $cardId 卡券ID(自定义 code 必填) - * @param bool $checkConsume 是否校验核销状态 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getCode($code, $cardId = null, $checkConsume = null) - { - $data = ['code' => $code]; - is_null($cardId) || $data['card_id'] = $cardId; - is_null($checkConsume) || $data['check_consume'] = $checkConsume; - $url = "https://api.weixin.qq.com/card/code/get?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 核销 Code - * @param string $code 待核销的 code - * @param null|string $card_id 卡券ID(自定义 code 必填) - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function consume($code, $card_id = null) - { - $data = ['code' => $code]; - is_null($card_id) || $data['card_id'] = $card_id; - $url = "https://api.weixin.qq.com/card/code/consume?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 解码加密 Code - * @param string $encryptCode 加密 code - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function decrypt($encryptCode) - { - $url = "https://api.weixin.qq.com/card/code/decrypt?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['encrypt_code' => $encryptCode]); - } - - /** - * 获取用户已领取卡券 - * @param string $openid 用户 openid - * @param null|string $cardId 卡券ID(可选) - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getCardList($openid, $cardId = null) - { - $data = ['openid' => $openid]; - is_null($cardId) || $data['card_id'] = $cardId; - $url = "https://api.weixin.qq.com/card/user/getcardlist?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 查看卡券详情 - * @param string $cardId 卡券ID - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getCard($cardId) - { - $url = "https://api.weixin.qq.com/card/get?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['card_id' => $cardId]); - } - - /** - * 批量查询卡券 - * @param int $offset 起始偏移量 - * @param int $count 拉取数量(<=50) - * @param array $statusList 状态过滤 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function batchGet($offset, $count = 50, array $statusList = []) - { - $data = ['offset' => $offset, 'count' => $count]; - empty($statusList) || $data['status_list'] = $statusList; - $url = "https://api.weixin.qq.com/card/batchget?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 更新会员卡信息 - * @param string $cardId 卡券ID - * @param array $memberCard 会员卡内容 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function updateCard($cardId, array $memberCard) - { - $url = "https://api.weixin.qq.com/card/update?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['card_id' => $cardId, 'member_card' => $memberCard]); - } - - /** - * 修改库存 - * @param string $card_id 卡券ID - * @param int|null $increase_stock_value 增加库存 - * @param int|null $reduce_stock_value 减少库存 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function modifyStock($card_id, $increase_stock_value = null, $reduce_stock_value = null) - { - $data = ['card_id' => $card_id]; - is_null($reduce_stock_value) || $data['reduce_stock_value'] = $reduce_stock_value; - is_null($increase_stock_value) || $data['increase_stock_value'] = $increase_stock_value; - $url = "https://api.weixin.qq.com/card/modifystock?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 变更 Code - * @param string $code 原 code - * @param string $new_code 新 code - * @param null|string $card_id 卡券ID - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function updateCode($code, $new_code, $card_id = null) - { - $data = ['code' => $code, 'new_code' => $new_code]; - is_null($card_id) || $data['card_id'] = $card_id; - $url = "https://api.weixin.qq.com/card/code/update?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 删除卡券 - * @param string $cardId 卡券ID - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function deleteCard($cardId) - { - $url = "https://api.weixin.qq.com/card/delete?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['card_id' => $cardId]); - } - - /** - * 设置卡券失效 - * @param string $code 卡券 code - * @param string $cardId 卡券ID - * @param null|string $reason 失效原因 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function unAvailable($code, $cardId, $reason = null) - { - $data = ['code' => $code, 'card_id' => $cardId]; - is_null($reason) || $data['reason'] = $reason; - $url = "https://api.weixin.qq.com/card/code/unavailable?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 拉取卡券概况数据 - * @param string $beginDate 开始日期 - * @param string $endDate 结束日期 - * @param string $condSource 卡券来源 0|1 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getCardBizuininfo($beginDate, $endDate, $condSource) - { - $url = "https://api.weixin.qq.com/datacube/getcardbizuininfo?access_token=ACCESS_TOKEN"; - $data = ['begin_date' => $beginDate, 'end_date' => $endDate, 'cond_source' => $condSource]; - return $this->callPostApi($url, $data); - } - - /** - * 获取免费券数据 - * @param string $beginDate 开始日期 - * @param string $endDate 结束日期 - * @param int $condSource 卡券来源 0|1 - * @param null|string $cardId 卡券ID - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getCardCardinfo($beginDate, $endDate, $condSource, $cardId = null) - { - $url = "https://api.weixin.qq.com/datacube/getcardcardinfo?access_token=ACCESS_TOKEN"; - $data = ['begin_date' => $beginDate, 'end_date' => $endDate, 'cond_source' => $condSource]; - is_null($cardId) || $data['card_id'] = $cardId; - return $this->callPostApi($url, $data); - } - - - /** - * 激活会员卡 - * @param array $data 激活参数 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function activateMemberCard(array $data) - { - $url = 'https://api.weixin.qq.com/card/membercard/activate?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data); - } - - /** - * 设置开卡字段(激活表单) - * @param array $data 表单字段 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function setActivateMemberCardUser(array $data) - { - $url = 'https://api.weixin.qq.com/card/membercard/activateuserform/set?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data); - } - - /** - * 获取用户提交的激活资料 - * @param string $activateTicket 激活 ticket - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getActivateMemberCardTempinfo($activateTicket) - { - $url = 'https://api.weixin.qq.com/card/membercard/activatetempinfo/get?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['activate_ticket' => $activateTicket]); - } - - /** - * 更新会员信息 - * @param array $data 更新参数 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function updateMemberCardUser(array $data) - { - $url = 'https://api.weixin.qq.com/card/membercard/updateuser?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data); - } - - /** - * 拉取会员卡概况数据 - * @param string $beginDate 开始日期 - * @param string $endDate 结束日期 - * @param string $condSource 卡券来源 0|1 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getCardMemberCardinfo($beginDate, $endDate, $condSource) - { - $url = "https://api.weixin.qq.com/datacube/getcardmembercardinfo?access_token=ACCESS_TOKEN"; - $data = ['begin_date' => $beginDate, 'end_date' => $endDate, 'cond_source' => $condSource]; - return $this->callPostApi($url, $data); - } - - /** - * 拉取单张会员卡数据 - * @param string $beginDate 开始日期 - * @param string $endDate 结束日期 - * @param string $cardId 卡券ID - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getCardMemberCardDetail($beginDate, $endDate, $cardId) - { - $url = "https://api.weixin.qq.com/datacube/getcardmembercarddetail?access_token=ACCESS_TOKEN"; - $data = ['begin_date' => $beginDate, 'end_date' => $endDate, 'card_id' => $cardId]; - return $this->callPostApi($url, $data); - } - - /** - * 查询会员信息(积分) - * @param string $cardId 会员卡ID - * @param string $code 用户 code - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getCardMemberCard($cardId, $code) - { - $data = ['card_id' => $cardId, 'code' => $code]; - $url = "https://api.weixin.qq.com/card/membercard/userinfo/get?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 支付后投放卡券规则 - * @param array $data 规则参数 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function payGiftCard(array $data) - { - $url = "https://api.weixin.qq.com/card/paygiftcard/add?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 删除支付后投放卡券规则 - * @param int $ruleId 规则ID - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function delPayGiftCard($ruleId) - { - $url = "https://api.weixin.qq.com/card/paygiftcard/add?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['rule_id' => $ruleId]); - } - - /** - * 查询支付后投放卡券规则 - * @param int $ruleId 规则ID - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getPayGiftCard($ruleId) - { - $url = "https://api.weixin.qq.com/card/paygiftcard/getbyid?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['rule_id' => $ruleId]); - } - - /** - * 批量查询支付后投放规则 - * @param int $offset 起始偏移 - * @param int $count 数量 - * @param bool $effective 是否仅查询生效规则 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function batchGetPayGiftCard($offset = 0, $count = 10, $effective = true) - { - $url = "https://api.weixin.qq.com/card/paygiftcard/batchget?access_token=ACCESS_TOKEN"; - $data = ['type' => 'RULE_TYPE_PAY_MEMBER_CARD', 'offset' => $offset, 'count' => $count, 'effective' => $effective]; - return $this->callPostApi($url, $data); - } - - /** - * 创建支付后立减金活动 - * @param array $data 活动参数 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function addActivity(array $data) - { - $url = "https://api.weixin.qq.com/card/mkt/activity/create?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 开通券点账户 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function payActivate() - { - $url = "https://api.weixin.qq.com/card/pay/activate?access_token=ACCESS_TOKEN"; - return $this->callGetApi($url); - } - - /** - * 预估优惠券库存价格 - * @param string $cardId 卡券ID - * @param int $quantity 兑换数量 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getPayprice($cardId, $quantity) - { - $url = "POST https://api.weixin.qq.com/card/pay/getpayprice?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['card_id' => $cardId, 'quantity' => $quantity]); - } - - /** - * 查询券点余额 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getCoinsInfo() - { - $url = "https://api.weixin.qq.com/card/pay/getcoinsinfo?access_token=ACCESS_TOKEN"; - return $this->callGetApi($url); - } - - /** - * 确认兑换库存 - * @param string $cardId 卡券ID - * @param int $quantity 数量 - * @param string $orderId 批价返回的订单号 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function payConfirm($cardId, $quantity, $orderId) - { - $url = "https://api.weixin.qq.com/card/pay/confirm?access_token=ACCESS_TOKEN"; - $data = ['card_id' => $cardId, 'quantity' => $quantity, 'order_id' => $orderId]; - return $this->callPostApi($url, $data); - } - - /** - * 充值券点 - * @param int $coinCount 充值数量 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function payRecharge($coinCount) - { - $url = "https://api.weixin.qq.com/card/pay/recharge?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['coin_count' => $coinCount]); - } - - /** - * 查询券点订单详情 - * @param string $orderId 订单号 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function payGetOrder($orderId) - { - $url = "https://api.weixin.qq.com/card/pay/getorder?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['order_id' => $orderId]); - } - - /** - * 查询券点流水 - * @param array $data 查询参数 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function payGetList(array $data) - { - $url = "https://api.weixin.qq.com/card/pay/getorderlist?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 获取开卡插件参数 - * @param array $data 入口参数 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getActivateUrl(array $data) - { - $url = "https://api.weixin.qq.com/card/membercard/activate/geturl?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } -} \ No newline at end of file diff --git a/WeChat/Contracts/BasicAliPay.php b/WeChat/Contracts/BasicAliPay.php deleted file mode 100644 index 24565f4..0000000 --- a/WeChat/Contracts/BasicAliPay.php +++ /dev/null @@ -1,531 +0,0 @@ -gateway = 'https://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=utf-8'; - } - $this->params = new DataArray([]); - $this->config = new DataArray($options); - $this->options = new DataArray([ - 'app_id' => $this->config->get('appid'), - 'charset' => empty($options['charset']) ? 'utf-8' : $options['charset'], - 'format' => 'JSON', - 'version' => '1.0', - 'sign_type' => empty($options['sign_type']) ? 'RSA2' : $options['sign_type'], - 'timestamp' => date('Y-m-d H:i:s'), - ]); - if (isset($options['notify_url']) && $options['notify_url'] !== '') { - $this->options->set('notify_url', $options['notify_url']); - } - if (isset($options['return_url']) && $options['return_url'] !== '') { - $this->options->set('return_url', $options['return_url']); - } - if (isset($options['app_auth_token']) && $options['app_auth_token'] !== '') { - $this->options->set('app_auth_token', $options['app_auth_token']); - } - - // 证书模式读取证书 - $appCertPath = $this->config->get('app_cert_path'); - $aliRootPath = $this->config->get('alipay_root_path'); - if (!$this->config->get('app_cert') && !empty($appCertPath) && is_file($appCertPath)) { - $this->config->set('app_cert', file_get_contents($appCertPath)); - } - if (!$this->config->get('root_cert') && !empty($aliRootPath) && is_file($aliRootPath)) { - $this->config->set('root_cert', file_get_contents($aliRootPath)); - } - } - - /** - * 静态创建对象 - * @param array $config - * @return static - */ - public static function instance(array $config) - { - $key = md5(get_called_class() . serialize($config)); - if (isset(self::$cache[$key])) return self::$cache[$key]; - return self::$cache[$key] = new static($config); - } - - /** - * 查询支付宝订单状态 - * @param string $outTradeNo 商户订单号 - * @return array 订单查询结果 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function query($outTradeNo = '') - { - $this->options->set('method', 'alipay.trade.query'); - return $this->getResult(['out_trade_no' => $outTradeNo]); - } - - /** - * 请求接口并验证访问数据 - * @param array $options 请求参数 - * @return array 接口响应数据 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - protected function getResult($options) - { - $this->applyData($options); - $method = str_replace('.', '_', $this->options['method']) . '_response'; - $data = json_decode(Tools::get($this->gateway, $this->options->get()), true); - if (!isset($data[$method]['code']) || $data[$method]['code'] !== '10000') { - throw new InvalidResponseException( - "Error: " . - (empty($data[$method]['code']) ? '' : "{$data[$method]['msg']} [{$data[$method]['code']}]\r\n") . - (empty($data[$method]['sub_code']) ? '' : "{$data[$method]['sub_msg']} [{$data[$method]['sub_code']}]\r\n"), - $data[$method]['code'], $data - ); - } - return $data[$method]; - // 返回结果签名检查 - // return $this->verify($data[$method], $data['sign']); - } - - /** - * 数据包生成及数据签名 - * @param array $options - */ - protected function applyData($options) - { - if ($this->config->get('app_cert') && $this->config->get('root_cert')) { - $this->setAppCertSnAndRootCertSn(); - } - $this->options->set('biz_content', json_encode($this->params->merge($options), 256)); - $this->options->set('sign', $this->getSign()); - } - - /** - * 新版 设置网关应用公钥证书SN、支付宝根证书SN - */ - protected function setAppCertSnAndRootCertSn() - { - if (!($appCert = $this->config->get('app_cert'))) { - throw new InvalidArgumentException('Missing Config -- [app_cert|app_cert_path]'); - } - if (!($rootCert = $this->config->get('root_cert'))) { - throw new InvalidArgumentException('Missing Config -- [root_cert|alipay_root_path]'); - } - $this->options->set('app_cert_sn', $this->getAppCertSN($appCert)); - $this->options->set('alipay_root_cert_sn', $this->getRootCertSN($rootCert)); - if (!$this->options->get('app_cert_sn')) { - throw new InvalidArgumentException('Missing options -- [app_cert_sn]'); - } - if (!$this->options->get('alipay_root_cert_sn')) { - throw new InvalidArgumentException('Missing options -- [alipay_root_cert_sn]'); - } - } - - /** - * 新版 从证书中提取序列号 - * @param string $sign - * @return string - */ - private function getAppCertSN($sign) - { - $ssl = openssl_x509_parse($sign, true); - $issuer = isset($ssl['issuer']) && is_array($ssl['issuer']) ? $ssl['issuer'] : []; - return md5($this->_arr2str(array_reverse($issuer)) . $ssl['serialNumber']); - } - - /** - * 新版 数组转字符串 - * @param array $array - * @return string - */ - private function _arr2str($array) - { - $string = []; - if ($array && is_array($array)) { - foreach ($array as $key => $value) { - $string[] = $key . '=' . $value; - } - } - return join(',', $string); - } - - /** - * 新版 提取根证书序列号 - * @param string $sign - * @return string|null - */ - private function getRootCertSN($sign) - { - if (strlen($sign) < 500 && file_exists($sign)) { - $sign = file_get_contents($sign); - } - $sn = null; - $array = explode('-----END CERTIFICATE-----', $sign); - for ($i = 0; $i < count($array) - 1; $i++) { - $ssl[$i] = openssl_x509_parse($array[$i] . '-----END CERTIFICATE-----', true); - if (strpos($ssl[$i]['serialNumber'], '0x') === 0) { - $ssl[$i]['serialNumber'] = $this->_hex2dec($ssl[$i]['serialNumberHex']); - } - if ($ssl[$i]['signatureTypeLN'] == 'sha1WithRSAEncryption' || $ssl[$i]['signatureTypeLN'] == 'sha256WithRSAEncryption') { - $issuer = isset($ssl[$i]['issuer']) && is_array($ssl[$i]['issuer']) ? $ssl[$i]['issuer'] : []; - if ($sn == null) { - $sn = md5($this->_arr2str(array_reverse($issuer)) . $ssl[$i]['serialNumber']); - } else { - $sn = $sn . '_' . md5($this->_arr2str(array_reverse($issuer)) . $ssl[$i]['serialNumber']); - } - } - } - return $sn; - } - - /** - * 新版 0x转高精度数字 - * @param string $hex - * @return int|string - */ - private function _hex2dec($hex) - { - list($dec, $len) = [0, strlen($hex)]; - for ($i = 1; $i <= $len; $i++) { - $dec = bcadd($dec, bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i)))); - } - return $dec; - } - - /** - * 获取数据签名 - * @return string - */ - protected function getSign() - { - if ($this->options->get('sign_type') === 'RSA2') { - openssl_sign($this->getSignContent($this->options->get(), true), $sign, $this->getAppPrivateKey(), OPENSSL_ALGO_SHA256); - } else { - openssl_sign($this->getSignContent($this->options->get(), true), $sign, $this->getAppPrivateKey(), OPENSSL_ALGO_SHA1); - } - return base64_encode($sign); - } - - /** - * 数据签名处理 - * @param array $data 需要进行签名数据 - * @param boolean $needSignType 是否需要sign_type字段 - * @return string - */ - private function getSignContent(array $data, $needSignType = false) - { - ksort($data); - $attrs = array(); - if (isset($data['sign'])) unset($data['sign']); - if (empty($needSignType)) unset($data['sign_type']); - foreach ($data as $key => $value) { - if ($value === '' || is_null($value)) continue; - $attrs[] = "{$key}={$value}"; - } - return join('&', $attrs); - } - - /** - * 获取应用私钥内容 - * @return string - */ - private function getAppPrivateKey() - { - $content = wordwrap($this->trimCert($this->config->get('private_key')), 64, "\n", true); - return "-----BEGIN RSA PRIVATE KEY-----\n{$content}\n-----END RSA PRIVATE KEY-----"; - } - - /** - * 去除证书前后内容及空白 - * @param string $sign - * @return string - */ - protected function trimCert($sign) - { - return preg_replace(['/\s+/', '/-{5}.*?-{5}/'], '', $sign); - } - - /** - * 支付宝订单退款操作 - * @param array|string $options 退款参数或退款商户订单号 - * @param string|null $refundAmount 退款金额 - * @return array 退款结果 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function refund($options, $refundAmount = null) - { - if (!is_array($options)) $options = ['out_trade_no' => $options, 'refund_amount' => $refundAmount]; - $this->options->set('method', 'alipay.trade.refund'); - return $this->getResult($options); - } - - /** - * 支付宝订单退款查询 - * @param array|string $options 退款参数或退款商户订单号 - * @param array|null $queryOptions 查询选项 - * @return array 退款查询结果 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function refundQuery($options, $queryOptions = null) - { - if (!is_array($options)) $options = ['out_trade_no' => $options]; - empty($queryOptions) || $options['query_options'] = $queryOptions; - $this->options->set('method', 'alipay.trade.fastpay.refund.query'); - return $this->getResult($options); - } - - /** - * 关闭支付宝进行中的订单 - * @param array|string $options 订单参数或商户订单号 - * @return array 关闭结果 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function close($options) - { - if (!is_array($options)) $options = ['out_trade_no' => $options]; - $this->options->set('method', 'alipay.trade.close'); - return $this->getResult($options); - } - - /** - * 获取通知数据 - * - * @param boolean $needSignType 是否需要sign_type字段 - * @param array $parameters - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function notify($needSignType = false, array $parameters = []) - { - $data = empty($parameters) ? $_POST : $parameters; - - if (empty($data) || empty($data['sign'])) { - throw new InvalidResponseException('Illegal push request.', 0, $data); - } - $string = $this->getSignContent($data, $needSignType); - if (openssl_verify($string, base64_decode($data['sign']), $this->getAliPublicKey(), OPENSSL_ALGO_SHA256) !== 1) { - throw new InvalidResponseException('Data signature verification failed.', 0, $data); - } - return $data; - } - - /** - * 获取支付公钥内容 - * @return string - */ - public function getAliPublicKey() - { - $cert = $this->config->get('public_key'); - if (strpos(trim($cert), '-----BEGIN CERTIFICATE-----') !== false) { - $pkey = openssl_pkey_get_public($cert); - $keyData = openssl_pkey_get_details($pkey); - return trim($keyData['key']); - } else { - $content = wordwrap($this->trimCert($cert), 64, "\n", true); - return "-----BEGIN PUBLIC KEY-----\n{$content}\n-----END PUBLIC KEY-----"; - } - } - - /** - * 应用数据操作 - * @param array $options - * @return mixed - */ - abstract public function apply($options); - - /** - * 通用接口调用(支付宝开放平台) - * @param string $apiMethod API 方法名(如 alipay.trade.query),必填 - * @param array|string $data 业务参数,数组会写入 biz_content;字符串尝试解析为 JSON - * @param string $method GET|POST|PUT|DELETE|PATCH,默认 GET(POST 会放入表单) - * @param bool $verify 是否验证返回签名 - * @return array 返回解析后的响应体 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function callApi($apiMethod, $data = [], $method = 'GET', $verify = false) - { - $method = strtoupper($method); - - // 验证API方法名(必填) - if (empty($apiMethod)) { - throw new \WeChat\Exceptions\InvalidArgumentException("Missing required parameter -- [apiMethod]"); - } - - // 使用gateway作为URL - $url = $this->gateway; - - // 设置API方法 - $this->options->set('method', $apiMethod); - - // 处理数据格式并合并参数 - $params = []; - if (is_array($data)) { - $params = $data; - } elseif (is_string($data) && !empty($data)) { - $decoded = json_decode($data, true); - if (json_last_error() === JSON_ERROR_NONE) { - $params = $decoded; - } - } - if (!empty($params)) { - $this->params->merge($params); - } - - // 处理签名(默认自动签名) - if ($this->config->get('app_cert') && $this->config->get('root_cert')) { - $this->setAppCertSnAndRootCertSn(); - } - $this->options->set('biz_content', json_encode($this->params->get(), 256)); - $this->options->set('sign', $this->getSign()); - - // 统一处理响应(减少重复代码) - $methodKey = str_replace('.', '_', $this->options->get('method')) . '_response'; - $self = $this; - $processResponse = function ($response) use ($verify, $methodKey, $self) { - $data = json_decode($response, true); - if (json_last_error() !== JSON_ERROR_NONE) { - return $response; // JSON解析失败,返回原始响应 - } - - // 验证响应签名 - if ($verify && isset($data['sign']) && isset($data[$methodKey])) { - return $self->verify($data[$methodKey], $data['sign']); - } - - // 检查错误 - if (isset($data[$methodKey])) { - if (isset($data[$methodKey]['code']) && $data[$methodKey]['code'] !== '10000') { - $msg = isset($data[$methodKey]['msg']) ? "{$data[$methodKey]['msg']} [{$data[$methodKey]['code']}]\r\n" : ''; - $subMsg = isset($data[$methodKey]['sub_msg']) ? "{$data[$methodKey]['sub_msg']} [{$data[$methodKey]['sub_code']}]\r\n" : ''; - throw new InvalidResponseException("Error: " . $msg . $subMsg, $data[$methodKey]['code'], $data); - } - return $data[$methodKey]; - } - - return $data; - }; - - // GET/HEAD/OPTIONS请求 - if (in_array($method, ['GET', 'HEAD', 'OPTIONS'])) { - $queryParams = array_merge($this->options->get(), $params); - $response = Tools::get($url, $queryParams); - return $processResponse($response); - } - - // POST/PUT/PATCH/DELETE请求 - $postData = array_merge($this->options->get(), $params); - $response = Tools::doRequest($method, $url, ['data' => $postData]); - return $processResponse($response); - } - - /** - * 验证接口返回的数据签名 - * @param array $data 通知数据 - * @param null|string $sign 数据签名 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - protected function verify($data, $sign) - { - unset($data['sign']); - if ($this->options->get('sign_type') === 'RSA2') { - if (openssl_verify(json_encode($data, 256), base64_decode($sign), $this->getAliPublicKey(), OPENSSL_ALGO_SHA256) !== 1) { - throw new InvalidResponseException('Data signature verification failed by RSA2.'); - } - } else { - if (openssl_verify(json_encode($data, 256), base64_decode($sign), $this->getAliPublicKey(), OPENSSL_ALGO_SHA1) !== 1) { - throw new InvalidResponseException('Data signature verification failed by RSA.'); - } - } - return $data; - } - - /** - * 生成支付HTML代码 - * @return string - */ - protected function buildPayHtml() - { - $html = "
"; - foreach ($this->options->get() as $key => $value) { - $value = str_replace("'", ''', $value); - $html .= ""; - } - $html .= "
"; - return "{$html}"; - } -} \ No newline at end of file diff --git a/WeChat/Contracts/BasicPushEvent.php b/WeChat/Contracts/BasicPushEvent.php deleted file mode 100644 index ddf5e77..0000000 --- a/WeChat/Contracts/BasicPushEvent.php +++ /dev/null @@ -1,242 +0,0 @@ -config = new DataArray($options); - $this->input = new DataArray($_REQUEST); - $this->appid = $this->config->get('appid'); - // 推送消息处理 - if ($_SERVER['REQUEST_METHOD'] == "POST") { - $this->postxml = Tools::getRawInput(); - $this->encryptType = $this->input->get('encrypt_type'); - if ($this->isEncrypt()) { - if (empty($options['encodingaeskey'])) { - throw new InvalidArgumentException("Missing Config -- [encodingaeskey]"); - } - $prpcrypt = new Prpcrypt($this->config->get('encodingaeskey')); - $result = Tools::xml2arr($this->postxml); - $array = $prpcrypt->decrypt($result['Encrypt']); - if (intval($array[0]) > 0) { - throw new InvalidResponseException($array[1], $array[0]); - } - list($this->postxml, $this->appid) = [$array[1], $array[2]]; - } - $this->receive = new DataArray(Tools::xml2arr($this->postxml)); - } elseif ($_SERVER['REQUEST_METHOD'] == "GET" && $this->checkSignature()) { - $this->receive = new DataArray([]); - if ($showEchoStr && ob_clean()) { - echo($this->input->get('echostr')); - } - } else { - throw new InvalidResponseException('Invalid interface request.', '0'); - } - } - - /** - * 消息是否需要加密 - * @return boolean - */ - public function isEncrypt() - { - return $this->encryptType === 'aes'; - } - - /** - * 验证来自微信服务器 - * @return bool - */ - private function checkSignature() - { - $nonce = $this->input->get('nonce'); - $timestamp = $this->input->get('timestamp'); - $msg_signature = $this->input->get('msg_signature'); - $signature = empty($msg_signature) ? $this->input->get('signature') : $msg_signature; - $tmpArr = [$this->config->get('token'), $timestamp, $nonce, '']; - sort($tmpArr, SORT_STRING); - return sha1(implode($tmpArr)) === $signature; - } - - /** - * 获取回显字串 - * @return string - */ - public function getEchoStr() - { - if ($_SERVER['REQUEST_METHOD'] == "GET" && $this->checkSignature()) { - return $this->input->get('echostr'); - } else { - return ''; - } - } - - /** - * 回复消息 - * @param array $data 消息内容 - * @param boolean $return 是否返回XML内容 - * @param boolean $isEncrypt 是否加密内容 - * @return string|void - * @throws \WeChat\Exceptions\InvalidDecryptException - */ - public function reply(array $data = [], $return = false, $isEncrypt = false) - { - $xml = Tools::arr2xml(empty($data) ? $this->message : $data); - if ($this->isEncrypt() || $isEncrypt) { - $prpcrypt = new Prpcrypt($this->config->get('encodingaeskey')); - // 如果是第三方平台,加密得使用 component_appid - $component_appid = $this->config->get('component_appid'); - $appid = empty($component_appid) ? $this->appid : $component_appid; - $array = $prpcrypt->encrypt($xml, $appid); - if ($array[0] > 0) throw new InvalidDecryptException('Encrypt Error.', '0'); - list($timestamp, $encrypt) = [time(), $array[1]]; - $nonce = rand(77, 999) * rand(605, 888) * rand(11, 99); - $tmpArr = [$this->config->get('token'), $timestamp, $nonce, $encrypt]; - sort($tmpArr, SORT_STRING); - $signature = sha1(implode($tmpArr)); - $format = "%s"; - $xml = sprintf($format, $encrypt, $signature, $timestamp, $nonce); - } - if ($return) return $xml; - @ob_clean(); - echo $xml; - } - - /** - * 获取公众号推送对象 - * @param null|string $field 指定获取字段 - * @return array - */ - public function getReceive($field = null) - { - return $this->receive->get($field); - } - - /** - * 获取当前微信OPENID - * @return string - */ - public function getOpenid() - { - return $this->receive->get('FromUserName'); - } - - /** - * 获取当前推送消息类型 - * @return string - */ - public function getMsgType() - { - return $this->receive->get('MsgType'); - } - - /** - * 获取当前推送消息ID - * @return string - */ - public function getMsgId() - { - return $this->receive->get('MsgId'); - } - - /** - * 获取当前推送时间 - * @return integer - */ - public function getMsgTime() - { - return $this->receive->get('CreateTime'); - } - - /** - * 获取当前推送公众号 - * @return string - */ - public function getToOpenid() - { - return $this->receive->get('ToUserName'); - } -} \ No newline at end of file diff --git a/WeChat/Contracts/BasicWeChat.php b/WeChat/Contracts/BasicWeChat.php deleted file mode 100644 index f071d98..0000000 --- a/WeChat/Contracts/BasicWeChat.php +++ /dev/null @@ -1,292 +0,0 @@ -GetAccessTokenCallback = $options['GetAccessTokenCallback']; - } - if (!empty($options['cache_path'])) { - Tools::$cache_path = $options['cache_path']; - } - $this->config = new DataArray($options); - } - - /** - * 静态创建对象 - * @param array $config 配置(appid、appsecret 等) - * @return static - */ - public static function instance(array $config) - { - $key = md5(get_called_class() . serialize($config)); - if (isset(self::$cache[$key])) return self::$cache[$key]; - return self::$cache[$key] = new static($config); - } - - /** - * 接口通用POST请求方法 - * @param string $url 接口URL - * @param array $data POST提交接口参数 - * @param bool $toJson 是否转换为JSON参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function callPostApi($url, array $data, $toJson = true, array $options = []) - { - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->httpPostForJson($url, $data, $toJson, $options); - } - - /** - * 注册当前请求接口(自动处理 ACCESS_TOKEN) - * @param string $url 接口地址(引用传递,会被修改) - * @param string $method 当前接口方法名 - * @param array $arguments 请求参数 - * @return string 替换 ACCESS_TOKEN 后的 URL - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - protected function registerApi(&$url, $method, $arguments = []) - { - $this->currentMethod = ['method' => $method, 'arguments' => $arguments]; - if (empty($this->access_token)) $this->access_token = $this->getAccessToken(); - return $url = str_replace('ACCESS_TOKEN', urlencode($this->access_token), $url); - } - - /** - * 获取访问 AccessToken - * @return string - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getAccessToken() - { - if (!empty($this->access_token)) { - return $this->access_token; - } - $cache = $this->config->get('appid') . '_access_token'; - $this->access_token = Tools::getCache($cache); - if (!empty($this->access_token)) { - return $this->access_token; - } - // 处理开放平台授权公众号获取AccessToken - if (!empty($this->GetAccessTokenCallback) && is_callable($this->GetAccessTokenCallback)) { - $this->access_token = call_user_func_array($this->GetAccessTokenCallback, [$this->config->get('appid'), $this]); - if (!empty($this->access_token)) { - Tools::setCache($cache, $this->access_token, 7000); - } - return $this->access_token; - } - list($appid, $secret) = [$this->config->get('appid'), $this->config->get('appsecret')]; - $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$appid}&secret={$secret}"; - $result = Tools::json2arr(Tools::get($url)); - if (!empty($result['access_token'])) { - Tools::setCache($cache, $result['access_token'], 7000); - } - return $this->access_token = $result['access_token']; - } - - /** - * 设置外部接口 AccessToken - * @param string $accessToken - * @throws \WeChat\Exceptions\LocalCacheException - * @author 高一平 - * - * 当用户使用自己的缓存驱动时,直接实例化对象后可直接设置 AccessToken - * - 多用于分布式项目时保持 AccessToken 统一 - * - 使用此方法后就由用户来保证传入的 AccessToken 为有效 AccessToken - */ - public function setAccessToken($accessToken) - { - if (!is_string($accessToken)) { - throw new InvalidArgumentException("Invalid AccessToken type, need string."); - } - $cache = $this->config->get('appid') . '_access_token'; - Tools::setCache($cache, $this->access_token = $accessToken); - } - - /** - * 以POST获取接口数据并转为数组 - * @param string $url 接口地址 - * @param array $data 请求数据 - * @param bool $toJson 转换JSON - * @param array $options 请求扩展数据 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - protected function httpPostForJson($url, array $data, $toJson = true, array $options = []) - { - try { - $options['headers'] = isset($options['headers']) ? $options['headers'] : []; - if ($toJson) $options['headers'][] = 'Content-Type: application/json'; - return Tools::json2arr(Tools::post($url, $toJson ? Tools::arr2json($data) : $data, $options)); - } catch (InvalidResponseException $exception) { - if (!$this->isTry && in_array($exception->getCode(), ['40014', '40001', '41001', '42001'])) { - $this->delAccessToken(); - $this->isTry = true; - return call_user_func_array([$this, $this->currentMethod['method']], $this->currentMethod['arguments']); - } - throw new InvalidResponseException($exception->getMessage(), $exception->getCode()); - } - } - - /** - * 清理删除 AccessToken - * @return bool - */ - public function delAccessToken() - { - $this->access_token = ''; - return Tools::delCache($this->config->get('appid') . '_access_token'); - } - - /** - * 通用 GET 请求(自动处理 ACCESS_TOKEN) - * @param string $url 接口 URL - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function callGetApi($url) - { - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->httpGetForJson($url); - } - - /** - * 以GET获取接口数据并转为数组 - * @param string $url 接口地址 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - protected function httpGetForJson($url) - { - try { - return Tools::json2arr(Tools::get($url)); - } catch (InvalidResponseException $exception) { - if (isset($this->currentMethod['method']) && empty($this->isTry)) { - if (in_array($exception->getCode(), ['40014', '40001', '41001', '42001'])) { - $this->delAccessToken(); - $this->isTry = true; - return call_user_func_array([$this, $this->currentMethod['method']], $this->currentMethod['arguments']); - } - } - throw new InvalidResponseException($exception->getMessage(), $exception->getCode()); - } - } - - /** - * 通用接口调用 - * @param string $url 完整 URL 或相对路径,支持 ACCESS_TOKEN 占位符自动替换 - * @param array|string $data GET 参数或请求体,数组自动 JSON;字符串原样发送 - * @param string $method GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS,默认 GET - * @return array 解析后的数组 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function callApi($url, $data = [], $method = 'GET') - { - $method = strtoupper($method); - - // 自动处理ACCESS_TOKEN - if (strpos($url, 'ACCESS_TOKEN') !== false) { - if (empty($this->access_token)) { - $this->access_token = $this->getAccessToken(); - } - $url = str_replace('ACCESS_TOKEN', urlencode($this->access_token), $url); - } - - // GET/HEAD/OPTIONS请求(无请求体) - if (in_array($method, ['GET', 'HEAD', 'OPTIONS'])) { - if (!empty($data) && is_array($data)) { - $url .= (strpos($url, '?') !== false ? '&' : '?') . http_build_query($data); - } - return $this->httpGetForJson($url); - } - - // POST/PUT/PATCH/DELETE请求(有请求体) - $postData = is_array($data) ? $data : []; - $toJson = is_array($data); - - // POST请求使用原有方法(支持自动重试) - if ($method === 'POST') { - return $this->httpPostForJson($url, $postData, $toJson, []); - } - - // PUT/PATCH/DELETE请求直接调用 - $requestData = is_string($data) ? $data : Tools::arr2json($postData); - $response = Tools::doRequest($method, $url, ['data' => $requestData]); - return Tools::json2arr($response); - } - -} \ No newline at end of file diff --git a/WeChat/Contracts/BasicWePay.php b/WeChat/Contracts/BasicWePay.php deleted file mode 100644 index 6edbb35..0000000 --- a/WeChat/Contracts/BasicWePay.php +++ /dev/null @@ -1,280 +0,0 @@ -config = new DataArray($options); - // 商户基础参数 - $this->params = new DataArray([ - 'appid' => $this->config->get('appid'), - 'mch_id' => $this->config->get('mch_id'), - 'nonce_str' => Tools::createNoncestr(), - ]); - // 商户参数支持 - if ($this->config->get('sub_appid')) { - $this->params->set('sub_appid', $this->config->get('sub_appid')); - } - if ($this->config->get('sub_mch_id')) { - $this->params->set('sub_mch_id', $this->config->get('sub_mch_id')); - } - } - - /** - * 静态创建对象 - * @param array $config 商户配置 - * @return static - */ - public static function instance(array $config) - { - $key = md5(get_called_class() . serialize($config)); - if (isset(self::$cache[$key])) return self::$cache[$key]; - return self::$cache[$key] = new static($config); - } - - /** - * 获取微信支付异步通知并验签 - * @param string|array $xml 可选,默认读取原始输入 - * @return array 验签通过的通知数据 - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function getNotify($xml = '') - { - $data = is_array($xml) ? $xml : Tools::xml2arr(empty($xml) ? Tools::getRawInput() : $xml); - if (isset($data['sign']) && $this->getPaySign($data) === $data['sign']) { - return $data; - } - throw new InvalidResponseException('Invalid Notify.', '0'); - } - - /** - * 生成支付签名 - * @param array $data 待签名数据 - * @param string $signType MD5|HMAC-SHA256 - * @param string $buff 签名前缀(内部使用) - * @return string 大写签名 - */ - public function getPaySign(array $data, $signType = 'MD5', $buff = '') - { - ksort($data); - if (isset($data['sign'])) unset($data['sign']); - foreach ($data as $k => $v) { - if ('' === $v || null === $v) continue; - $buff .= "{$k}={$v}&"; - } - $buff .= ("key=" . $this->config->get('mch_key')); - if (strtoupper($signType) === 'MD5') { - return strtoupper(md5($buff)); - } - return strtoupper(hash_hmac('SHA256', $buff, $this->config->get('mch_key'))); - } - - /** - * 获取微信支付通知成功回复 XML - * @return string - */ - public function getNotifySuccessReply() - { - return Tools::arr2xml(['return_code' => 'SUCCESS', 'return_msg' => 'OK']); - } - - /** - * 转换短链接(tools/shorturl) - * @param string $longUrl 需转换的URL,签名用原串,传输需URLencode - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function shortUrl($longUrl) - { - $url = 'https://api.mch.weixin.qq.com/tools/shorturl'; - return $this->callPostApi($url, ['long_url' => $longUrl]); - } - - /** - * 基础 POST 调用(自动签名,可选双向证书) - * @param string $url 请求地址 - * @param array $data 接口参数 - * @param bool $isCert 是否需要双向证书(退款等场景) - * @param string $signType MD5|HMAC-SHA256 - * @param bool $needSignType 是否追加 sign_type 字段 - * @param bool $needNonceStr 是否自动附加 nonce_str - * @return array XML 解析结果 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - protected function callPostApi($url, array $data, $isCert = false, $signType = 'HMAC-SHA256', $needSignType = true, $needNonceStr = true) - { - $option = []; - if ($isCert) { - $option['ssl_p12'] = $this->config->get('ssl_p12'); - $option['ssl_cer'] = $this->config->get('ssl_cer'); - $option['ssl_key'] = $this->config->get('ssl_key'); - if (is_string($option['ssl_p12']) && file_exists($option['ssl_p12'])) { - $content = file_get_contents($option['ssl_p12']); - if (openssl_pkcs12_read($content, $certs, $this->config->get('mch_id'))) { - $option['ssl_key'] = Tools::pushFile(md5($certs['pkey']) . '.pem', $certs['pkey']); - $option['ssl_cer'] = Tools::pushFile(md5($certs['cert']) . '.pem', $certs['cert']); - } else throw new InvalidArgumentException("P12 certificate does not match MCH_ID --- ssl_p12"); - } - if (empty($option['ssl_cer']) || !file_exists($option['ssl_cer'])) { - throw new InvalidArgumentException("Missing Config -- ssl_cer", '0'); - } - if (empty($option['ssl_key']) || !file_exists($option['ssl_key'])) { - throw new InvalidArgumentException("Missing Config -- ssl_key", '0'); - } - } - $params = $this->params->merge($data); - if (!$needNonceStr) unset($params['nonce_str']); - if ($needSignType) $params['sign_type'] = strtoupper($signType); - $params['sign'] = $this->getPaySign($params, $signType); - $result = Tools::xml2arr(Tools::post($url, Tools::arr2xml($params), $option)); - if ($result['return_code'] !== 'SUCCESS') { - throw new InvalidResponseException($result['return_msg'], '0'); - } - return $result; - } - - /** - * 数组转 XML 输出 - * @param array $data 待转换数据 - * @param bool $isReturn true 返回字符串,false 直接输出 - * @return string|void - */ - public function toXml(array $data, $isReturn = false) - { - $xml = Tools::arr2xml($data); - if ($isReturn) return $xml; - echo $xml; - } - - /** - * 通用接口(V2)调用 - * @param string $url 完整 URL - * @param array|string $data 请求参数,数组自动补全商户参数并签名;字符串原样发送 - * @param string $method GET|POST|PUT|DELETE|PATCH,默认 POST(GET/HEAD/OPTIONS 不带签名) - * @param bool $isCert 是否启用双向证书 - * @param string $signType MD5|HMAC-SHA256,默认 HMAC-SHA256 - * @return array XML 解析结果 - * @throws \WeChat\Exceptions\InvalidArgumentException - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function callApi($url, $data = [], $method = 'POST', $isCert = false, $signType = 'HMAC-SHA256') - { - $method = strtoupper($method); - - // 处理数据格式 - $requestData = is_array($data) ? $data : (is_string($data) ? $data : []); - - // GET/HEAD/OPTIONS请求(无请求体,不签名) - if (in_array($method, ['GET', 'HEAD', 'OPTIONS'])) { - if (!empty($requestData) && is_array($requestData)) { - $url .= (strpos($url, '?') !== false ? '&' : '?') . http_build_query($requestData); - } - $response = Tools::get($url); - $result = Tools::xml2arr($response); - if (isset($result['return_code']) && $result['return_code'] !== 'SUCCESS') { - throw new InvalidResponseException($result['return_msg'], '0'); - } - return $result; - } - - // POST/PUT/PATCH/DELETE请求(有请求体) - $option = []; - if ($isCert) { - $option['ssl_p12'] = $this->config->get('ssl_p12'); - $option['ssl_cer'] = $this->config->get('ssl_cer'); - $option['ssl_key'] = $this->config->get('ssl_key'); - if (is_string($option['ssl_p12']) && file_exists($option['ssl_p12'])) { - $content = file_get_contents($option['ssl_p12']); - if (openssl_pkcs12_read($content, $certs, $this->config->get('mch_id'))) { - $option['ssl_key'] = Tools::pushFile(md5($certs['pkey']) . '.pem', $certs['pkey']); - $option['ssl_cer'] = Tools::pushFile(md5($certs['cert']) . '.pem', $certs['cert']); - } else { - throw new InvalidArgumentException("P12 certificate does not match MCH_ID --- ssl_p12"); - } - } - if (empty($option['ssl_cer']) || !file_exists($option['ssl_cer'])) { - throw new InvalidArgumentException("Missing Config -- ssl_cer", '0'); - } - if (empty($option['ssl_key']) || !file_exists($option['ssl_key'])) { - throw new InvalidArgumentException("Missing Config -- ssl_key", '0'); - } - } - - // 合并参数并处理签名(默认自动签名) - if (is_array($requestData)) { - $params = $this->params->merge($requestData); - $params['sign_type'] = strtoupper($signType); - $params['sign'] = $this->getPaySign($params, $signType); - $option['data'] = Tools::arr2xml($params); - } else { - $option['data'] = $requestData; - } - - // 使用doRequest支持PUT/DELETE/PATCH - $response = Tools::doRequest($method, $url, $option); - - $result = Tools::xml2arr($response); - if (isset($result['return_code']) && $result['return_code'] !== 'SUCCESS') { - throw new InvalidResponseException($result['return_msg'], '0'); - } - return $result; - } -} diff --git a/WeChat/Contracts/BasicWeWork.php b/WeChat/Contracts/BasicWeWork.php deleted file mode 100644 index 8b701a1..0000000 --- a/WeChat/Contracts/BasicWeWork.php +++ /dev/null @@ -1,42 +0,0 @@ -access_token) return $this->access_token; - $ckey = $this->config->get('appid') . '_access_token'; - if ($this->access_token = Tools::getCache($ckey)) return $this->access_token; - list($appid, $secret) = [$this->config->get('appid'), $this->config->get('appsecret')]; - $result = Tools::json2arr(Tools::get("https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={$appid}&corpsecret={$secret}")); - if (isset($result['access_token']) && $result['access_token']) Tools::setCache($ckey, $result['access_token'], 7000); - return $this->access_token = $result['access_token']; - } - -} \ No newline at end of file diff --git a/WeChat/Contracts/DataArray.php b/WeChat/Contracts/DataArray.php deleted file mode 100644 index a8fa8da..0000000 --- a/WeChat/Contracts/DataArray.php +++ /dev/null @@ -1,130 +0,0 @@ -config = $options; - } - - /** - * 设置配置项值 - * @param string $offset - * @param string|array|null|integer $value - */ - public function set($offset, $value) - { - $this->offsetSet($offset, $value); - } - - /** - * 设置配置项值 - * @param string $offset - * @param string|array|null|integer $value - * @return void - */ - #[\ReturnTypeWillChange] - public function offsetSet($offset, $value) - { - if (is_null($offset)) { - $this->config[] = $value; - } else { - $this->config[$offset] = $value; - } - } - - /** - * 获取配置项参数 - * @param string|null $offset - * @return array|string|null|mixed - */ - public function get($offset = null) - { - return $this->offsetGet($offset); - } - - /** - * 获取配置项参数 - * @param string|null $offset - * @return mixed - */ - #[\ReturnTypeWillChange] - public function offsetGet($offset = null) - { - if (is_null($offset)) return $this->config; - return isset($this->config[$offset]) ? $this->config[$offset] : null; - } - - /** - * 合并数据到对象 - * @param array $data 需要合并的数据 - * @param bool $append 是否追加数据 - * @return array - */ - public function merge(array $data, $append = false) - { - if ($append) { - return $this->config = array_merge($this->config, $data); - } - return array_merge($this->config, $data); - } - - /** - * 判断配置Key是否存在 - * @param string $offset - * @return bool - */ - #[\ReturnTypeWillChange] - public function offsetExists($offset) - { - return isset($this->config[$offset]); - } - - /** - * 清理配置项 - * @param string|null $offset - * @return void - */ - #[\ReturnTypeWillChange] - public function offsetUnset($offset = null) - { - if (is_null($offset)) { - $this->config = []; - } else { - unset($this->config[$offset]); - } - } -} \ No newline at end of file diff --git a/WeChat/Contracts/DataError.php b/WeChat/Contracts/DataError.php deleted file mode 100644 index 75638e9..0000000 --- a/WeChat/Contracts/DataError.php +++ /dev/null @@ -1,195 +0,0 @@ - '系统繁忙,此时请开发者稍候再试', - 0 => '请求成功', - 40001 => '获取 access_token 时 AppSecret 错误,或者 access_token 无效。请开发者认真比对 AppSecret 的正确性,或查看是否正在为恰当的公众号调用接口', - 40002 => '不合法的凭证类型', - 40003 => '不合法的 OpenID ,请开发者确认 OpenID (该用户)是否已关注公众号,或是否是其他公众号的 OpenID', - 40004 => '不合法的媒体文件类型', - 40005 => '不合法的文件类型', - 40006 => '不合法的文件大小', - 40007 => '不合法的媒体文件 id', - 40008 => '不合法的消息类型', - 40009 => '不合法的图片文件大小', - 40010 => '不合法的语音文件大小', - 40011 => '不合法的视频文件大小', - 40012 => '不合法的缩略图文件大小', - 40013 => '不合法的 AppID ,请开发者检查 AppID 的正确性,避免异常字符,注意大小写', - 40014 => '不合法的 access_token ,请开发者认真比对 access_token 的有效性(如是否过期),或查看是否正在为恰当的公众号调用接口', - 40015 => '不合法的菜单类型', - 40016 => '不合法的按钮个数', - 40017 => '不合法的按钮个数', - 40018 => '不合法的按钮名字长度', - 40019 => '不合法的按钮 KEY 长度', - 40020 => '不合法的按钮 URL 长度', - 40021 => '不合法的菜单版本号', - 40022 => '不合法的子菜单级数', - 40023 => '不合法的子菜单按钮个数', - 40024 => '不合法的子菜单按钮类型', - 40025 => '不合法的子菜单按钮名字长度', - 40026 => '不合法的子菜单按钮 KEY 长度', - 40027 => '不合法的子菜单按钮 URL 长度', - 40028 => '不合法的自定义菜单使用用户', - 40029 => '不合法的 oauth_code', - 40030 => '不合法的 refresh_token', - 40031 => '不合法的 openid 列表', - 40032 => '不合法的 openid 列表长度', - 40033 => '不合法的请求字符,不能包含 \\uxxxx 格式的字符', - 40035 => '不合法的参数', - 40038 => '不合法的请求格式', - 40039 => '不合法的 URL 长度', - 40050 => '不合法的分组 id', - 40051 => '分组名字不合法', - 40060 => '删除单篇图文时,指定的 article_idx 不合法', - 40117 => '分组名字不合法', - 40118 => 'media_id 大小不合法', - 40119 => 'button 类型错误', - 40120 => 'button 类型错误', - 40121 => '不合法的 media_id 类型', - 40132 => '微信号不合法', - 40137 => '不支持的图片格式', - 40155 => '请勿添加其他公众号的主页链接', - 41001 => '缺少 access_token 参数', - 41002 => '缺少 appid 参数', - 41003 => '缺少 refresh_token 参数', - 41004 => '缺少 secret 参数', - 41005 => '缺少多媒体文件数据', - 41006 => '缺少 media_id 参数', - 41007 => '缺少子菜单数据', - 41008 => '缺少 oauth code', - 41009 => '缺少 openid', - 42001 => 'access_token 超时,请检查 access_token 的有效期,请参考基础支持 - 获取 access_token 中,对 access_token 的详细机制说明', - 42002 => 'refresh_token 超时', - 42003 => 'oauth_code 超时', - 42007 => '用户修改微信密码, accesstoken 和 refreshtoken 失效,需要重新授权', - 43001 => '需要 GET 请求', - 43002 => '需要 POST 请求', - 43003 => '需要 HTTPS 请求', - 43004 => '需要接收者关注', - 43005 => '需要好友关系', - 43019 => '需要将接收者从黑名单中移除', - 44001 => '多媒体文件为空', - 44002 => 'POST 的数据包为空', - 44003 => '图文消息内容为空', - 44004 => '文本消息内容为空', - 45001 => '多媒体文件大小超过限制', - 45002 => '消息内容超过限制', - 45003 => '标题字段超过限制', - 45004 => '描述字段超过限制', - 45005 => '链接字段超过限制', - 45006 => '图片链接字段超过限制', - 45007 => '语音播放时间超过限制', - 45008 => '图文消息超过限制', - 45009 => '接口调用超过限制', - 45010 => '创建菜单个数超过限制', - 45011 => 'API 调用太频繁,请稍候再试', - 45015 => '回复时间超过限制', - 45016 => '系统分组,不允许修改', - 45017 => '分组名字过长', - 45018 => '分组数量超过上限', - 45047 => '客服接口下行条数超过上限', - 46001 => '不存在媒体数据', - 46002 => '不存在的菜单版本', - 46003 => '不存在的菜单数据', - 46004 => '不存在的用户', - 47001 => '解析 JSON/XML 内容错误', - 48001 => 'api 功能未授权,请确认公众号已获得该接口,可以在公众平台官网 - 开发者中心页中查看接口权限', - 48002 => '粉丝拒收消息(粉丝在公众号选项中,关闭了 “ 接收消息 ” )', - 48004 => 'api 接口被封禁,请登录 mp.weixin.qq.com 查看详情', - 48005 => 'api 禁止删除被自动回复和自定义菜单引用的素材', - 48006 => 'api 禁止清零调用次数,因为清零次数达到上限', - 48008 => '没有该类型消息的发送权限', - 50001 => '用户未授权该 api', - 50002 => '用户受限,可能是违规后接口被封禁', - 61451 => '参数错误 (invalid parameter)', - 61452 => '无效客服账号 (invalid kf_account)', - 61453 => '客服帐号已存在 (kf_account exsited)', - 61454 => '客服帐号名长度超过限制 ( 仅允许 10 个英文字符,不包括 @ 及 @ 后的公众号的微信号 )(invalid kf_acount length)', - 61455 => '客服帐号名包含非法字符 ( 仅允许英文 + 数字 )(illegal character in kf_account)', - 61456 => '客服帐号个数超过限制 (10 个客服账号 )(kf_account count exceeded)', - 61457 => '无效头像文件类型 (invalid file type)', - 61450 => '系统错误 (system error)', - 61500 => '日期格式错误', - 65301 => '不存在此 menuid 对应的个性化菜单', - 65302 => '没有相应的用户', - 65303 => '没有默认菜单,不能创建个性化菜单', - 65304 => 'MatchRule 信息为空', - 65305 => '个性化菜单数量受限', - 65306 => '不支持个性化菜单的帐号', - 65307 => '个性化菜单信息为空', - 65308 => '包含没有响应类型的 button', - 65309 => '个性化菜单开关处于关闭状态', - 65310 => '填写了省份或城市信息,国家信息不能为空', - 65311 => '填写了城市信息,省份信息不能为空', - 65312 => '不合法的国家信息', - 65313 => '不合法的省份信息', - 65314 => '不合法的城市信息', - 65316 => '该公众号的菜单设置了过多的域名外跳(最多跳转到 3 个域名的链接)', - 65317 => '不合法的 URL', - 9001001 => 'POST 数据参数不合法', - 9001002 => '远端服务不可用', - 9001003 => 'Ticket 不合法', - 9001004 => '获取摇周边用户信息失败', - 9001005 => '获取商户信息失败', - 9001006 => '获取 OpenID 失败', - 9001007 => '上传文件缺失', - 9001008 => '上传素材的文件类型不合法', - 9001009 => '上传素材的文件尺寸不合法', - 9001010 => '上传失败', - 9001020 => '帐号不合法', - 9001021 => '已有设备激活率低于 50% ,不能新增设备', - 9001022 => '设备申请数不合法,必须为大于 0 的数字', - 9001023 => '已存在审核中的设备 ID 申请', - 9001024 => '一次查询设备 ID 数量不能超过 50', - 9001025 => '设备 ID 不合法', - 9001026 => '页面 ID 不合法', - 9001027 => '页面参数不合法', - 9001028 => '一次删除页面 ID 数量不能超过 10', - 9001029 => '页面已应用在设备中,请先解除应用关系再删除', - 9001030 => '一次查询页面 ID 数量不能超过 50', - 9001031 => '时间区间不合法', - 9001032 => '保存设备与页面的绑定关系参数错误', - 9001033 => '门店 ID 不合法', - 9001034 => '设备备注信息过长', - 9001035 => '设备申请参数不合法', - 9001036 => '查询起始值 begin 不合法', - ]; - - /** - * 异常代码解析描述 - * @param string $code - * @return string - */ - public static function toMessage($code) - { - return isset(self::$message[$code]) ? self::$message[$code] : $code; - } - -} \ No newline at end of file diff --git a/WeChat/Contracts/MyCurlFile.php b/WeChat/Contracts/MyCurlFile.php deleted file mode 100644 index 485283d..0000000 --- a/WeChat/Contracts/MyCurlFile.php +++ /dev/null @@ -1,78 +0,0 @@ - $v) $this->{$k} = $v; - } else { - $this->mimetype = $mimetype; - $this->postname = $postname; - $this->extension = pathinfo($filename, PATHINFO_EXTENSION); - if (empty($this->extension)) $this->extension = 'tmp'; - if (empty($this->mimetype)) $this->mimetype = Tools::getExtMine($this->extension); - if (empty($this->postname)) $this->postname = pathinfo($filename, PATHINFO_BASENAME); - $this->content = base64_encode(file_get_contents($filename)); - $this->tempname = md5($this->content) . ".{$this->extension}"; - } - } - - /** - * 获取文件上传信息 - * @return \CURLFile|string - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function get() - { - $this->filename = Tools::pushFile($this->tempname, base64_decode($this->content)); - if (class_exists('CURLFile')) { - return new \CURLFile($this->filename, $this->mimetype, $this->postname); - } else { - return "@{$this->tempname};filename={$this->postname};type={$this->mimetype}"; - } - } - - /** - * 通用销毁函数清理缓存文件 - * 提前删除过期因此放到了网络请求之后 - */ - public function __destruct() - { - // Tools::delCache($this->tempname); - } - -} \ No newline at end of file diff --git a/WeChat/Contracts/Tools.php b/WeChat/Contracts/Tools.php deleted file mode 100644 index fe148e3..0000000 --- a/WeChat/Contracts/Tools.php +++ /dev/null @@ -1,528 +0,0 @@ - function ($name, $value, $expired = 360) { -// var_dump(func_get_args()); -// return $value; -// }, -// 'get' => function ($name) { -// var_dump(func_get_args()); -// return $value; -// }, -// 'del' => function ($name) { -// var_dump(func_get_args()); -// return true; -// }, -// 'put' => function ($name) { -// var_dump(func_get_args()); -// return $filePath; -// }, -// ]; - -/** - * 网络请求支持 - * @package WeChat\Contracts - */ -class Tools -{ - /** - * 缓存路径 - * @var null - */ - public static $cache_path = null; - - /** - * 缓存读写配置 - * @var array - */ - public static $cache_callable = [ - 'set' => null, // 写入缓存 ($name,$value='',$expired=3600):string - 'get' => null, // 获取缓存 ($name):mixed|null - 'del' => null, // 删除缓存 ($name):boolean - 'put' => null, // 写入文件 ($name,$content):string - ]; - - /** - * 网络缓存 - * @var array - */ - private static $cache_curl = []; - - /** - * 产生随机字符串 - * @param int $length 指定字符长度 - * @param string $str 字符串前缀 - * @return string - */ - public static function createNoncestr($length = 32, $str = "") - { - $chars = "abcdefghijklmnopqrstuvwxyz0123456789"; - for ($i = 0; $i < $length; $i++) { - $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); - } - return $str; - } - - /** - * 获取输入对象 - * @return string - */ - public static function getRawInput() - { - if (empty($GLOBALS['HTTP_RAW_POST_DATA'])) { - return file_get_contents('php://input'); - } else { - return $GLOBALS['HTTP_RAW_POST_DATA']; - } - } - - /** - * 设置输入内容 - * @param string $rawInput - * @return void - */ - public static function setRawInput($rawInput) - { - $GLOBALS['HTTP_RAW_POST_DATA'] = $rawInput; - } - - /** - * 数组转XML内容 - * @param array $data - * @return string - */ - public static function arr2xml($data) - { - return "" . self::_arr2xml($data) . ""; - } - - /** - * XML内容生成 - * @param array $data 数据 - * @param string $content - * @return string - */ - private static function _arr2xml($data, $content = '') - { - foreach ($data as $key => $val) { - is_numeric($key) && $key = 'item'; - $content .= "<{$key}>"; - if (is_array($val) || is_object($val)) { - $content .= self::_arr2xml($val); - } elseif (is_string($val)) { - $content .= ''; - } else { - $content .= $val; - } - $content .= ""; - } - return $content; - } - - /** - * 解析XML文本内容 - * @param string $xml - * @return array|false - */ - public static function xml3arr($xml) - { - $state = xml_parse($parser = xml_parser_create(), $xml, true); - return xml_parser_free($parser) && $state ? self::xml2arr($xml) : false; - } - - /** - * 解析XML内容到数组 - * @param string $xml - * @return array - */ - public static function xml2arr($xml) - { - if (PHP_VERSION_ID < 80000) { - $backup = libxml_disable_entity_loader(true); - $data = (array)simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA); - libxml_disable_entity_loader($backup); - } else { - $data = (array)simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA); - } - return json_decode(json_encode($data), true); - } - - /** - * 数组转xml内容 - * @param array $data - * @return null|string - */ - public static function arr2json($data) - { - $json = json_encode($data, JSON_UNESCAPED_UNICODE); - return $json === '[]' ? '{}' : $json; - } - - /** - * 数组对象Emoji编译处理 - * @param array $data - * @return array - */ - public static function buildEnEmojiData(array $data) - { - foreach ($data as $key => $value) { - if (is_array($value)) { - $data[$key] = self::buildEnEmojiData($value); - } elseif (is_string($value)) { - $data[$key] = self::emojiEncode($value); - } else { - $data[$key] = $value; - } - } - return $data; - } - - /** - * Emoji原形转换为String - * @param string $content - * @return string - */ - public static function emojiEncode($content) - { - return json_decode(preg_replace_callback("/(\\\u[ed][0-9a-f]{3})/i", function ($string) { - return addslashes($string[0]); - }, json_encode($content))); - } - - /** - * 数组对象Emoji反解析处理 - * @param array $data - * @return array - */ - public static function buildDeEmojiData(array $data) - { - foreach ($data as $key => $value) { - if (is_array($value)) { - $data[$key] = self::buildDeEmojiData($value); - } elseif (is_string($value)) { - $data[$key] = self::emojiDecode($value); - } else { - $data[$key] = $value; - } - } - return $data; - } - - /** - * Emoji字符串转换为原形 - * @param string $content - * @return string - */ - public static function emojiDecode($content) - { - return json_decode(preg_replace_callback('/\\\\\\\\/i', function () { - return '\\'; - }, json_encode($content))); - } - - /** - * 解析JSON内容到数组 - * @param string $json - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public static function json2arr($json) - { - $result = json_decode($json, true); - if (empty($result)) { - throw new InvalidResponseException('invalid response.', '0'); - } - if (!empty($result['errcode'])) { - throw new InvalidResponseException($result['errmsg'], $result['errcode'], $result); - } - return $result; - } - - /** - * 以get访问模拟访问 - * @param string $url 访问URL - * @param array $query GET数 - * @param array $options - * @return boolean|string - * @throws \WeChat\Exceptions\LocalCacheException - */ - public static function get($url, $query = [], $options = []) - { - $options['query'] = $query; - return self::doRequest('get', $url, $options); - } - - /** - * CURL模拟网络请求 - * @param string $method 请求方法 - * @param string $url 请求方法 - * @param array $options 请求参数[headers,data,ssl_cer,ssl_key] - * @return boolean|string - * @throws \WeChat\Exceptions\LocalCacheException - */ - public static function doRequest($method, $url, $options = []) - { - $curl = curl_init(); - // GET参数设置 - if (!empty($options['query'])) { - $url .= (stripos($url, '?') !== false ? '&' : '?') . http_build_query($options['query']); - } - // CURL头信息设置 - if (!empty($options['headers'])) { - curl_setopt($curl, CURLOPT_HTTPHEADER, $options['headers']); - } - // POST/PUT/PATCH/DELETE数据设置 - $methodLower = strtolower($method); - if (in_array($methodLower, ['post', 'put', 'patch', 'delete'])) { - if ($methodLower === 'post') { - curl_setopt($curl, CURLOPT_POST, true); - } else { - curl_setopt($curl, CURLOPT_CUSTOMREQUEST, strtoupper($method)); - } - curl_setopt($curl, CURLOPT_POSTFIELDS, self::_buildHttpData($options['data'])); - } - // 证书文件设置 - if (!empty($options['ssl_cer'])) if (file_exists($options['ssl_cer'])) { - curl_setopt($curl, CURLOPT_SSLCERTTYPE, 'PEM'); - curl_setopt($curl, CURLOPT_SSLCERT, $options['ssl_cer']); - } else throw new InvalidArgumentException("Certificate files that do not exist. --- [ssl_cer]"); - // 证书文件设置 - if (!empty($options['ssl_key'])) if (file_exists($options['ssl_key'])) { - curl_setopt($curl, CURLOPT_SSLKEYTYPE, 'PEM'); - curl_setopt($curl, CURLOPT_SSLKEY, $options['ssl_key']); - } else throw new InvalidArgumentException("Certificate files that do not exist. --- [ssl_key]"); - curl_setopt($curl, CURLOPT_URL, $url); - curl_setopt($curl, CURLOPT_TIMEOUT, isset($options['timeout']) ? intval($options['timeout']) : 60); - curl_setopt($curl, CURLOPT_HEADER, false); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); - $content = curl_exec($curl); - curl_close($curl); - // 清理 CURL 缓存文件 - if (!empty(self::$cache_curl)) foreach (self::$cache_curl as $key => $file) { - Tools::delCache($file); - unset(self::$cache_curl[$key]); - } - return $content; - } - - /** - * POST数据过滤处理 - * @param array $data 需要处理的数据 - * @param boolean $build 是否编译数据 - * @return array|string - * @throws \WeChat\Exceptions\LocalCacheException - */ - private static function _buildHttpData($data, $build = true) - { - if (!is_array($data)) return $data; - foreach ($data as $key => $value) if ($value instanceof \CURLFile) { - $build = false; - } elseif (is_object($value) && isset($value->datatype) && $value->datatype === 'MY_CURL_FILE') { - $build = false; - $mycurl = new MyCurlFile((array)$value); - $data[$key] = $mycurl->get(); - self::$cache_curl[] = $mycurl->tempname; - } elseif (is_array($value) && isset($value['datatype']) && $value['datatype'] === 'MY_CURL_FILE') { - $build = false; - $mycurl = new MyCurlFile($value); - $data[$key] = $mycurl->get(); - self::$cache_curl[] = $mycurl->tempname; - } elseif (is_string($value) && class_exists('CURLFile', false) && stripos($value, '@') === 0) { - if (($filename = realpath(trim($value, '@'))) && file_exists($filename)) { - $build = false; - $data[$key] = self::createCurlFile($filename); - } - } - return $build ? http_build_query($data) : $data; - } - - /** - * 创建CURL文件对象 - * @param mixed $filename - * @param string $mimetype - * @param string $postname - * @return \CURLFile|string - * @throws \WeChat\Exceptions\LocalCacheException - */ - public static function createCurlFile($filename, $mimetype = null, $postname = null) - { - if (is_string($filename) && file_exists($filename)) { - if (is_null($postname)) $postname = basename($filename); - if (is_null($mimetype)) $mimetype = self::getExtMine(pathinfo($filename, 4)); - if (class_exists('CURLFile')) { - return new \CURLFile($filename, $mimetype, $postname); - } else { - return "@{$filename};filename={$postname};type={$mimetype}"; - } - } - return $filename; - } - - /** - * 根据文件后缀获取文件类型 - * @param string|array $ext 文件后缀 - * @param array $mine 文件后缀MINE信息 - * @return string - * @throws \WeChat\Exceptions\LocalCacheException - */ - public static function getExtMine($ext, $mine = []) - { - $mines = self::getMines(); - foreach (is_string($ext) ? explode(',', $ext) : $ext as $e) { - $mine[] = isset($mines[strtolower($e)]) ? $mines[strtolower($e)] : 'application/octet-stream'; - } - return join(',', array_unique($mine)); - } - - /** - * 获取所有文件扩展的类型 - * @return array - * @throws \WeChat\Exceptions\LocalCacheException - */ - private static function getMines() - { - $mines = self::getCache('all_ext_mine'); - if (empty($mines)) { - $content = file_get_contents('http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types'); - preg_match_all('#^([^\s]{2,}?)\s+(.+?)$#ism', $content, $matches, PREG_SET_ORDER); - foreach ($matches as $match) foreach (explode(" ", $match[2]) as $ext) $mines[$ext] = $match[1]; - self::setCache('all_ext_mine', $mines); - } - return $mines; - } - - /** - * 获取缓存内容 - * @param string $name 缓存名称 - * @return null|mixed - */ - public static function getCache($name) - { - if (is_callable(self::$cache_callable['get'])) { - return call_user_func_array(self::$cache_callable['get'], func_get_args()); - } - $file = self::_getCacheName($name); - if (file_exists($file) && is_file($file) && ($content = file_get_contents($file))) { - $data = unserialize($content); - if (isset($data['expired']) && (intval($data['expired']) === 0 || intval($data['expired']) >= time())) { - return $data['value']; - } - self::delCache($name); - } - return null; - } - - /** - * 应用缓存目录 - * @param string $name - * @return string - */ - private static function _getCacheName($name) - { - if (empty(self::$cache_path)) { - self::$cache_path = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . 'Cache' . DIRECTORY_SEPARATOR; - } - $cachePath = rtrim((string)self::$cache_path, '/\\') . DIRECTORY_SEPARATOR; - if (!file_exists($cachePath)) { - mkdir($cachePath, 0755, true); - } - return $cachePath . $name; - } - - /** - * 移除缓存文件 - * @param string $name 缓存名称 - * @return boolean - */ - public static function delCache($name) - { - if (is_callable(self::$cache_callable['del'])) { - return call_user_func_array(self::$cache_callable['del'], func_get_args()); - } - $file = self::_getCacheName($name); - return !file_exists($file) || @unlink($file); - } - - /** - * 缓存配置与存储 - * @param string $name 缓存名称 - * @param string $value 缓存内容 - * @param int $expired 缓存时间(0表示永久缓存) - * @return string - * @throws \WeChat\Exceptions\LocalCacheException - */ - public static function setCache($name, $value = '', $expired = 3600) - { - if (is_callable(self::$cache_callable['set'])) { - return call_user_func_array(self::$cache_callable['set'], func_get_args()); - } - $file = self::_getCacheName($name); - $data = ['name' => $name, 'value' => $value, 'expired' => time() + intval($expired)]; - if (!file_put_contents($file, serialize($data))) { - throw new LocalCacheException('local cache error.', '0'); - } - return $file; - } - - /** - * 以post访问模拟访问 - * @param string $url 访问URL - * @param array $data POST数据 - * @param array $options - * @return boolean|string - * @throws \WeChat\Exceptions\LocalCacheException - */ - public static function post($url, $data = [], $options = []) - { - $options['data'] = $data; - return self::doRequest('post', $url, $options); - } - - /** - * 写入文件 - * @param string $name 文件名称 - * @param string $content 文件内容 - * @return string - * @throws \WeChat\Exceptions\LocalCacheException - */ - public static function pushFile($name, $content) - { - if (is_callable(self::$cache_callable['put'])) { - return call_user_func_array(self::$cache_callable['put'], func_get_args()); - } - $file = self::_getCacheName($name); - if (!file_put_contents($file, $content)) { - throw new LocalCacheException('local file write error.', '0'); - } - return $file; - } -} diff --git a/WeChat/Custom.php b/WeChat/Custom.php deleted file mode 100644 index 4773ce2..0000000 --- a/WeChat/Custom.php +++ /dev/null @@ -1,228 +0,0 @@ -callPostApi($url, ['kf_account' => $kf_account, 'nickname' => $nickname]); - } - - /** - * 修改客服帐号 - * @param string $kfAccount 客服账号 - * @param string $nickname 客服昵称 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function updateAccount($kfAccount, $nickname) - { - $url = "https://api.weixin.qq.com/customservice/kfaccount/update?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['kf_account' => $kfAccount, 'nickname' => $nickname]); - } - - /** - * 删除客服帐号 - * @param string $kfAccount 客服账号 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function deleteAccount($kfAccount) - { - $url = "https://api.weixin.qq.com/customservice/kfaccount/del?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['kf_account' => $kfAccount]); - } - - /** - * 邀请绑定客服帐号 - * @param string $kfAccount 客服账号,格式 账号前缀@公众号微信号 - * @param string $invite_wx 接收绑定邀请的客服微信号 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function inviteWorker($kfAccount, $invite_wx) - { - $url = 'https://api.weixin.qq.com/customservice/kfaccount/inviteworker?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['kf_account' => $kfAccount, 'invite_wx' => $invite_wx]); - } - - /** - * 获取客服账号列表 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getAccountList() - { - $url = "https://api.weixin.qq.com/cgi-bin/customservice/getkflist?access_token=ACCESS_TOKEN"; - return $this->callGetApi($url); - } - - /** - * 设置客服头像 - * @param string $kf_account 客服账号 - * @param string $image 本地图片路径 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function uploadHeadimg($kf_account, $image) - { - $url = "https://api.weixin.qq.com/customservice/kfaccount/uploadheadimg?access_token=ACCESS_TOKEN&kf_account={$kf_account}"; - return $this->callPostApi($url, ['media' => Tools::createCurlFile($image)], false); - } - - /** - * 发送客服消息 - * @param array $data 消息体(touser, msgtype, content 等) - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function send(array $data) - { - $url = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 设置客服输入状态 - * @param string $openid 用户 openid - * @param string $command Typing|CancelTyping - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function typing($openid, $command = 'Typing') - { - $url = "https://api.weixin.qq.com/cgi-bin/message/custom/typing?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['touser' => $openid, 'command' => $command]); - } - - /** - * 根据标签群发 - * @param array $data 群发参数 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function massSendAll(array $data) - { - $url = "https://api.weixin.qq.com/cgi-bin/message/mass/sendall?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 根据 OpenID 列表群发 - * @param array $data 群发参数 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function massSend(array $data) - { - $url = "https://api.weixin.qq.com/cgi-bin/message/mass/send?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 删除群发 - * @param int $msg_id 群发消息ID - * @param null|int $article_idx 图文位置,0 删除全部 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function massDelete($msg_id, $article_idx = null) - { - $data = ['msg_id' => $msg_id]; - is_null($article_idx) || $data['article_idx'] = $article_idx; - $url = "https://api.weixin.qq.com/cgi-bin/message/mass/delete?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 群发预览 - * @param array $data 预览参数 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function massPreview(array $data) - { - $url = "https://api.weixin.qq.com/cgi-bin/message/mass/preview?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 查询群发状态 - * @param int $msgId 群发消息ID - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function massGet($msgId) - { - $url = "https://api.weixin.qq.com/cgi-bin/message/mass/get?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['msg_id' => $msgId]); - } - - /** - * 获取群发速度 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function massGetSeed() - { - $url = "https://api.weixin.qq.com/cgi-bin/message/mass/speed/get?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, []); - } - - /** - * 设置群发速度 - * @param int $speed 速度级别 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function massSetSeed($speed) - { - $url = "https://api.weixin.qq.com/cgi-bin/message/mass/speed/set?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['speed' => $speed]); - } -} \ No newline at end of file diff --git a/WeChat/Draft.php b/WeChat/Draft.php deleted file mode 100644 index 28fb20d..0000000 --- a/WeChat/Draft.php +++ /dev/null @@ -1,123 +0,0 @@ -callPostApi($url, ['articles' => $articles]); - } - - /** - * 获取草稿 - * @param string $mediaId 草稿 media_id - * @param string $outType 可选回调处理 - * @return array 草稿内容 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function get($mediaId, $outType = null) - { - $url = "https://api.weixin.qq.com/cgi-bin/draft/get?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['media_id' => $mediaId]); - } - - /** - * 删除草稿 - * @param string $mediaId 草稿 media_id - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function delete($mediaId) - { - $url = "https://api.weixin.qq.com/cgi-bin/draft/delete?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['media_id' => $mediaId]); - } - - /** - * 新增图文素材 - * @param array $data 图文 articles - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function addNews($data) - { - $url = "https://api.weixin.qq.com/cgi-bin/material/add_news?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 修改草稿 - * @param string $media_id 草稿 media_id - * @param int $index 文章序号(0 开始) - * @param array $articles 文章内容 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function update($media_id, $index, $articles) - { - $url = "https://api.weixin.qq.com/cgi-bin/draft/update?access_token=ACCESS_TOKEN"; - $data = ['media_id' => $media_id, 'index' => $index, 'articles' => $articles]; - return $this->callPostApi($url, $data); - } - - /** - * 获取草稿总数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getCount() - { - $url = "https://api.weixin.qq.com/cgi-bin/draft/count?access_token=ACCESS_TOKEN"; - return $this->callGetApi($url); - } - - /** - * 获取草稿列表 - * @param int $offset 起始位置 - * @param int $count 拉取数量 1-20 - * @param int $noContent 1 不返回 content,0 返回 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function batchGet($offset = 0, $count = 20, $noContent = 0) - { - $url = "https://api.weixin.qq.com/cgi-bin/draft/batchget?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['no_content' => $noContent, 'offset' => $offset, 'count' => $count]); - } -} diff --git a/WeChat/Exceptions/InvalidArgumentException.php b/WeChat/Exceptions/InvalidArgumentException.php deleted file mode 100644 index 5def2af..0000000 --- a/WeChat/Exceptions/InvalidArgumentException.php +++ /dev/null @@ -1,41 +0,0 @@ -raw = $raw; - } -} \ No newline at end of file diff --git a/WeChat/Exceptions/InvalidDecryptException.php b/WeChat/Exceptions/InvalidDecryptException.php deleted file mode 100644 index c561aa9..0000000 --- a/WeChat/Exceptions/InvalidDecryptException.php +++ /dev/null @@ -1,41 +0,0 @@ -raw = $raw; - } -} \ No newline at end of file diff --git a/WeChat/Exceptions/InvalidInstanceException.php b/WeChat/Exceptions/InvalidInstanceException.php deleted file mode 100644 index 710d317..0000000 --- a/WeChat/Exceptions/InvalidInstanceException.php +++ /dev/null @@ -1,41 +0,0 @@ -raw = $raw; - } -} \ No newline at end of file diff --git a/WeChat/Exceptions/InvalidResponseException.php b/WeChat/Exceptions/InvalidResponseException.php deleted file mode 100644 index 20f3824..0000000 --- a/WeChat/Exceptions/InvalidResponseException.php +++ /dev/null @@ -1,42 +0,0 @@ -raw = $raw; - } - -} \ No newline at end of file diff --git a/WeChat/Exceptions/LocalCacheException.php b/WeChat/Exceptions/LocalCacheException.php deleted file mode 100644 index e0d5cb6..0000000 --- a/WeChat/Exceptions/LocalCacheException.php +++ /dev/null @@ -1,43 +0,0 @@ -raw = $raw; - } - -} \ No newline at end of file diff --git a/WeChat/Freepublish.php b/WeChat/Freepublish.php deleted file mode 100644 index d60c150..0000000 --- a/WeChat/Freepublish.php +++ /dev/null @@ -1,95 +0,0 @@ -callPostApi($url, ['media_id' => $mediaId]); - } - - /** - * 查询发布状态 - * @param string $publishId 发布任务 ID - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function get($publishId) - { - $url = "https://api.weixin.qq.com/cgi-bin/freepublish/get?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['publish_id' => $publishId]); - } - - /** - * 删除已发布文章 - * @param string $articleId 发布返回的 article_id - * @param int $index 图文序号,1 开始,0 删除全部 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function delete($articleId, $index = 0) - { - $url = "https://api.weixin.qq.com/cgi-bin/freepublish/delete?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['article_id' => $articleId, 'index' => $index]); - } - - /** - * 获取已发布文章 - * @param string $articleId article_id - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getArticle($articleId) - { - $url = "https://api.weixin.qq.com/cgi-bin/freepublish/getarticle?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['article_id' => $articleId]); - } - - /** - * 获取已发布列表 - * @param int $offset 起始偏移 - * @param int $count 数量 1-20 - * @param int $noContent 1 不返回 content - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function batchGet($offset = 0, $count = 20, $noContent = 0) - { - $url = "https://api.weixin.qq.com/cgi-bin/freepublish/batchget?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['no_content' => $noContent, 'offset' => $offset, 'count' => $count]); - } -} \ No newline at end of file diff --git a/WeChat/Limit.php b/WeChat/Limit.php deleted file mode 100644 index 85eeb24..0000000 --- a/WeChat/Limit.php +++ /dev/null @@ -1,65 +0,0 @@ -callPostApi($url, ['appid' => $this->config->get('appid')]); - } - - /** - * 网络检测 - * @param string $action ping|dns|all - * @param string $operator DEFAULT|CT|CU|CM - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function ping($action = 'all', $operator = 'DEFAULT') - { - $url = 'https://api.weixin.qq.com/cgi-bin/callback/check?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['action' => $action, 'check_operator' => $operator]); - } - - /** - * 获取微信服务器 IP - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getCallbackIp() - { - $url = 'https://api.weixin.qq.com/cgi-bin/getcallbackip?access_token=ACCESS_TOKEN'; - return $this->callGetApi($url); - } -} \ No newline at end of file diff --git a/WeChat/Media.php b/WeChat/Media.php deleted file mode 100644 index 2b3e8f3..0000000 --- a/WeChat/Media.php +++ /dev/null @@ -1,195 +0,0 @@ -callPostApi($url, ['media' => Tools::createCurlFile($filename)], false); - } - - /** - * 获取临时素材 - * @param string $media_id 媒体 ID - * @param string $outType 可选:回调处理或 'url' 仅返回 URL - * @return array|string - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function get($media_id, $outType = null) - { - $url = "https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id={$media_id}"; - $this->registerApi($url, __FUNCTION__, func_get_args()); - if ($outType == 'url') return $url; - $result = Tools::get($url); - if (is_array($json = json_decode($result, true))) { - if (!$this->isTry && isset($json['errcode']) && in_array($json['errcode'], ['40014', '40001', '41001', '42001'])) { - [$this->delAccessToken(), $this->isTry = true]; - return call_user_func_array([$this, $this->currentMethod['method']], $this->currentMethod['arguments']); - } - return Tools::json2arr($result); - } - return is_null($outType) ? $result : $outType($result); - } - - /** - * 新增永久图文素材 - * @param array $data 图文列表 articles - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function addNews($data) - { - $url = "https://api.weixin.qq.com/cgi-bin/material/add_news?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 更新图文素材 - * @param string $media_id 图文 media_id - * @param int $index 文章位置(0 开始) - * @param array $news 文章内容 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function updateNews($media_id, $index, $news) - { - $url = "https://api.weixin.qq.com/cgi-bin/material/update_news?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['media_id' => $media_id, 'index' => $index, 'articles' => $news]); - } - - /** - * 上传图文消息内的图片,获取 URL - * @param string $filename 本地文件 - * @return array 含 url - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function uploadImg($filename) - { - $url = "https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['media' => Tools::createCurlFile($filename)], false); - } - - /** - * 新增其他类型永久素材 - * @param string $filename 本地文件路径 - * @param string $type 媒体类型 image|voice|video|thumb - * @param array $description 视频描述等 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function addMaterial($filename, $type = 'image', $description = []) - { - if (!in_array($type, ['image', 'voice', 'video', 'thumb'])) { - throw new InvalidResponseException('Invalid Media Type.', '0'); - } - $url = "https://api.weixin.qq.com/cgi-bin/material/add_material?access_token=ACCESS_TOKEN&type={$type}"; - return $this->callPostApi($url, ['media' => Tools::createCurlFile($filename), 'description' => Tools::arr2json($description)], false); - } - - /** - * 获取永久素材 - * @param string $media_id 媒体 ID - * @param null|string $outType 回调处理或 'url' 返回地址 - * @return array|string - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getMaterial($media_id, $outType = null) - { - $url = "https://api.weixin.qq.com/cgi-bin/material/get_material?access_token=ACCESS_TOKEN"; - $this->registerApi($url, __FUNCTION__, func_get_args()); - if ($outType == 'url') return $url; - $result = Tools::post($url, ['media_id' => $media_id]); - if (is_array($json = json_decode($result, true))) { - if (!$this->isTry && isset($json['errcode']) && in_array($json['errcode'], ['40014', '40001', '41001', '42001'])) { - [$this->delAccessToken(), $this->isTry = true]; - return call_user_func_array([$this, $this->currentMethod['method']], $this->currentMethod['arguments']); - } - return Tools::json2arr($result); - } - return is_null($outType) ? $result : $outType($result); - } - - /** - * 删除永久素材 - * @param string $mediaId 媒体 ID - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function delMaterial($mediaId) - { - $url = "https://api.weixin.qq.com/cgi-bin/material/del_material?access_token=ACCESS_TOKEN"; - return $this->httpPostForJson($url, ['media_id' => $mediaId]); - } - - /** - * 获取素材总数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getMaterialCount() - { - $url = "https://api.weixin.qq.com/cgi-bin/material/get_materialcount?access_token=ACCESS_TOKEN"; - return $this->callGetApi($url); - } - - /** - * 获取素材列表 - * @param string $type image|voice|video|news - * @param int $offset 起始位置 - * @param int $count 拉取数量 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function batchGetMaterial($type = 'image', $offset = 0, $count = 20) - { - if (!in_array($type, ['image', 'voice', 'video', 'news'])) { - throw new InvalidResponseException('Invalid Media Type.', '0'); - } - $url = "https://api.weixin.qq.com/cgi-bin/material/batchget_material?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['type' => $type, 'offset' => $offset, 'count' => $count]); - } -} \ No newline at end of file diff --git a/WeChat/Menu.php b/WeChat/Menu.php deleted file mode 100644 index 81124f7..0000000 --- a/WeChat/Menu.php +++ /dev/null @@ -1,103 +0,0 @@ -callGetApi($url); - } - - /** - * 删除自定义菜单接口 - * @return array 操作结果 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function delete() - { - $url = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN"; - return $this->callGetApi($url); - } - - /** - * 创建自定义菜单接口 - * @param array $data 菜单配置(button数组,最多3个一级菜单,每个一级菜单最多5个二级菜单) - * @return array 操作结果 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function create(array $data) - { - $url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 创建个性化菜单接口 - * @param array $data 菜单配置(button数组和matchrule匹配规则) - * @return array 返回menuid - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function addConditional(array $data) - { - $url = "https://api.weixin.qq.com/cgi-bin/menu/addconditional?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 删除个性化菜单接口 - * @param string $menuid 菜单ID - * @return array 操作结果 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function delConditional($menuid) - { - $url = "https://api.weixin.qq.com/cgi-bin/menu/delconditional?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['menuid' => $menuid]); - } - - /** - * 测试个性化菜单匹配结果接口 - * @param string $openid 用户openid - * @return array 该用户匹配的菜单配置 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function tryConditional($openid) - { - $url = "https://api.weixin.qq.com/cgi-bin/menu/trymatch?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['user_id' => $openid]); - } -} \ No newline at end of file diff --git a/WeChat/Oauth.php b/WeChat/Oauth.php deleted file mode 100644 index 1a13e3a..0000000 --- a/WeChat/Oauth.php +++ /dev/null @@ -1,100 +0,0 @@ -config->get('appid'); - $redirect_uri = urlencode($redirect_url); - return "https://open.weixin.qq.com/connect/oauth2/authorize?appid={$appid}&redirect_uri={$redirect_uri}&response_type=code&scope={$scope}&state={$state}#wechat_redirect"; - } - - /** - * 通过 code 换取 access_token/refresh_token/openid - * @param string $code 授权 code,不传则取 GET 参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getOauthAccessToken($code = '') - { - $appid = $this->config->get('appid'); - $appsecret = $this->config->get('appsecret'); - $code = $code ? $code : (isset($_GET['code']) ? $_GET['code'] : ''); - $url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={$appid}&secret={$appsecret}&code={$code}&grant_type=authorization_code"; - return $this->httpGetForJson($url); - } - - /** - * 刷新网页授权 access_token - * @param string $refresh_token 刷新 token - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getOauthRefreshToken($refresh_token) - { - $appid = $this->config->get('appid'); - $url = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid={$appid}&grant_type=refresh_token&refresh_token={$refresh_token}"; - return $this->httpGetForJson($url); - } - - /** - * 校验网页授权 access_token 是否有效 - * @param string $accessToken 网页授权 access_token - * @param string $openid 用户 openid - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function checkOauthAccessToken($accessToken, $openid) - { - $url = "https://api.weixin.qq.com/sns/auth?access_token={$accessToken}&openid={$openid}"; - return $this->httpGetForJson($url); - } - - /** - * 拉取用户信息(snsapi_userinfo) - * @param string $accessToken 网页授权 access_token - * @param string $openid 用户 openid - * @param string $lang zh_CN|zh_TW|en - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getUserInfo($accessToken, $openid, $lang = 'zh_CN') - { - $url = "https://api.weixin.qq.com/sns/userinfo?access_token={$accessToken}&openid={$openid}&lang={$lang}"; - return $this->httpGetForJson($url); - } -} diff --git a/WeChat/Pay.php b/WeChat/Pay.php deleted file mode 100644 index c463698..0000000 --- a/WeChat/Pay.php +++ /dev/null @@ -1,232 +0,0 @@ -config->get())->create($options); - } - - /** - * 刷卡支付(被扫) - * @param array $options 支付参数(auth_code, out_trade_no, total_fee 等) - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function createMicropay($options) - { - return Order::instance($this->config->get())->micropay($options); - } - - /** - * 生成 JSAPI/H5 支付参数 - * @param string $prepay_id 统一下单返回的 prepay_id - * @return array 前端调起参数 - */ - public function createParamsForJsApi($prepay_id) - { - return Order::instance($this->config->get())->jsapiParams($prepay_id); - } - - /** - * 生成 APP 支付参数 - * @param string $prepay_id 统一下单返回的 prepay_id - * @return array APP 支付参数 - */ - public function createParamsForApp($prepay_id) - { - return Order::instance($this->config->get())->appParams($prepay_id); - } - - /** - * 生成 Native 支付二维码 URL - * @param string $product_id 商品 ID 或订单号 - * @return string - */ - public function createParamsForRuleQrc($product_id) - { - return Order::instance($this->config->get())->qrcParams($product_id); - } - - /** - * 查询订单 - * @param array $options 查询参数(transaction_id 或 out_trade_no) - * @return array 订单详情 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function queryOrder(array $options) - { - return Order::instance($this->config->get())->query($options); - } - - /** - * 关闭订单 - * @param string $out_trade_no 商户订单号 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function closeOrder($out_trade_no) - { - return Order::instance($this->config->get())->close($out_trade_no); - } - - /** - * 申请退款 - * @param array $options 退款参数(out_trade_no/transaction_id,out_refund_no,total_fee,refund_fee 等) - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function createRefund(array $options) - { - return Refund::instance($this->config->get())->create($options); - } - - /** - * 查询退款 - * @param array $options 查询参数(transaction_id/out_trade_no/out_refund_no/refund_id 四选一) - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function queryRefund(array $options) - { - return Refund::instance($this->config->get())->query($options); - } - - /** - * 交易保障上报 - * @param array $options 上报参数(interface_url, execute_time, return_code 等) - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function report(array $options) - { - return Order::instance($this->config->get())->report($options); - } - - /** - * 授权码查询 openid - * @param string $authCode 扫码支付授权码 - * @return array 含 openid - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function queryAuthCode($authCode) - { - return Order::instance($this->config->get())->queryAuthCode($authCode); - } - - /** - * 下载对账单 - * @param array $options 账单参数(bill_date, bill_type 等) - * @param null|string $outType 输出处理回调,为 null 返回原始内容 - * @return bool|string - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function billDownload(array $options, $outType = null) - { - return Bill::instance($this->config->get())->download($options, $outType); - } - - /** - * 拉取订单评价数据(需证书) - * @param array $options 查询参数(bill_date, offset, limit 等) - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function billCommtent(array $options) - { - return Bill::instance($this->config->get())->comment($options); - } - - /** - * 企业付款到零钱 - * @param array $options 付款参数(partner_trade_no, openid, amount, desc 等) - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function createTransfers(array $options) - { - return Transfers::instance($this->config->get())->create($options); - } - - /** - * 查询企业付款到零钱 - * @param string $partner_trade_no 商户付款单号 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function queryTransfers($partner_trade_no) - { - return Transfers::instance($this->config->get())->query($partner_trade_no); - } - - /** - * 企业付款到银行卡 - * @param array $options 付款参数(partner_trade_no, enc_bank_no, enc_true_name, bank_code, amount 等) - * @return array - * @throws \WeChat\Exceptions\InvalidDecryptException - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function createTransfersBank(array $options) - { - return TransfersBank::instance($this->config->get())->create($options); - } - - /** - * 查询企业付款到银行卡结果 - * @param string $partner_trade_no 商户付款单号 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function queryTransFresBank($partner_trade_no) - { - return TransfersBank::instance($this->config->get())->query($partner_trade_no); - } -} \ No newline at end of file diff --git a/WeChat/Product.php b/WeChat/Product.php deleted file mode 100644 index 9d6d6a7..0000000 --- a/WeChat/Product.php +++ /dev/null @@ -1,165 +0,0 @@ - $keystandard, 'keystr' => $keystr, 'status' => $status]; - return $this->callPostApi($url, $data); - } - - /** - * 设置测试人员白名单 - * @param array $openids openid 列表 - * @param array $usernames 微信号列表 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function setTestWhiteList(array $openids = [], array $usernames = []) - { - $url = "https://api.weixin.qq.com/scan/testwhitelist/set?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['openid' => $openids, 'username' => $usernames]); - } - - /** - * 获取商品二维码 - * @param string $keystandard 编码标准 - * @param string $keystr 编码内容 - * @param int $qrcode_size 边长像素,默认100 - * @param array $extinfo 自定义扩展 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getQrcode($keystandard, $keystr, $qrcode_size, $extinfo = []) - { - $url = "https://api.weixin.qq.com/scan/product/getqrcode?access_token=ACCESS_TOKEN"; - $data = ['keystandard' => $keystandard, 'keystr' => $keystr, 'qrcode_size' => $qrcode_size]; - empty($extinfo) || $data['extinfo'] = $extinfo; - return $this->callPostApi($url, $data); - } - - /** - * 查询商品信息 - * @param string $keystandard 商品编码标准 - * @param string $keystr 商品编码内容 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getProduct($keystandard, $keystr) - { - $url = "https://api.weixin.qq.com/scan/product/get?access_token=ACCESS_TOKEN"; - $data = ['keystandard' => $keystandard, 'keystr' => $keystr]; - empty($extinfo) || $data['extinfo'] = $extinfo; - return $this->callPostApi($url, $data); - } - - /** - * 批量查询商品信息 - * @param int $offset 起始位置 - * @param int $limit 数量 - * @param null|string $status on|off|check|reject|all - * @param string $keystr 模糊编码过滤 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getProductList($offset, $limit = 10, $status = null, $keystr = '') - { - $url = "https://api.weixin.qq.com/scan/product/get?access_token=ACCESS_TOKEN"; - $data = ['offset' => $offset, 'limit' => $limit]; - is_null($status) || $data['status'] = $status; - empty($keystr) || $data['keystr'] = $keystr; - return $this->callPostApi($url, $data); - } - - /** - * 更新商品信息 - * @param array $data 商品数据 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function updateProduct(array $data) - { - $url = "https://api.weixin.qq.com/scan/product/update?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 清除商品信息 - * @param string $keystandard 商品编码标准 - * @param string $keystr 商品编码内容 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function clearProduct($keystandard, $keystr) - { - $url = "https://api.weixin.qq.com/scan/product/clear?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['keystandard' => $keystandard, 'keystr' => $keystr]); - } - - /** - * 检查 wxticket 参数 - * @param string $ticket - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function scanTicketCheck($ticket) - { - $url = "https://api.weixin.qq.com/scan/scanticket/check?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['ticket' => $ticket]); - } - - /** - * 清除扫码记录 - * @param string $keystandard 商品编码标准 - * @param string $keystr 商品编码内容 - * @param string $extinfo 获取二维码时的 extinfo - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function clearScanticket($keystandard, $keystr, $extinfo) - { - $url = "https://api.weixin.qq.com/scan/scanticket/check?access_token=ACCESS_TOKEN"; - $data = ['keystandard' => $keystandard, 'keystr' => $keystr, 'extinfo' => $extinfo]; - return $this->callPostApi($url, $data); - } -} \ No newline at end of file diff --git a/WeChat/Prpcrypt/ErrorCode.php b/WeChat/Prpcrypt/ErrorCode.php deleted file mode 100644 index fb1be61..0000000 --- a/WeChat/Prpcrypt/ErrorCode.php +++ /dev/null @@ -1,52 +0,0 @@ - '处理成功', - '40001' => '校验签名失败', - '40002' => '解析xml失败', - '40003' => '计算签名失败', - '40004' => '不合法的AESKey', - '40005' => '校验AppID失败', - '40006' => 'AES加密失败', - '40007' => 'AES解密失败', - '40008' => '公众平台发送的xml不合法', - '40009' => 'Base64编码失败', - '40010' => 'Base64解码失败', - '40011' => '公众帐号生成回包xml失败', - ]; - - /** - * 获取错误消息内容 - * @param string $code 错误代码 - * @return bool - */ - public static function getErrText($code) - { - if (isset(self::$errCode[$code])) { - return self::$errCode[$code]; - } - return false; - } - -} \ No newline at end of file diff --git a/WeChat/Prpcrypt/PKCS7Encoder.php b/WeChat/Prpcrypt/PKCS7Encoder.php deleted file mode 100644 index 3192446..0000000 --- a/WeChat/Prpcrypt/PKCS7Encoder.php +++ /dev/null @@ -1,45 +0,0 @@ - PKCS7Encoder::$blockSize) { - $pad = 0; - } - return substr($text, 0, strlen($text) - $pad); - } - -} \ No newline at end of file diff --git a/WeChat/Prpcrypt/Prpcrypt.php b/WeChat/Prpcrypt/Prpcrypt.php deleted file mode 100644 index f61482d..0000000 --- a/WeChat/Prpcrypt/Prpcrypt.php +++ /dev/null @@ -1,97 +0,0 @@ -key = base64_decode("{$key}="); - } - - /** - * 对明文进行加密 - * @param string $text 需要加密的明文 - * @param string $appid 公众号APPID - * @return array - */ - public function encrypt($text, $appid) - { - try { - $random = $this->getRandomStr(); - $iv = substr($this->key, 0, 16); - $pkcEncoder = new PKCS7Encoder(); - $text = $pkcEncoder->encode($random . pack("N", strlen($text)) . $text . $appid); - $encrypted = openssl_encrypt($text, 'AES-256-CBC', substr($this->key, 0, 32), OPENSSL_ZERO_PADDING, $iv); - return [ErrorCode::$OK, $encrypted]; - } catch (\Exception $e) { - return [ErrorCode::$EncryptAESError, null]; - } - } - - /** - * 随机生成16位字符串 - * @param string $str - * @return string 生成的字符串 - */ - function getRandomStr($str = "") - { - $str_pol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"; - $max = strlen($str_pol) - 1; - for ($i = 0; $i < 16; $i++) { - $str .= $str_pol[mt_rand(0, $max)]; - } - return $str; - } - - /** - * 对密文进行解密 - * @param string $encrypted 需要解密的密文 - * @return array - */ - public function decrypt($encrypted) - { - try { - $iv = substr($this->key, 0, 16); - $decrypted = openssl_decrypt($encrypted, 'AES-256-CBC', substr($this->key, 0, 32), OPENSSL_ZERO_PADDING, $iv); - } catch (\Exception $e) { - return [ErrorCode::$DecryptAESError, null]; - } - try { - $pkcEncoder = new PKCS7Encoder(); - $result = $pkcEncoder->decode($decrypted); - if (strlen($result) < 16) { - return [ErrorCode::$DecryptAESError, null]; - } - $content = substr($result, 16, strlen($result)); - $len_list = unpack("N", substr($content, 0, 4)); - $xml_len = $len_list[1]; - return [0, substr($content, 4, $xml_len), substr($content, $xml_len + 4)]; - } catch (\Exception $e) { - return [ErrorCode::$IllegalBuffer, null]; - } - } - -} diff --git a/WeChat/Qrcode.php b/WeChat/Qrcode.php deleted file mode 100644 index 51efb9f..0000000 --- a/WeChat/Qrcode.php +++ /dev/null @@ -1,75 +0,0 @@ - ['scene' => ['scene_id' => $scene]]]; - } else { - $data = ['action_info' => ['scene' => ['scene_str' => $scene]]]; - } - if ($expire_seconds > 0) { // 临时二维码 - $data['expire_seconds'] = $expire_seconds; - $data['action_name'] = is_integer($scene) ? 'QR_SCENE' : 'QR_STR_SCENE'; - } else { // 永久二维码 - $data['action_name'] = is_integer($scene) ? 'QR_LIMIT_SCENE' : 'QR_LIMIT_STR_SCENE'; - } - $url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 通过 ticket 换取二维码 URL - * @param string $ticket 二维码 ticket - * @return string 图片 URL - */ - public function url($ticket) - { - return "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" . urlencode($ticket); - } - - /** - * 长链接转短链接 - * @param string $longUrl 长链接 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function shortUrl($longUrl) - { - $url = "https://api.weixin.qq.com/cgi-bin/shorturl?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['action' => 'long2short', 'long_url' => $longUrl]); - } -} \ No newline at end of file diff --git a/WeChat/Receive.php b/WeChat/Receive.php deleted file mode 100644 index b1d68c8..0000000 --- a/WeChat/Receive.php +++ /dev/null @@ -1,165 +0,0 @@ -message = [ - 'CreateTime' => time(), - 'ToUserName' => $this->getOpenid(), - 'FromUserName' => $this->getToOpenid(), - 'MsgType' => 'transfer_customer_service', - ]; - empty($account) || $this->message['TransInfo'] = ['KfAccount' => $account]; - return $this; - } - - /** - * 设置文本消息 - * @param string $content 文本内容 - * @return $this - */ - public function text($content = '') - { - $this->message = [ - 'MsgType' => 'text', - 'CreateTime' => time(), - 'Content' => $content, - 'ToUserName' => $this->getOpenid(), - 'FromUserName' => $this->getToOpenid(), - ]; - return $this; - } - - /** - * 设置图文消息 - * @param array $newsData Articles 列表 - * @return $this - */ - public function news($newsData = []) - { - $this->message = [ - 'CreateTime' => time(), - 'MsgType' => 'news', - 'Articles' => $newsData, - 'ToUserName' => $this->getOpenid(), - 'FromUserName' => $this->getToOpenid(), - 'ArticleCount' => count($newsData), - ]; - return $this; - } - - /** - * 设置图片消息 - * @param string $mediaId 图片 media_id - * @return $this - */ - public function image($mediaId = '') - { - $this->message = [ - 'MsgType' => 'image', - 'CreateTime' => time(), - 'ToUserName' => $this->getOpenid(), - 'FromUserName' => $this->getToOpenid(), - 'Image' => ['MediaId' => $mediaId], - ]; - return $this; - } - - /** - * 设置语音消息 - * @param string $mediaid 语音 media_id - * @return $this - */ - public function voice($mediaid = '') - { - $this->message = [ - 'CreateTime' => time(), - 'MsgType' => 'voice', - 'ToUserName' => $this->getOpenid(), - 'FromUserName' => $this->getToOpenid(), - 'Voice' => ['MediaId' => $mediaid], - ]; - return $this; - } - - /** - * 设置视频消息 - * @param string $mediaid 视频 media_id - * @param string $title 标题 - * @param string $description 描述 - * @return $this - */ - public function video($mediaid = '', $title = '', $description = '') - { - $this->message = [ - 'CreateTime' => time(), - 'MsgType' => 'video', - 'ToUserName' => $this->getOpenid(), - 'FromUserName' => $this->getToOpenid(), - 'Video' => [ - 'Title' => $title, - 'MediaId' => $mediaid, - 'Description' => $description, - ], - ]; - return $this; - } - - /** - * 设置音乐消息 - * @param string $title 标题 - * @param string $desc 描述 - * @param string $musicurl 音乐链接 - * @param string $hgmusicurl 高清链接 - * @param string $thumbmediaid 缩略图 media_id - * @return $this - */ - public function music($title, $desc, $musicurl, $hgmusicurl = '', $thumbmediaid = '') - { - $this->message = [ - 'CreateTime' => time(), - 'MsgType' => 'music', - 'ToUserName' => $this->getOpenid(), - 'FromUserName' => $this->getToOpenid(), - 'Music' => [ - 'Title' => $title, - 'Description' => $desc, - 'MusicUrl' => $musicurl, - 'HQMusicUrl' => $hgmusicurl, - ], - ]; - if ($thumbmediaid) { - $this->message['Music']['ThumbMediaId'] = $thumbmediaid; - } - return $this; - } -} \ No newline at end of file diff --git a/WeChat/Scan.php b/WeChat/Scan.php deleted file mode 100644 index 6f5df49..0000000 --- a/WeChat/Scan.php +++ /dev/null @@ -1,187 +0,0 @@ -callGetApi($url); - } - - /** - * 创建商品 - * @param array $data 商品数据 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function addProduct(array $data) - { - $url = "https://api.weixin.qq.com/scan/product/create?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 商品发布/取消 - * @param string $keystandard 商品编码标准 - * @param string $keystr 商品编码内容 - * @param string $status on 提交审核 | off 取消 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function modProduct($keystandard, $keystr, $status = 'on') - { - $url = "https://api.weixin.qq.com/scan/product/modstatus?access_token=ACCESS_TOKEN"; - $data = ['keystandard' => $keystandard, 'keystr' => $keystr, 'status' => $status]; - return $this->callPostApi($url, $data); - } - - /** - * 设置测试人员白名单 - * @param array $openids openid 列表 - * @param array $usernames 微信号列表 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function setTestWhiteList($openids = [], $usernames = []) - { - $url = "https://api.weixin.qq.com/scan/product/modstatus?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['openid' => $openids, 'username' => $usernames]); - } - - /** - * 获取商品二维码 - * @param string $keystandard 编码标准 - * @param string $keystr 编码内容 - * @param null|string $extinfo 自定义标识 - * @param int $qrcode_size 边长像素 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getQrc($keystandard, $keystr, $extinfo = null, $qrcode_size = 64) - { - $url = "https://api.weixin.qq.com/scan/product/getqrcode?access_token=ACCESS_TOKEN"; - $data = ['keystandard' => $keystandard, 'keystr' => $keystr, 'qrcode_size' => $qrcode_size]; - is_null($extinfo) || $data['extinfo'] = $extinfo; - return $this->callPostApi($url, $data); - } - - /** - * 查询商品信息 - * @param string $keystandard 商品编码标准 - * @param string $keystr 商品编码内容 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getProductInfo($keystandard, $keystr) - { - $url = "https://api.weixin.qq.com/scan/product/get?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['keystandard' => $keystandard, 'keystr' => $keystr]); - } - - /** - * 批量查询商品信息 - * @param int $offset 起始位置 - * @param int $limit 数量 - * @param string $status on|off|check|reject|all - * @param string $keystr 关键词过滤 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getProductList($offset = 1, $limit = 10, $status = null, $keystr = null) - { - $url = "https://api.weixin.qq.com/scan/product/getlist?access_token=ACCESS_TOKEN"; - $data = ['offset' => $offset, 'limit' => $limit]; - is_null($status) || $data['status'] = $status; - is_null($keystr) || $data['keystr'] = $keystr; - return $this->callPostApi($url, $data); - } - - /** - * 更新商品信息 - * @param array $data 商品数据 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function updateProduct(array $data) - { - $url = "https://api.weixin.qq.com/scan/product/update?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 清除商品信息 - * @param string $keystandard 商品编码标准 - * @param string $keystr 商品编码内容 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function clearProduct($keystandard, $keystr) - { - $url = "https://api.weixin.qq.com/scan/product/clear?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['keystandard' => $keystandard, 'keystr' => $keystr]); - } - - /** - * 检查 wxticket 参数 - * @param string $ticket - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function checkTicket($ticket) - { - $url = "https://api.weixin.qq.com/scan/scanticket/check?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['ticket' => $ticket]); - } - - /** - * 清除扫码记录 - * @param string $keystandard 商品编码标准 - * @param string $keystr 商品编码内容 - * @param string $extinfo 二维码接口时的 extinfo - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function clearScanTicket($keystandard, $keystr, $extinfo) - { - $url = "https://api.weixin.qq.com/scan/scanticket/check?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['keystandard' => $keystandard, 'keystr' => $keystr, 'extinfo' => $extinfo]); - } -} \ No newline at end of file diff --git a/WeChat/Script.php b/WeChat/Script.php deleted file mode 100644 index 243012a..0000000 --- a/WeChat/Script.php +++ /dev/null @@ -1,116 +0,0 @@ -config->get('appid'); - $cache_name = "{$appid}_ticket_{$type}"; - Tools::delCache($cache_name); - } - - /** - * 获取 JSAPI 签名 - * @param string $url 当前页面 URL(不含 #) - * @param string $appid 可选指定 appid - * @param string $ticket 可选指定 ticket - * @param array $jsApiList 需注入的 API 列表 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getJsSign($url, $appid = null, $ticket = null, $jsApiList = null) - { - list($url,) = explode('#', $url); - is_null($ticket) && $ticket = $this->getTicket('jsapi'); - is_null($appid) && $appid = $this->config->get('appid'); - is_null($jsApiList) && $jsApiList = [ - 'updateAppMessageShareData', 'updateTimelineShareData', 'onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareWeibo', 'onMenuShareQZone', - 'startRecord', 'stopRecord', 'onVoiceRecordEnd', 'playVoice', 'pauseVoice', 'stopVoice', 'onVoicePlayEnd', 'uploadVoice', 'downloadVoice', - 'chooseImage', 'previewImage', 'uploadImage', 'downloadImage', 'translateVoice', 'getNetworkType', 'openLocation', 'getLocation', - 'hideOptionMenu', 'showOptionMenu', 'hideMenuItems', 'showMenuItems', 'hideAllNonBaseMenuItem', 'showAllNonBaseMenuItem', - 'closeWindow', 'scanQRCode', 'chooseWXPay', 'openProductSpecificView', 'addCard', 'chooseCard', 'openCard', - ]; - $data = ["url" => $url, "timestamp" => '' . time(), "jsapi_ticket" => $ticket, "noncestr" => Tools::createNoncestr(16)]; - return [ - 'debug' => false, - "appId" => $appid, - "nonceStr" => $data['noncestr'], - "timestamp" => $data['timestamp'], - "signature" => $this->getSignature($data, 'sha1'), - 'jsApiList' => $jsApiList, - ]; - } - - /** - * 获取 JSAPI_TICKET - * @param string $type jsapi|wx_card - * @param string $appid 可选指定 appid - * @return string ticket - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getTicket($type = 'jsapi', $appid = null) - { - is_null($appid) && $appid = $this->config->get('appid'); - $cache_name = "{$appid}_ticket_{$type}"; - $ticket = Tools::getCache($cache_name); - if (empty($ticket)) { - $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type={$type}"; - $this->registerApi($url, __FUNCTION__, func_get_args()); - $result = $this->httpGetForJson($url); - if (empty($result['ticket'])) { - throw new InvalidResponseException('Invalid Resoponse Ticket.', '0'); - } - $ticket = $result['ticket']; - Tools::setCache($cache_name, $ticket, 7000); - } - return $ticket; - } - - /** - * 数据生成签名 - * @param array $data 待签名数据 - * @param string $method 签名方法 - * @param array $params 额外参数 - * @return bool|string - */ - protected function getSignature($data, $method = "sha1", $params = []) - { - ksort($data); - if (!function_exists($method)) return false; - foreach ($data as $k => $v) $params[] = "{$k}={$v}"; - return $method(join('&', $params)); - } -} \ No newline at end of file diff --git a/WeChat/Shake.php b/WeChat/Shake.php deleted file mode 100644 index 2b10278..0000000 --- a/WeChat/Shake.php +++ /dev/null @@ -1,340 +0,0 @@ -callPostApi($url, $data); - } - - /** - * 查询审核状态 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function auditStatus() - { - $url = "https://api.weixin.qq.com/shakearound/account/auditstatus?access_token=ACCESS_TOKEN"; - return $this->callGetApi($url); - } - - /** - * 申请设备 ID - * @param string $quantity 数量(>500 走人工审核) - * @param string $apply_reason 理由 - * @param null|string $comment 备注 - * @param null|string $poi_id 门店ID - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function createApply($quantity, $apply_reason, $comment = null, $poi_id = null) - { - $data = ['quantity' => $quantity, 'apply_reason' => $apply_reason]; - is_null($poi_id) || $data['poi_id'] = $poi_id; - is_null($comment) || $data['comment'] = $comment; - $url = "https://api.weixin.qq.com/shakearound/device/applyid?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 查询设备 ID 申请状态 - * @param int $applyId 批次ID - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getApplyStatus($applyId) - { - $url = "https://api.weixin.qq.com/shakearound/device/applyid?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['apply_id' => $applyId]); - } - - /** - * 编辑设备信息 - * @param array $data 设备参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function updateApply(array $data) - { - $url = "https://api.weixin.qq.com/shakearound/device/update?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 设备绑定门店 - * @param array $data 绑定参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function bindLocation(array $data) - { - $url = "https://api.weixin.qq.com/shakearound/device/bindlocation?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 查询设备列表 - * @param array $data 查询参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function search(array $data) - { - $url = "https://api.weixin.qq.com/shakearound/device/search?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 创建页面 - * @param array $data 页面参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function createPage(array $data) - { - $url = "https://api.weixin.qq.com/shakearound/page/add?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 编辑页面 - * @param array $data 页面参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function updatePage(array $data) - { - $url = "https://api.weixin.qq.com/shakearound/page/update?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 查询页面列表 - * @param array $data 查询参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function searchPage(array $data) - { - $url = "https://api.weixin.qq.com/shakearound/page/search?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 删除页面 - * @param int $pageId 页面ID - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function deletePage($pageId) - { - $url = "https://api.weixin.qq.com/shakearound/page/delete?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['page_id' => $pageId]); - } - - /** - * 上传图片素材 - * @param string $filename 图片路径 - * @param string $type icon|license - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function upload($filename, $type = 'icon') - { - $url = "https://api.weixin.qq.com/shakearound/material/add?access_token=ACCESS_TOKEN&type={$type}"; - return $this->callPostApi($url, ['media' => Tools::createCurlFile($filename)], false); - } - - /** - * 配置设备与页面关联 - * @param array $data 关联参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function bindPage(array $data) - { - $url = "https://api.weixin.qq.com/shakearound/device/bindpage?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 查询设备与页面关联 - * @param array $data 查询参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function queryPage(array $data) - { - $url = "https://api.weixin.qq.com/shakearound/relation/search?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 设备维度数据统计 - * @param array $data 查询参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function totalDevice(array $data) - { - $url = "https://api.weixin.qq.com/shakearound/statistics/device?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 批量查询设备统计 - * @param int $date 日期时间戳(秒) - * @param int $pageIndex 页码 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function totalDeviceList($date, $pageIndex = 1) - { - $url = "https://api.weixin.qq.com/shakearound/statistics/devicelist?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['date' => $date, 'page_index' => $pageIndex]); - } - - /** - * 页面维度数据统计 - * @param int $pageId 页面ID - * @param int $beginDate 起始时间戳 - * @param int $endDate 结束时间戳 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function totalPage($pageId, $beginDate, $endDate) - { - $url = "https://api.weixin.qq.com/shakearound/statistics/page?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['page_id' => $pageId, 'begin_date' => $beginDate, 'end_date' => $endDate]); - } - - /** - * 编辑分组信息 - * @param int $groupId 分组ID - * @param string $groupName 分组名称 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function updateGroup($groupId, $groupName) - { - $url = "https://api.weixin.qq.com/shakearound/device/group/update?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['group_id' => $groupId, 'group_name' => $groupName]); - } - - /** - * 删除分组 - * @param int $groupId 分组ID - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function deleteGroup($groupId) - { - $url = "https://api.weixin.qq.com/shakearound/device/group/delete?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['group_id' => $groupId]); - } - - /** - * 查询分组列表 - * @param int $begin 起始索引 - * @param int $count 数量(<=1000) - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getGroupList($begin = 0, $count = 10) - { - $url = "https://api.weixin.qq.com/shakearound/device/group/getlist?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['begin' => $begin, 'count' => $count]); - } - - - /** - * 查询分组详情 - * @param int $group_id 分组ID - * @param int $begin 设备起始索引 - * @param int $count 数量(<=1000) - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getGroupDetail($group_id, $begin = 0, $count = 100) - { - $url = "https://api.weixin.qq.com/shakearound/device/group/getdetail?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['group_id' => $group_id, 'begin' => $begin, 'count' => $count]); - } - - /** - * 分组添加设备 - * @param array $data 设备参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function addDeviceGroup(array $data) - { - $url = "https://api.weixin.qq.com/shakearound/device/group/adddevice?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } - - /** - * 分组移除设备 - * @param array $data 设备参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function deleteDeviceGroup(array $data) - { - $url = "https://api.weixin.qq.com/shakearound/device/group/deletedevice?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } -} \ No newline at end of file diff --git a/WeChat/Tags.php b/WeChat/Tags.php deleted file mode 100644 index 268a417..0000000 --- a/WeChat/Tags.php +++ /dev/null @@ -1,119 +0,0 @@ -callGetApi($url); - } - - /** - * 创建标签 - * @param string $name 标签名称 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function createTags($name) - { - $url = "https://api.weixin.qq.com/cgi-bin/tags/create?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['tag' => ['name' => $name]]); - } - - /** - * 更新标签 - * @param int $id 标签ID - * @param string $name 标签名称 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function updateTags($id, $name) - { - $url = "https://api.weixin.qq.com/cgi-bin/tags/update?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['tag' => ['name' => $name, 'id' => $id]]); - } - - /** - * 删除标签 - * @param int $tagId 标签ID - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function deleteTags($tagId) - { - $url = 'https://api.weixin.qq.com/cgi-bin/tags/delete?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['tag' => ['id' => $tagId]]); - } - - /** - * 批量为用户打标签 - * @param array $openids openid 列表 - * @param int $tagId 标签ID - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function batchTagging(array $openids, $tagId) - { - $url = 'https://api.weixin.qq.com/cgi-bin/tags/members/batchtagging?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['openid_list' => $openids, 'tagid' => $tagId]); - } - - /** - * 批量取消用户标签 - * @param array $openids openid 列表 - * @param int $tagId 标签ID - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function batchUntagging(array $openids, $tagId) - { - $url = 'https://api.weixin.qq.com/cgi-bin/tags/members/batchuntagging?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['openid_list' => $openids, 'tagid' => $tagId]); - } - - /** - * 获取用户标签列表 - * @param string $openid 用户 openid - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getUserTagId($openid) - { - $url = 'https://api.weixin.qq.com/cgi-bin/tags/getidlist?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['openid' => $openid]); - } -} \ No newline at end of file diff --git a/WeChat/Template.php b/WeChat/Template.php deleted file mode 100644 index 5cbe498..0000000 --- a/WeChat/Template.php +++ /dev/null @@ -1,104 +0,0 @@ -callPostApi($url, ['industry_id1' => $industryId1, 'industry_id2' => $industryId2]); - } - - /** - * 获取已设置的行业信息 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getIndustry() - { - $url = "https://api.weixin.qq.com/cgi-bin/template/get_industry?access_token=ACCESS_TOKEN"; - return $this->callGetApi($url); - } - - /** - * 领取模板 ID - * @param string $templateIdShort 模板编号(如 TM** 或 OPENTMTM**) - * @param array $keywordNameList 选用关键词 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function addTemplate($templateIdShort, $keywordNameList = []) - { - $url = "https://api.weixin.qq.com/cgi-bin/template/api_add_template?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['template_id_short' => $templateIdShort, 'keyword_name_list' => $keywordNameList]); - } - - /** - * 获取私有模板列表 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getAllPrivateTemplate() - { - $url = "https://api.weixin.qq.com/cgi-bin/template/get_all_private_template?access_token=ACCESS_TOKEN"; - return $this->callGetApi($url); - } - - /** - * 删除模板 - * @param string $tplId 模板 ID - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function delPrivateTemplate($tplId) - { - $url = "https://api.weixin.qq.com/cgi-bin/template/del_private_template?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['template_id' => $tplId]); - } - - /** - * 发送模板消息 - * @param array $data 消息内容(touser, template_id, data 等) - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function send(array $data) - { - $url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data); - } -} \ No newline at end of file diff --git a/WeChat/User.php b/WeChat/User.php deleted file mode 100644 index 49155bf..0000000 --- a/WeChat/User.php +++ /dev/null @@ -1,139 +0,0 @@ -callPostApi($url, ['openid' => $openid, 'remark' => $remark]); - } - - /** - * 获取用户基本信息接口(包括UnionID) - * @param string $openid 用户openid - * @param string $lang 返回国家地区语言版本(zh_CN, zh_TW, en) - * @return array 用户信息(nickname, headimgurl, sex, province, city, country, unionid等) - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getUserInfo($openid, $lang = 'zh_CN') - { - $url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid={$openid}&lang={$lang}"; - return $this->callGetApi($url); - } - - /** - * 批量获取用户基本信息接口 - * @param array $openids 用户openid列表(最多100个) - * @param string $lang 返回国家地区语言版本 - * @return array 用户信息列表 - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getBatchUserInfo(array $openids, $lang = 'zh_CN') - { - $url = 'https://api.weixin.qq.com/cgi-bin/user/info/batchget?access_token=ACCESS_TOKEN'; - $data = ['user_list' => []]; - foreach ($openids as $openid) { - $data['user_list'][] = ['openid' => $openid, 'lang' => $lang]; - } - return $this->callPostApi($url, $data); - } - - /** - * 获取用户列表接口 - * @param string $next_openid 第一个拉取的openid,不填默认从头开始拉取 - * @return array 用户列表(total, count, data.openid, next_openid) - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getUserList($next_openid = '') - { - $url = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&next_openid={$next_openid}"; - return $this->callGetApi($url); - } - - /** - * 获取标签下粉丝列表接口 - * @param integer $tagid 标签ID - * @param string $nextOpenid 第一个拉取的openid,不填默认从头开始拉取 - * @return array 粉丝列表(count, data.openid, next_openid) - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getUserListByTag($tagid, $nextOpenid = '') - { - $url = 'https://api.weixin.qq.com/cgi-bin/user/tag/get?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['tagid' => $tagid, 'next_openid' => $nextOpenid]); - } - - /** - * 获取公众号黑名单列表接口 - * @param string $beginOpenid 第一个拉取的openid,不填默认从头开始拉取 - * @return array 黑名单列表(total, count, data.openid, next_openid) - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getBlackList($beginOpenid = '') - { - $url = "https://api.weixin.qq.com/cgi-bin/tags/members/getblacklist?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['begin_openid' => $beginOpenid]); - } - - /** - * 批量拉黑用户接口 - * @param array $openids 用户openid列表(最多20个) - * @return array 操作结果 - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function batchBlackList(array $openids) - { - $url = "https://api.weixin.qq.com/cgi-bin/tags/members/batchblacklist?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['openid_list' => $openids]); - } - - /** - * 批量取消拉黑用户接口 - * @param array $openids 用户openid列表(最多20个) - * @return array 操作结果 - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function batchUnblackList(array $openids) - { - $url = "https://api.weixin.qq.com/cgi-bin/tags/members/batchunblacklist?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['openid_list' => $openids]); - } -} \ No newline at end of file diff --git a/WeChat/Wifi.php b/WeChat/Wifi.php deleted file mode 100644 index 6c4ec5d..0000000 --- a/WeChat/Wifi.php +++ /dev/null @@ -1,285 +0,0 @@ -registerApi($url, __FUNCTION__, func_get_args()); - return $this->httpPostForJson($url, ['pageindex' => $pageindex, 'pagesize' => $pagesize]); - } - - /** - * 查询门店 Wi-Fi 信息 - * @param int $shop_id 门店ID - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getShopWifi($shop_id) - { - $url = 'https://api.weixin.qq.com/bizwifi/shop/list?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->httpPostForJson($url, ['shop_id' => $shop_id]); - } - - /** - * 修改门店 Wi-Fi - * @param int $shop_id 门店ID - * @param string $old_ssid 原 SSID - * @param string $ssid 新 SSID - * @param string $password 可选密码 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function upShopWifi($shop_id, $old_ssid, $ssid, $password = null) - { - $data = ['shop_id' => $shop_id, 'old_ssid' => $old_ssid, 'ssid' => $ssid]; - is_null($password) || $data['password'] = $password; - $url = 'https://api.weixin.qq.com/bizwifi/shop/update?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->httpPostForJson($url, $data); - } - - /** - * 清空门店网络与设备 - * @param int $shop_id 门店ID - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function clearShopWifi($shop_id) - { - $url = 'https://api.weixin.qq.com/bizwifi/shop/clean?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->httpPostForJson($url, ['shop_id' => $shop_id]); - } - - /** - * 添加密码型设备 - * @param int $shop_id 门店ID - * @param string $ssid SSID - * @param null|string $password 密码 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function addShopWifi($shop_id, $ssid, $password = null) - { - $data = ['shop_id' => $shop_id, 'ssid' => $ssid, 'password' => $password]; - $url = 'https://api.weixin.qq.com/bizwifi/device/add?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->httpPostForJson($url, $data); - } - - /** - * 添加 portal 型设备 - * @param int $shop_id 门店ID - * @param string $ssid SSID - * @param bool $reset 是否重置 secretkey - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function addShopPortal($shop_id, $ssid, $reset = false) - { - $data = ['shop_id' => $shop_id, 'ssid' => $ssid, 'reset' => $reset]; - $url = 'https://api.weixin.qq.com/bizwifi/apportal/register?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->httpPostForJson($url, $data); - } - - /** - * 查询设备 - * @param int|null $shop_id 门店ID - * @param int|null $pageindex 页码 - * @param int|null $pagesize 数量 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function queryShopWifi($shop_id = null, $pageindex = null, $pagesize = null) - { - $data = []; - is_null($pagesize) || $data['pagesize'] = $pagesize; - is_null($pageindex) || $data['pageindex'] = $pageindex; - is_null($shop_id) || $data['shop_id'] = $shop_id; - $url = 'https://api.weixin.qq.com/bizwifi/device/list?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->httpPostForJson($url, $data); - } - - /** - * 删除设备 - * @param string $bssid 设备 MAC,冒号分隔小写 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function delShopWifi($bssid) - { - $url = 'https://api.weixin.qq.com/bizwifi/device/delete?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->httpPostForJson($url, ['bssid' => $bssid]); - } - - /** - * 获取物料二维码 - * @param int $shop_id 门店ID - * @param string $ssid SSID - * @param int $img_id 物料样式 0|1 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getQrc($shop_id, $ssid, $img_id = 1) - { - $url = 'https://api.weixin.qq.com/bizwifi/qrcode/get?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->httpPostForJson($url, ['shop_id' => $shop_id, 'ssid' => $ssid, 'img_id' => $img_id]); - } - - /** - * 设置商家主页 - * @param int $shop_id 门店ID - * @param int $template_id 0 默认 | 1 自定义链接 - * @param null|string $url 自定义链接(template_id=1 必填) - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function setHomePage($shop_id, $template_id, $url = null) - { - $data = ['shop_id' => $shop_id, 'template_id' => $template_id]; - !is_null($url) && $data['struct'] = ['url' => $url]; - $url = 'https://api.weixin.qq.com/bizwifi/homepage/set?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->httpPostForJson($url, $data); - } - - /** - * 查询商家主页 - * @param int $shop_id 门店ID - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getHomePage($shop_id) - { - $url = 'https://api.weixin.qq.com/bizwifi/homepage/get?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->httpPostForJson($url, ['shop_id' => $shop_id]); - } - - /** - * 设置微信首页欢迎语 - * @param int $shop_id 门店ID - * @param int $bar_type 0|1|2|3 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function setBar($shop_id, $bar_type = 1) - { - $url = 'https://api.weixin.qq.com/bizwifi/bar/set?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->httpPostForJson($url, ['shop_id' => $shop_id, 'bar_type' => $bar_type]); - } - - /** - * 设置连网完成页 - * @param int $shop_id 门店ID - * @param string $finishpage_url 完成页 URL - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function setFinishPage($shop_id, $finishpage_url) - { - $url = 'https://api.weixin.qq.com/bizwifi/finishpage/set?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->httpPostForJson($url, ['shop_id' => $shop_id, 'finishpage_url' => $finishpage_url]); - } - - /** - * Wi-Fi 数据统计 - * @param string $begin_date 开始日期 yyyy-mm-dd - * @param string $end_date 结束日期 yyyy-mm-dd - * @param int $shop_id 门店ID,-1 总统计 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function staticList($begin_date, $end_date, $shop_id = -1) - { - $url = 'https://api.weixin.qq.com/bizwifi/statistics/list?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->httpPostForJson($url, ['shop_id' => $shop_id, 'begin_date' => $begin_date, 'end_date' => $end_date]); - } - - /** - * 设置门店卡券投放 - * @param int $shop_id 门店ID,0 表示全部 - * @param int $card_id 卡券ID - * @param string $card_describe 描述(<=18 字符) - * @param string $start_time 开始时间戳 - * @param string $end_time 结束时间戳 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function setCouponput($shop_id, $card_id, $card_describe, $start_time, $end_time) - { - $data = ['shop_id' => $shop_id, 'card_id' => $card_id, 'card_describe' => $card_describe, 'start_time' => $start_time, 'end_time' => $end_time]; - $url = 'https://api.weixin.qq.com/bizwifi/couponput/set?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->httpPostForJson($url, $data); - } - - /** - * 查询门店卡券投放 - * @param int $shop_id 门店ID,0 表示全部 - * @return array - * @throws Exceptions\InvalidResponseException - * @throws Exceptions\LocalCacheException - */ - public function getCouponput($shop_id) - { - $url = 'https://api.weixin.qq.com/bizwifi/couponput/get?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->httpPostForJson($url, ['shop_id' => $shop_id]); - } - -} \ No newline at end of file diff --git a/WeMini/Crypt.php b/WeMini/Crypt.php deleted file mode 100644 index 139d1fc..0000000 --- a/WeMini/Crypt.php +++ /dev/null @@ -1,122 +0,0 @@ -session($code); - if (empty($result['session_key'])) { - throw new InvalidResponseException('Code 换取 SessionKey 失败', 403); - } - $userinfo = $this->decode($iv, $result['session_key'], $encryptedData); - if (empty($userinfo)) { - throw new InvalidDecryptException('用户信息解析失败', 403); - } - return array_merge($result, $userinfo); - } - - /** - * code 换取 session_key - * @param string $code 登录 code - * @return array - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function session($code) - { - $appid = $this->config->get('appid'); - $secret = $this->config->get('appsecret'); - $url = "https://api.weixin.qq.com/sns/jscode2session?appid={$appid}&secret={$secret}&js_code={$code}&grant_type=authorization_code"; - return json_decode(Tools::get($url), true); - } - - /** - * 解密数据 - * @param string $iv 初始向量 - * @param string $sessionKey 会话密钥 - * @param string $encryptedData 加密数据 - * @return bool|array - */ - public function decode($iv, $sessionKey, $encryptedData) - { - require_once __DIR__ . DIRECTORY_SEPARATOR . 'crypt' . DIRECTORY_SEPARATOR . 'wxBizDataCrypt.php'; - $pc = new WXBizDataCrypt($this->config->get('appid'), $sessionKey); - $data = ''; - $errCode = $pc->decryptData($encryptedData, $iv, $data); - if ($errCode == 0) { - return json_decode($data, true); - } - return false; - } - - /** - * 通过 code 获取手机号 - * @param string $code 授权码 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getPhoneNumber($code) - { - $url = 'https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->httpPostForJson($url, ['code' => $code], true); - } - - /** - * 支付后获取用户 UnionId - * @param string $openid 用户 openid - * @param null|string $transaction_id 微信支付订单号 - * @param null|string $mch_id 商户号 - * @param null|string $out_trade_no 商户订单号 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getPaidUnionId($openid, $transaction_id = null, $mch_id = null, $out_trade_no = null) - { - $url = "https://api.weixin.qq.com/wxa/getpaidunionid?access_token=ACCESS_TOKEN&openid={$openid}"; - if (!is_null($mch_id)) $url .= "&mch_id={$mch_id}"; - if (!is_null($out_trade_no)) $url .= "&out_trade_no={$out_trade_no}"; - if (!is_null($transaction_id)) $url .= "&transaction_id={$transaction_id}"; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->callGetApi($url); - } -} \ No newline at end of file diff --git a/WeMini/Custom.php b/WeMini/Custom.php deleted file mode 100644 index 39cc378..0000000 --- a/WeMini/Custom.php +++ /dev/null @@ -1,221 +0,0 @@ -callPostApi($url, $data, true); - } - - /** - * 创建商户 - * @param array $data - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function businessRegister($data) - { - $url = 'https://api.weixin.qq.com/cgi‐bin/business/register?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 更新商户信息 - * @param array $data - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function businessUpdate($data) - { - $url = 'https://api.weixin.qq.com/cgi‐bin/business/update?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - - /** - * 拉取单个商户信息 - * @param array $data - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function businessGet($data) - { - $url = 'https://api.weixin.qq.com/cgi‐bin/business/get?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - - /** - * 拉取多个商户信息 - * @param array $data - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function businessList($data) - { - $url = 'https://api.weixin.qq.com/cgi‐bin/business/list?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 发送客服消息 - * @param array $data - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function businessSend($data) - { - $url = 'https://api.weixin.qq.com/cgi‐bin/message/custom/business/send?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 客服输入状态 - * @param array $data - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function businessTyping($data) - { - $url = 'https://api.weixin.qq.com/cgi‐bin/message/custom/business/typing?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - - /** - * 获取客服基本信息 - * @param string $business_id 客服子商户的business_id,对于普通小程序客服不需要填business_id - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getKfList($business_id = '') - { - $url = 'https://api.weixin.qq.com/cgi-bin/customservice/getkflist?access_token=ACCESS_TOKEN'; - if (!empty($business_id)) { - $url .= '&business_id=' . $business_id; - } - return $this->callGetApi($url); - } - - - /** - * 获取在线客服列表 - * @param string $business_id 客服子商户的business_id,对于普通小程序客服不需要填business_id - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getOnlineKfList($business_id = '') - { - $url = 'https://api.weixin.qq.com/cgi-bin/customservice/getonlinekflist?access_token=ACCESS_TOKEN'; - if (!empty($business_id)) { - $url .= '&business_id=' . $business_id; - } - return $this->callGetApi($url); - } - - - /** - * 客服输入状态 - * @param array $data - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function addKfAccount($data) - { - $url = 'https://api.weixin.qq.com/customservice/kfaccount/add?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - - /** - * 删除客服账号 - * @param string $kf_openid 客服openid - * @param string $business_id 客服子商户的business_id,对于普通小程序客服不需要填business_id - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function delKfAccount($kf_openid, $business_id = '') - { - $url = 'https://api.weixin.qq.com/customservice/kfaccount/del?access_token=ACCESS_TOKEN&kf_openid=' . $kf_openid; - if (!empty($business_id)) { - $url .= '&business_id=' . $business_id; - } - return $this->callGetApi($url); - } - - /** - * 设置客服管理员 - * @param string $kf_openid 客服openid - * @param string $business_id 客服子商户的business_id,对于普通小程序客服不需要填business_id - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function setKfAdmin($kf_openid, $business_id = '') - { - $url = 'https://api.weixin.qq.com/customservice/kfaccount/setadmin?access_token=ACCESS_TOKEN&kf_openid=' . $kf_openid; - if (!empty($business_id)) { - $url .= '&business_id=' . $business_id; - } - return $this->callGetApi($url); - } - - - /** - * 取消客服管理员 - * @param string $kf_openid 客服openid - * @param string $business_id 客服子商户的business_id,对于普通小程序客服不需要填business_id - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function cancelKfAdmin($kf_openid, $business_id = '') - { - $url = 'https://api.weixin.qq.com/customservice/kfaccount/canceladmin?access_token=ACCESS_TOKEN&kf_openid=' . $kf_openid; - if (!empty($business_id)) { - $url .= '&business_id=' . $business_id; - } - return $this->callGetApi($url); - } -} \ No newline at end of file diff --git a/WeMini/Delivery.php b/WeMini/Delivery.php deleted file mode 100644 index ad97902..0000000 --- a/WeMini/Delivery.php +++ /dev/null @@ -1,171 +0,0 @@ -callPostApi($url, $data, true); - } - - /** - * 下配送单 - * @param array $data 订单信息 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function addOrder($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/local/business/order/add?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 待接单订单加小费 - * @param array $data 订单与小费信息 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function addTip($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/local/business/order/addtips?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 取消配送单 - * @param array $data 订单信息 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function cancelOrder($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/local/business/order/cancel?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 获取配送公司列表 - * @param array $data 请求参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getAllImmeDelivery($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/local/business/delivery/getall?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 拉取已绑定账号 - * @param array $data 请求参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getBindAccount($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/local/business/shop/get?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 查询配送单 - * @param array $data 订单信息 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getOrder($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/local/business/order/get?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 模拟更新配送单状态 - * @param array $data 模拟参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function mockUpdateOrder($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/local/business/test_update_order?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 预下单 - * @param array $data 订单信息 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function preAddOrder($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/local/business/order/pre_add?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 预取消配送单 - * @param array $data 订单信息 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function preCancelOrder($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/local/business/order/precancel?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 重新下单 - * @param array $data 订单信息 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function reOrder($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/local/business/order/readd?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - -} \ No newline at end of file diff --git a/WeMini/Guide.php b/WeMini/Guide.php deleted file mode 100644 index c0a2241..0000000 --- a/WeMini/Guide.php +++ /dev/null @@ -1,527 +0,0 @@ -callPostApi($url, $data, true); - } - - /** - * 删除导购 - * @param array $data 导购账号 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function delGuideAcct($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/delguideacct?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 获取导购信息 - * @param array $data 导购账号 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getGuideAcct($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/getguideacct?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 获取敏感词与自动回复配置 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getGuideAcctConfig() - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/getguideacctconfig?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, [], true); - } - - /** - * 拉取导购列表 - * @param int $page 页码 - * @param int $num 数量 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getGuideAcctList($page = 0, $num = 10) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/getguideacctconfig?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['page' => $page, 'num' => $num], true); - } - - /** - * 获取导购聊天记录 - * @param array $data 查询条件 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getGuideBuyerChatRecord($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/getguideacct?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 获取导购快捷回复 - * @param array $data 导购信息 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getGuideConfig($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/getguideconfig?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 生成导购二维码 - * @param array $data 导购信息 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function guideCreateQrCode($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/guidecreateqrcode?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 推送小程序路径菜单 - * @param array $data 请求参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function pushShowWxaPathMenu($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/pushshowwxapathmenu?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 设置敏感词与自动回复 - * @param array $data 配置数据 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function setGuideAcctConfig($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/setguideacctconfig?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 设置导购快捷回复 - * @param array $data 快捷回复数据 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function setGuideConfig($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/setguideconfig?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 更新导购昵称或头像 - * @param array $data 导购信息 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function updateGuideAcct($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/setguideconfig?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 添加展示标签 - * @param array $data 标签数据 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function addGuideBuyerDisplayTag($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/addguidebuyerdisplaytag?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 为粉丝添加可查询标签 - * @param array $data 标签数据 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function addGuideBuyerTag($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/addguidebuyertag?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 添加标签可选值 - * @param array $data 选项数据 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function addGuideTagOption($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/addguidetagoption?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 删除粉丝标签 - * @param array $data 标签数据 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function delGuideBuyerTag($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/delguidebuyertag?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 查询展示标签 - * @param array $data 查询参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getGuideBuyerDisplayTag($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/getguidebuyerdisplaytag?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 查询粉丝标签 - * @param array $data 查询参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getGuideBuyerTag($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/getguidebuyertag?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 查询标签可选值 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getGuideTagOption() - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/getguidetagoption?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, [], true); - } - - /** - * 新建可查询标签类型 - * @param array $data 标签数据 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function newGuideTagOption($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/newguidetagoption?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 标签筛选粉丝 - * @param array $data 标签查询条件 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function queryGuideBuyerByTag($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/queryguidebuyerbytag?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->callPostApi($url, $data, true); - } - - /** - * 导购添加粉丝 - * @param array $data 绑定数据 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function addGuideBuyerRelation($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/addguidebuyerrelation?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->callPostApi($url, $data, true); - } - - /** - * 删除导购粉丝 - * @param array $data 解绑数据 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function delGuideBuyerRelation($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/delguidebuyerrelation?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->callPostApi($url, $data, true); - } - - /** - * 查询粉丝绑定关系 - * @param array $data 查询参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getGuideBuyerRelation($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/getguidebuyerrelation?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->callPostApi($url, $data, true); - } - - /** - * 通过粉丝查询绑定关系 - * @param string $openid 粉丝 openid - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getGuideBuyerRelationByBuyer($openid) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/getguidebuyerrelation?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->callPostApi($url, ['openid' => $openid], true); - } - - /** - * 拉取导购粉丝列表 - * @param array $data 查询参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getGuideBuyerRelationList($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/getguidebuyerrelationlist?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->callPostApi($url, $data, true); - } - - /** - * 粉丝迁移导购 - * @param array $data 迁移数据 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function rebindGuideAcctForBuyer($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/rebindguideacctforbuyer?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->callPostApi($url, $data, true); - } - - /** - * 更新粉丝昵称 - * @param array $data 昵称数据 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function updateGuideBuyerRelation($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/updateguidebuyerrelation?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->callPostApi($url, $data, true); - } - - /** - * 删除卡片素材 - * @param array $data 素材信息 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function delGuideCardMaterial($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/delguidecardmaterial?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->callPostApi($url, $data, true); - } - - /** - * 删除图片素材 - * @param array $data 素材信息 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function delGuideImageMaterial($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/delguideimagematerial?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->callPostApi($url, $data, true); - } - - /** - * 删除文字素材 - * @param array $data 素材信息 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function delGuideWordMaterial($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/delguidewordmaterial?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->callPostApi($url, $data, true); - } - - /** - * 获取卡片素材 - * @param int $type 类型 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getGuideCardMaterial($type = 0) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/getguidecardmaterial?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->callPostApi($url, ['type' => $type], true); - } - - /** - * 获取图片素材 - * @param int $type 类型 - * @param int $start 起始 - * @param int $num 数量 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getGuideImageMaterial($type = 0, $start = 0, $num = 10) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/getguideimagematerial?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->callPostApi($url, ['type' => $type, 'start' => $start, 'num' => $num], true); - } - - /** - * 获取文字素材 - * @param int $type 类型 - * @param int $start 起始 - * @param int $num 数量 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getGuideWordMaterial($type = 0, $start = 0, $num = 10) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/getguidewordmaterial?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->callPostApi($url, ['type' => $type, 'start' => $start, 'num' => $num], true); - } - - /** - * 添加卡片素材 - * @param array $data 素材数据 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function setGuideCardMaterial($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/setguidecardmaterial?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->callPostApi($url, $data, true); - } - - /** - * 添加图片素材 - * @param array $data 素材数据 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function setGuideImageMaterial($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/setguideimagematerial?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->callPostApi($url, $data, true); - } - - /** - * 添加文字素材 - * @param array $data 素材数据 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function setGuideWordMaterial($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/guide/setguidewordmaterial?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->callPostApi($url, $data, true); - } -} \ No newline at end of file diff --git a/WeMini/Image.php b/WeMini/Image.php deleted file mode 100644 index 6101baf..0000000 --- a/WeMini/Image.php +++ /dev/null @@ -1,69 +0,0 @@ -callPostApi($url, ['img_url' => $img_url, 'img' => $img], true); - } - - /** - * 条码/二维码识别 - * @param string $img_url 图片 URL(与 img 二选一) - * @param string $img form-data 媒体文件(与 img_url 二选一) - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function scanQRCode($img_url, $img) - { - $url = "https://api.weixin.qq.com/cv/img/qrcode?img_url=ENCODE_URL&access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['img_url' => $img_url, 'img' => $img], true); - } - - /** - * 图片高清化 - * @param string $img_url 图片 URL(与 img 二选一) - * @param string $img form-data 媒体文件(与 img_url 二选一) - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function superresolution($img_url, $img) - { - $url = "https://api.weixin.qq.com/cv/img/qrcode?img_url=ENCODE_URL&access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['img_url' => $img_url, 'img' => $img], true); - } -} \ No newline at end of file diff --git a/WeMini/Insurance.php b/WeMini/Insurance.php deleted file mode 100644 index 18173f6..0000000 --- a/WeMini/Insurance.php +++ /dev/null @@ -1,208 +0,0 @@ -callPostApi($url, [], true); - } - - /** - * 查询开通状态 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function queryOpen() - { - $url = 'https://api.weixin.qq.com/wxa/business/insurance_freight/query_open?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, [], true); - } - - /** - * 发货投保 - * @param array $data 投保数据 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function createOrder($data) - { - $url = 'https://api.weixin.qq.com/wxa/business/insurance_freight/createorder?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 退货理赔 - * @param array $data 理赔数据 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function claim($data) - { - $url = 'https://api.weixin.qq.com/wxa/business/insurance_freight/claim?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 申请充值订单号 - * @param array $data 充值请求 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function createChargeId($data) - { - $url = 'https://api.weixin.qq.com/wxa/business/insurance_freight/createchargeid?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 申请支付 - * @param array $data 支付数据 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function applyPay($data) - { - $url = 'https://api.weixin.qq.com/wxa/business/insurance_freight/applypay?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 拉取充值订单 - * @param array $data 查询参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getPayOrderList($data) - { - $url = 'https://api.weixin.qq.com/wxa/business/insurance_freight/getpayorderlist?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - - /** - * 保险退款 - * @param array $data 退款参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function refund($data) - { - $url = 'https://api.weixin.qq.com/wxa/business/insurance_freight/refund?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 获取保费摘要 - * @param array $data 查询参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getSummary($data) - { - $url = 'https://api.weixin.qq.com/wxa/business/insurance_freight/getsummary?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 拉取保单信息 - * @param array $data 查询参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getOrderList($data) - { - $url = 'https://api.weixin.qq.com/wxa/business/insurance_freight/getorderlist?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 设置告警余额 - * @param array $data 配置参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function updateNotifyFunds($data) - { - $url = 'https://api.weixin.qq.com/wxa/business/insurance_freight/update_notify_funds?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 创建退货 ID - * @param array $data 退货参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function returnAdd($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/delivery/no_worry_return/add?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 查询退货 ID 状态 - * @param array $data 查询参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function returnGet($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/delivery/no_worry_return/get?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 解绑退货 ID - * @param array $data 解绑参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function returnUbind($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/delivery/no_worry_return/unbind?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } -} \ No newline at end of file diff --git a/WeMini/Live.php b/WeMini/Live.php deleted file mode 100644 index bf7095c..0000000 --- a/WeMini/Live.php +++ /dev/null @@ -1,517 +0,0 @@ -callPostApi($url, $data, true); - } - - /** - * 获取直播房间列表 - * @param int $start 起始 - * @param int $limit 数量 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getLiveList($start = 0, $limit = 10) - { - $url = 'https://api.weixin.qq.com/wxa/business/getliveinfo?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['start' => $start, 'limit' => $limit], true); - } - - /** - * 获取直播/回放信息 - * @param array $data 查询参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getLiveInfo($data = []) - { - $url = 'https://api.weixin.qq.com/wxa/business/getliveinfo?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 直播间导入商品 - * @param array $data 商品列表 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function addLiveGoods($data = []) - { - $url = 'https://api.weixin.qq.com/wxaapi/broadcast/room/addgoods?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 商品添加并提审 - * @param array $data 商品信息 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function addGoods($data) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/goods/add?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - /** - * 撤回商品审核 - * @param array $data 商品信息 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function resetAuditGoods($data) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/goods/resetaudit?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - /** - * 重新提交商品审核 - * @param array $data 商品信息 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function auditGoods($data) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/goods/audit?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - /** - * 删除商品 - * @param array $data 商品信息 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function deleteGoods($data) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/goods/delete?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - /** - * 更新商品 - * @param array $data 商品信息 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function updateGoods($data) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/goods/update?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - /** - * 获取商品状态 - * @param array $data 商品ID列表 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function stateGoods($data) - { - $url = "https://api.weixin.qq.com/wxa/business/getgoodswarehouse?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - /** - * 获取商品列表 - * @param int $offset 起始 - * @param int $status 0 未审 |1 审核中 |2 通过 |3 驳回 - * @param int $limit 数量(<=100) - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getGoods($offset, $status, $limit = 30) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/goods/getapproved?access_token=ACCESS_TOKEN&offset={$offset}&limit={$limit}&status={$status}"; - return $this->callGetApi($url); - } - - /** - * 删除直播间 - * @param array $data 房间信息 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function delLive($data) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/deleteroom?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - /** - * 编辑直播间 - * @param array $data 房间信息 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function editLive($data) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/editroom?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - /** - * 获取推流地址 - * @param string $roomId 房间ID - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getPushUrl($roomId) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/getpushurl?access_token=ACCESS_TOKEN&roomId={$roomId}"; - return $this->callGetApi($url); - } - - /** - * 获取直播间分享码 - * @param string $roomId 房间ID - * @param string $params 自定义参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getShareCode($roomId, $params = '') - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/getsharedcode?access_token=ACCESS_TOKEN&roomId={$roomId}¶ms={$params}"; - return $this->callGetApi($url); - } - - /** - * 添加小助手 - * @param array $data 助手信息 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function addAssistant($data) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/addassistant?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - /** - * 修改小助手 - * @param array $data 助手信息 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function modifyAssistant($data) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/modifyassistant?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - /** - * 删除小助手 - * @param array $data 助手信息 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function removeAssistant($data) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/removeassistant?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - /** - * 查询小助手列表 - * @param string $roomId 房间ID - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getAssistantList($roomId) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/getassistantlist?access_token=ACCESS_TOKEN&roomId={$roomId}"; - return $this->callGetApi($url); - } - - /** - * 添加主播副号 - * @param array $data 副号信息 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function addSubAnchor($data) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/addsubanchor?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - /** - * 修改主播副号 - * @param array $data 副号信息 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function modifySubAnchor($data) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/modifysubanchor?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - /** - * 删除主播副号 - * @param array $data 副号信息 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function delSubAnchor($data) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/deletesubanchor?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - /** - * 查询主播副号 - * @param string $roomId 房间ID - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getSubAnchor($roomId) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/getsubanchor?access_token=ACCESS_TOKEN&roomId={$roomId}"; - return $this->callGetApi($url); - } - - /** - * 开关官方收录 - * @param array $data 房间配置 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function updateFeedPublic($data) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/updatefeedpublic?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - /** - * 开关回放 - * @param array $data 房间配置 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function updateReplay($data) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/updatereplay?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - /** - * 开关客服 - * @param array $data 房间配置 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function updateKf($data) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/updatekf?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - /** - * 开关全局禁言 - * @param array $data 房间配置 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function updateComment($data) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/updatecomment?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - /** - * 上下架商品 - * @param array $data 商品参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function goodsOnsale($data) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/goods/onsale?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - /** - * 删除直播间商品 - * @param array $data 商品参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function goodsDeleteInRoom($data) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/goods/deleteInRoom?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - /** - * 推送商品 - * @param array $data 商品参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function goodsPush($data) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/goods/push?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - /** - * 商品排序 - * @param array $data 排序参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function goodsSort($data) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/goods/sort?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - /** - * 下载商品讲解视频 - * @param array $data 商品信息 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getVideo($data) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/goods/getVideo?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - /** - * 获取长期订阅用户 - * @param array $data 查询参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getFollowers($data) - { - $url = "https://api.weixin.qq.com/wxa/business/get_wxa_followers?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - /** - * 长订阅群发 - * @param array $data 消息参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function pushMessage($data) - { - $url = "https://api.weixin.qq.com/wxa/business/push_message?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - - /** - * 设置成员角色 - * @param array $data 角色参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function addRole($data) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/role/addrole?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - - /** - * 解除成员角色 - * @param array $data 角色参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function delRole($data) - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/role/deleterole?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - - /** - * 查询成员角色 - * @param int $role 角色过滤,-1 全部 - * @param int $offset 起始 - * @param int $limit 数量 - * @param string $keyword 关键词 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getRole($role = -1, $offset = 0, $limit = 30, $keyword = '') - { - $url = "https://api.weixin.qq.com/wxaapi/broadcast/role/getrolelist?access_token=ACCESS_TOKEN&offset={$offset}&limit={$limit}&keyword={$keyword}&role={$role}"; - return $this->callGetApi($url); - } -} \ No newline at end of file diff --git a/WeMini/Logistics.php b/WeMini/Logistics.php deleted file mode 100644 index 596bd33..0000000 --- a/WeMini/Logistics.php +++ /dev/null @@ -1,236 +0,0 @@ -callPostApi($url, $data, true); - } - - /** - * 取消运单 - * @param array $data 取消参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function cancelOrder($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/business/order/cancel?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 获取快递公司列表 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getAllDelivery() - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/business/delivery/getall?access_token=ACCESS_TOKEN'; - return $this->callGetApi($url); - } - - /** - * 获取运单数据 - * @param array $data 查询参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getOrder($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/business/order/get?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 查询运单轨迹 - * @param array $data 查询参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getPath($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/business/path/get?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 获取打印员列表(需使用微信打单时调用) - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getPrinter() - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/business/printer/getall?access_token=ACCESS_TOKEN'; - return $this->callGetApi($url); - } - - /** - * 获取电子面单余额(加盟类快递) - * @param array $data 查询参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getQuota($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/business/quota/get?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 模拟更新订单状态(测试用) - * @param array $data 模拟参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function testUpdateOrder($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/business/test_update_order?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 配置面单打印员(微信打单) - * @param array $data 配置参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function updatePrinter($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/business/printer/update?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 获取面单联系人信息 - * @param array $data 查询参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getContact($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/delivery/contact/get?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 预览面单模板(调试用) - * @param array $data 模板参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function previewTemplate($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/delivery/template/preview?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 更新商户审核结果 - * @param array $data 审核结果 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function updateBusiness($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/delivery/service/business/update?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 更新运单轨迹 - * @param array $data 轨迹参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function updatePath($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/delivery/path/update?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - - /** - * 绑定/解绑物流账号 - * @param array $data 账号参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function bindAccount($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/business/account/bind?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - - /** - * 获取已绑定的物流账号列表 - * @param array $data 查询参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getAllAccount($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/business/account/getall?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 批量获取运单数据 - * @param array $data 查询参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function batchGetOrder($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/express/business/order/batchget?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - -} \ No newline at end of file diff --git a/WeMini/Market.php b/WeMini/Market.php deleted file mode 100644 index be41f43..0000000 --- a/WeMini/Market.php +++ /dev/null @@ -1,54 +0,0 @@ -callPostApi($url, $data, true); - } - - /** - * 拉取服务结果 - * @param array $data 查询参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function retrieve($data) - { - $url = 'https://api.weixin.qq.com/wxa/servicemarketretrieve?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - -} \ No newline at end of file diff --git a/WeMini/Media.php b/WeMini/Media.php deleted file mode 100644 index ecde99d..0000000 --- a/WeMini/Media.php +++ /dev/null @@ -1,54 +0,0 @@ -callGetApi($url); - } - - /** - * 新增图片素材 - * @param string $filename 本地文件 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function upload($filename) - { - $url = 'https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=image'; - return $this->callPostApi($url, ['media' => Tools::createCurlFile($filename)], false); - } -} \ No newline at end of file diff --git a/WeMini/Message.php b/WeMini/Message.php deleted file mode 100644 index 5175cf8..0000000 --- a/WeMini/Message.php +++ /dev/null @@ -1,65 +0,0 @@ -callPostApi($url, $data, true); - } - - /** - * 修改动态消息 - * @param array $data 消息数据 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function setUpdatableMsg($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/message/wxopen/updatablemsg/send?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 统一服务消息下发 - * @param array $data 消息内容 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function uniformSend($data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } -} \ No newline at end of file diff --git a/WeMini/Newtmpl.php b/WeMini/Newtmpl.php deleted file mode 100644 index fccb00f..0000000 --- a/WeMini/Newtmpl.php +++ /dev/null @@ -1,144 +0,0 @@ -callPostApi($url, $data, true); - } - - /** - * 获取账号类目 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getCategory() - { - $url = 'https://api.weixin.qq.com/wxaapi/newtmpl/getcategory?access_token=ACCESS_TOKEN'; - return $this->callGetApi($url); - } - - /** - * 删除账号类目 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function deleteCategory() - { - $url = 'https://api.weixin.qq.com/cgi-bin/wxopen/deletecategory?access_token=TOKEN'; - return $this->callPostApi($url, [], true); - } - - /** - * 获取类目下公共模板标题 - * @param string $ids 类目ID,逗号分隔 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getPubTemplateTitleList($ids) - { - $url = 'https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatetitles?access_token=ACCESS_TOKEN'; - $url .= '&' . http_build_query(['ids' => $ids, 'start' => '0', 'limit' => '30']); - return $this->callGetApi($url); - } - - /** - * 获取模板标题关键词 - * @param string $tid 模板标题ID - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getPubTemplateKeyWordsById($tid) - { - $url = 'https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatekeywords?access_token=ACCESS_TOKEN'; - $url .= '&' . http_build_query(['tid' => $tid]); - return $this->callGetApi($url); - } - - /** - * 组合模板并入库 - * @param string $tid 模板标题ID - * @param array $kidList 关键词ID列表 - * @param string $sceneDesc 场景描述 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function addTemplate($tid, array $kidList, $sceneDesc = '') - { - $url = 'https://api.weixin.qq.com/wxaapi/newtmpl/addtemplate?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['tid' => $tid, 'kidList' => $kidList, 'sceneDesc' => $sceneDesc], false); - } - - /** - * 获取个人模板列表 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getTemplateList() - { - $url = 'https://api.weixin.qq.com/wxaapi/newtmpl/gettemplate?access_token=ACCESS_TOKEN'; - return $this->callGetApi($url); - } - - /** - * 删除个人模板 - * @param string $priTmplId 模板ID - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function delTemplate($priTmplId) - { - $url = 'https://api.weixin.qq.com/wxaapi/newtmpl/deltemplate?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['priTmplId' => $priTmplId], true); - } - - /** - * 发送订阅消息 - * @param array $data 消息数据 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function send(array $data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } -} \ No newline at end of file diff --git a/WeMini/Ocr.php b/WeMini/Ocr.php deleted file mode 100644 index 7dc4f60..0000000 --- a/WeMini/Ocr.php +++ /dev/null @@ -1,104 +0,0 @@ -callPostApi($url, $data, false); - } - - /** - * 营业执照 OCR - * @param array $data 图片参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function businessLicense($data) - { - $url = 'https://api.weixin.qq.com/cv/ocr/bizlicense?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, false); - } - - /** - * 驾驶证 OCR - * @param array $data 图片参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function driverLicense($data) - { - $url = 'https://api.weixin.qq.com/cv/ocr/drivinglicense?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, false); - } - - /** - * 身份证 OCR - * @param array $data 图片参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function idcard($data) - { - $url = 'https://api.weixin.qq.com/cv/ocr/idcard?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, false); - } - - /** - * 通用印刷体 OCR - * @param array $data 图片参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function printedText($data) - { - $url = 'https://api.weixin.qq.com/cv/ocr/comm?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, false); - } - - /** - * 行驶证 OCR - * @param array $data 图片参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function vehicleLicense($data) - { - $url = 'https://api.weixin.qq.com/cv/ocr/driving?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, false); - } -} \ No newline at end of file diff --git a/WeMini/Operation.php b/WeMini/Operation.php deleted file mode 100644 index 09773e0..0000000 --- a/WeMini/Operation.php +++ /dev/null @@ -1,69 +0,0 @@ -callPostApi($url, $data, true); - } - - /** - * 获取反馈媒体文件 - * @param array $data query 参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getFeedbackmedia($data) - { - $query = http_build_query($data); - $url = 'https://api.weixin.qq.com/cgi-bin/media/getfeedbackmedia?' . $query . '&access_token=ACCESS_TOKEN'; - return $this->callGetApi($url); - } - - - /** - * 获取用户反馈列表 - * @param array $data query 参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getFeedback($data) - { - $query = http_build_query($data); - $url = 'https://api.weixin.qq.com/wxaapi/userlog/userlog_search?' . $query . '&access_token=ACCESS_TOKEN'; - return $this->callGetApi($url); - } -} \ No newline at end of file diff --git a/WeMini/Plugs.php b/WeMini/Plugs.php deleted file mode 100644 index ce244bf..0000000 --- a/WeMini/Plugs.php +++ /dev/null @@ -1,105 +0,0 @@ -callPostApi($url, ['action' => 'apply', 'plugin_appid' => $plugin_appid], true); - } - - /** - * 查询已添加插件 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getList() - { - $url = 'https://api.weixin.qq.com/wxa/plugin?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['action' => 'list'], true); - } - - /** - * 删除插件 - * @param string $plugin_appid 插件 appid - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function unbind($plugin_appid) - { - $url = 'https://api.weixin.qq.com/wxa/plugin?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['action' => 'unbind', 'plugin_appid' => $plugin_appid], true); - } - - /** - * 获取插件使用方或修改申请状态 - * @param array $data 请求参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function devplugin($data) - { - $url = 'https://api.weixin.qq.com/wxa/devplugin?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 获取插件使用方列表(开发者) - * @param int $page 页码 - * @param int $num 每页数量 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function devApplyList($page = 1, $num = 10) - { - $url = 'https://api.weixin.qq.com/wxa/plugin?access_token=ACCESS_TOKEN'; - $data = ['action' => 'dev_apply_list', 'page' => $page, 'num' => $num]; - return $this->callPostApi($url, $data, true); - } - - /** - * 修改插件申请状态(开发者) - * @param string $action dev_agree|dev_refuse|dev_delete - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function devAgree($action = 'dev_agree') - { - $url = 'https://api.weixin.qq.com/wxa/plugin?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['action' => $action], true); - } -} \ No newline at end of file diff --git a/WeMini/Poi.php b/WeMini/Poi.php deleted file mode 100644 index bd31b37..0000000 --- a/WeMini/Poi.php +++ /dev/null @@ -1,87 +0,0 @@ - $related_name, 'related_credential' => $related_credential, - 'related_address' => $related_address, 'related_proof_material' => $related_proof_material, - ]; - return $this->callPostApi($url, $data, true); - } - - /** - * 查看地点列表 - * @param int $page 页码从1开始 - * @param int $page_rows 每页数量(<=1000) - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getNearByPoiList($page = 1, $page_rows = 1000) - { - $url = "https://api.weixin.qq.com/wxa/getnearbypoilist?page={$page}&page_rows={$page_rows}&access_token=ACCESS_TOKEN"; - return $this->callGetApi($url); - } - - /** - * 删除附近地点 - * @param string $poi_id 地点ID - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function delNearByPoiList($poi_id) - { - $url = "https://api.weixin.qq.com/wxa/delnearbypoi?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['poi_id' => $poi_id], true); - } - - /** - * 设置附近小程序展示 - * @param string $poi_id 地点ID - * @param string $status 0 取消 | 1 展示 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function setNearByPoiShowStatus($poi_id, $status) - { - $url = "https://api.weixin.qq.com/wxa/setnearbypoishowstatus?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, ['poi_id' => $poi_id, 'status' => $status], true); - } -} \ No newline at end of file diff --git a/WeMini/Qrcode.php b/WeMini/Qrcode.php deleted file mode 100644 index 58ca8f3..0000000 --- a/WeMini/Qrcode.php +++ /dev/null @@ -1,119 +0,0 @@ - "0", "g" => "0", "b" => "0"]; - - /** - * 获取小程序码(永久有效) - * 接口A: 适用于需要的码数量较少的业务场景 - * @param string $path 不能为空,最大长度 128 字节 - * @param integer $width 二维码的宽度 - * @param bool $autoColor 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 - * @param null|array $lineColor auto_color 为 false 时生效 - * @param boolean $isHyaline 透明底色 - * @param string|null $outType 输出类型 - * @param array $extra 其他参数 - * @return string|array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function createMiniPath($path, $width = 430, $autoColor = false, $lineColor = null, $isHyaline = true, $outType = null, array $extra = []) - { - $url = 'https://api.weixin.qq.com/wxa/getwxacode?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - $lineColor = empty($lineColor) ? $this->lineColor : $lineColor; - $data = ['path' => $path, 'width' => $width, 'auto_color' => $autoColor, 'line_color' => $lineColor, 'is_hyaline' => $isHyaline]; - return $this->parseResult(Tools::post($url, Tools::arr2json(array_merge($data, $extra))), $outType); - } - - /** - * 解释接口数据 - * @param bool|string $result - * @param null|string $outType - * @return array|mixed - * @throws \WeChat\Exceptions\InvalidResponseException - */ - private function parseResult($result, $outType) - { - if (is_array($json = json_decode($result, true))) { - if (!$this->isTry && isset($json['errcode']) && in_array($json['errcode'], ['40014', '40001', '41001', '42001'])) { - [$this->delAccessToken(), $this->isTry = true]; - return call_user_func_array([$this, $this->currentMethod['method']], $this->currentMethod['arguments']); - } - return Tools::json2arr($result); - } else { - return is_null($outType) ? $result : $outType($result); - } - } - - /** - * 获取小程序码(永久有效) - * 接口B:适用于需要的码数量极多的业务场景 - * @param string $scene 最大32个可见字符,只支持数字 - * @param string $page 必须是已经发布的小程序存在的页面 - * @param integer $width 二维码的宽度 - * @param bool $autoColor 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 - * @param null|array $lineColor auto_color 为 false 时生效 - * @param bool $isHyaline 是否需要透明底色 - * @param null|string $outType 输出类型 - * @param array $extra 其他参数 - * @return array|string - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function createMiniScene($scene, $page = '', $width = 430, $autoColor = false, $lineColor = null, $isHyaline = true, $outType = null, array $extra = []) - { - $url = 'https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - $lineColor = empty($lineColor) ? $this->lineColor : $lineColor; - $data = ['scene' => $scene, 'width' => $width, 'page' => $page, 'auto_color' => $autoColor, 'line_color' => $lineColor, 'is_hyaline' => $isHyaline, 'check_path' => false]; - if (empty($page)) unset($data['page']); - return $this->parseResult(Tools::post($url, Tools::arr2json(array_merge($data, $extra))), $outType); - } - - /** - * 获取小程序二维码(永久有效) - * 接口C:适用于需要的码数量较少的业务场景 - * @param string $path 不能为空,最大长度 128 字节 - * @param integer $width 二维码的宽度 - * @param string|null $outType 输出类型 - * @return array|string - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function createDefault($path, $width = 430, $outType = null) - { - $url = 'https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode?access_token=ACCESS_TOKEN'; - $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->parseResult(Tools::post($url, Tools::arr2json(['path' => $path, 'width' => $width])), $outType); - } -} \ No newline at end of file diff --git a/WeMini/Scheme.php b/WeMini/Scheme.php deleted file mode 100644 index 9ab78d8..0000000 --- a/WeMini/Scheme.php +++ /dev/null @@ -1,79 +0,0 @@ -callPostApi($url, $data, true); - } - - /** - * 查询 URL-Scheme - * @param string $scheme Scheme 字符串 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function query($scheme) - { - $url = 'https://api.weixin.qq.com/wxa/queryscheme?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['scheme' => $scheme], true); - } - - /** - * 创建 URL-Link - * @param array $data 场景参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function urlLink($data) - { - $url = "https://api.weixin.qq.com/wxa/generate_urllink?access_token=ACCESS_TOKEN"; - return $this->callPostApi($url, $data, true); - } - - /** - * 查询 URL-Link - * @param string $urllink URL-Link 字符串 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function urlQuery($urllink) - { - $url = 'https://api.weixin.qq.com/wxa/query_urllink?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['url_link' => $urllink], true); - } -} \ No newline at end of file diff --git a/WeMini/Search.php b/WeMini/Search.php deleted file mode 100644 index d46abea..0000000 --- a/WeMini/Search.php +++ /dev/null @@ -1,39 +0,0 @@ -callPostApi($url, ['pages' => $pages], true); - } -} \ No newline at end of file diff --git a/WeMini/Security.php b/WeMini/Security.php deleted file mode 100644 index 1a01fb0..0000000 --- a/WeMini/Security.php +++ /dev/null @@ -1,67 +0,0 @@ -callPostApi($url, ['media' => $media], false, ['headers' => ['Content-Type: application/octet-stream']]); - } - - /** - * 异步校验媒体(图片/音频) - * @param string $media_url 媒体 URL - * @param string $media_type 1 音频 | 2 图片 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function mediaCheckAsync($media_url, $media_type) - { - $url = 'https://api.weixin.qq.com/wxa/media_check_async?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['media_url' => $media_url, 'media_type' => $media_type], true); - } - - /** - * 文本内容安全校验 - * @param string $content 文本内容 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function msgSecCheck($content) - { - $url = 'https://api.weixin.qq.com/wxa/msg_sec_check?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['content' => $content], true); - } -} \ No newline at end of file diff --git a/WeMini/Shipping.php b/WeMini/Shipping.php deleted file mode 100644 index 11522b1..0000000 --- a/WeMini/Shipping.php +++ /dev/null @@ -1,117 +0,0 @@ -callPostApi($url, $data, true); - } - - /** - * 合单发货录入 - * @param array $data 发货数据 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function combined($data) - { - $url = 'https://api.weixin.qq.com/wxa/sec/order/upload_combined_shipping_info?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 查询发货状态 - * @param array $data 查询参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function query($data) - { - $url = 'https://api.weixin.qq.com/wxa/sec/order/get_order?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 查询发货订单列表 - * @param array $data 查询参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function qlist($data) - { - $url = 'https://api.weixin.qq.com/wxa/sec/order/get_order_list?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 提醒确认收货 - * @param array $data 提醒参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function confirm($data) - { - $url = 'https://api.weixin.qq.com/wxa/sec/order/notify_confirm_receive?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 设置消息跳转路径 - * @param array $data 路径参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function setJump($data) - { - $url = 'https://api.weixin.qq.com/wxa/sec/order/set_msg_jump_path?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 查询是否开通发货管理 - * @param array $data 查询参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function isTrade($data) - { - $url = 'https://api.weixin.qq.com/wxa/sec/order/is_trade_managed?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 查询交易结算确认状态 - * @param array $data 查询参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function isCompleted($data) - { - $url = 'https://api.weixin.qq.com/wxa/sec/order/is_trade_management_confirmation_completed?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } -} \ No newline at end of file diff --git a/WeMini/Shopping.php b/WeMini/Shopping.php deleted file mode 100644 index 70db178..0000000 --- a/WeMini/Shopping.php +++ /dev/null @@ -1,94 +0,0 @@ -callPostApi($url, $data, true); - } - - /** - * 上传物流信息 - * @param array $data 物流数据 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function uploadShippingInfo($data) - { - $url = 'https://api.weixin.qq.com/user-order/orders?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 上传合单购物详情 - * @param array $data 订单数据 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function uploadCombinedShoppingInfo($data) - { - $url = 'https://api.weixin.qq.com/user-order/orders?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 上传合单物流 - * @param array $data 物流数据 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function uploadCombinedShippingInfo($data) - { - $url = 'https://api.weixin.qq.com/user-order/orders?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - /** - * 校验购物订单上传结果 - * @param array $data 校验参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function ShoppingInfoVerifyUploadResult($data) - { - $url = 'https://api.weixin.qq.com/user-order/shoppinginfo/verify?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } - - -} \ No newline at end of file diff --git a/WeMini/Soter.php b/WeMini/Soter.php deleted file mode 100644 index 4263436..0000000 --- a/WeMini/Soter.php +++ /dev/null @@ -1,39 +0,0 @@ -callPostApi($url, $data, true); - } -} \ No newline at end of file diff --git a/WeMini/Template.php b/WeMini/Template.php deleted file mode 100644 index c41c45e..0000000 --- a/WeMini/Template.php +++ /dev/null @@ -1,104 +0,0 @@ -callPostApi($url, ['offset' => '0', 'count' => '20'], true); - } - - /** - * 获取模板关键词库 - * @param string $template_id 模板标题ID - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getTemplateLibrary($template_id) - { - $url = 'https://api.weixin.qq.com/cgi-bin/wxopen/template/library/get?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['id' => $template_id], true); - } - - /** - * 组合关键词并添加模板 - * @param string $template_id 模板标题ID - * @param array $keyword_id_list 关键词 ID 列表 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function addTemplate($template_id, array $keyword_id_list) - { - $url = 'https://api.weixin.qq.com/cgi-bin/wxopen/template/add?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['id' => $template_id, 'keyword_id_list' => $keyword_id_list], true); - } - - /** - * 获取帐号模板列表 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getTemplateList() - { - $url = 'https://api.weixin.qq.com/cgi-bin/wxopen/template/list?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['offset' => '0', 'count' => '20'], true); - } - - /** - * 删除模板 - * @param string $template_id 模板 ID - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function delTemplate($template_id) - { - $url = 'https://api.weixin.qq.com/cgi-bin/wxopen/template/del?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['template_id' => $template_id], true); - } - - /** - * 发送模板消息 - * @param array $data 消息体(touser, template_id, form_id, data 等) - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function send(array $data) - { - $url = 'https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, $data, true); - } -} \ No newline at end of file diff --git a/WeMini/Total.php b/WeMini/Total.php deleted file mode 100644 index cc715ac..0000000 --- a/WeMini/Total.php +++ /dev/null @@ -1,166 +0,0 @@ -callPostApi($url, ['begin_date' => $beginDate, 'end_date' => $endDate], true); - } - - /** - * 访问趋势(日) - * @param string $beginDate 开始日期 - * @param string $endDate 结束日期,需同一天 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getWeanalysisAppidDailyVisittrend($beginDate, $endDate) - { - $url = 'https://api.weixin.qq.com/datacube/getweanalysisappiddailyvisittrend?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['begin_date' => $beginDate, 'end_date' => $endDate], true); - } - - /** - * 访问趋势(周) - * @param string $begin_date 周一 - * @param string $end_date 周日 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getWeanalysisAppidWeeklyVisittrend($begin_date, $end_date) - { - $url = 'https://api.weixin.qq.com/datacube/getweanalysisappidweeklyvisittrend?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['begin_date' => $begin_date, 'end_date' => $end_date], true); - } - - /** - * 访问趋势(月) - * @param string $begin_date 月首日 - * @param string $end_date 月末日 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getWeanalysisAppidMonthlyVisittrend($begin_date, $end_date) - { - $url = 'https://api.weixin.qq.com/datacube/getweanalysisappidmonthlyvisittrend?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['begin_date' => $begin_date, 'end_date' => $end_date], true); - } - - /** - * 访问分布 - * @param string $begin_date 开始日期 - * @param string $end_date 结束日期,需同一天 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getWeanalysisAppidVisitdistribution($begin_date, $end_date) - { - $url = 'https://api.weixin.qq.com/datacube/getweanalysisappidvisitdistribution?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['begin_date' => $begin_date, 'end_date' => $end_date], true); - } - - /** - * 留存(日) - * @param string $begin_date 开始日期 - * @param string $end_date 结束日期,需同一天 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getWeanalysisAppidDailyRetaininfo($begin_date, $end_date) - { - $url = 'https://api.weixin.qq.com/datacube/getweanalysisappiddailyretaininfo?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['begin_date' => $begin_date, 'end_date' => $end_date], true); - } - - /** - * 留存(周) - * @param string $begin_date 周一 - * @param string $end_date 周日 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getWeanalysisAppidWeeklyRetaininfo($begin_date, $end_date) - { - $url = 'https://api.weixin.qq.com/datacube/getweanalysisappidweeklyretaininfo?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['begin_date' => $begin_date, 'end_date' => $end_date], true); - } - - /** - * 留存(月) - * @param string $begin_date 月首日 - * @param string $end_date 月末日 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getWeanalysisAppidMonthlyRetaininfo($begin_date, $end_date) - { - $url = 'https://api.weixin.qq.com/datacube/getweanalysisappidmonthlyretaininfo?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['begin_date' => $begin_date, 'end_date' => $end_date], true); - } - - /** - * 访问页面数据 - * @param string $begin_date 开始日期 - * @param string $end_date 结束日期,需同一天 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getWeanalysisAppidVisitPage($begin_date, $end_date) - { - $url = 'https://api.weixin.qq.com/datacube/getweanalysisappidvisitpage?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['begin_date' => $begin_date, 'end_date' => $end_date], true); - } - - /** - * 用户画像 - * @param string $begin_date 开始日期 - * @param string $end_date 结束日期,间隔为0/6/29 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function getWeanalysisAppidUserportrait($begin_date, $end_date) - { - $url = 'https://api.weixin.qq.com/datacube/getweanalysisappiduserportrait?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['begin_date' => $begin_date, 'end_date' => $end_date], true); - } -} \ No newline at end of file diff --git a/WeMini/crypt/errorCode.php b/WeMini/crypt/errorCode.php deleted file mode 100644 index a4f8e72..0000000 --- a/WeMini/crypt/errorCode.php +++ /dev/null @@ -1,20 +0,0 @@ - - *
  • -41001: encodingAesKey 非法
  • - *
  • -41003: aes 解密失败
  • - *
  • -41004: 解密后得到的buffer非法
  • - *
  • -41005: base64加密失败
  • - *
  • -41016: base64解密失败
  • - * - */ -class ErrorCode -{ - public static $OK = 0; - public static $IllegalAesKey = -41001; - public static $IllegalIv = -41002; - public static $IllegalBuffer = -41003; - public static $DecodeBase64Error = -41004; -} \ No newline at end of file diff --git a/WeMini/crypt/wxBizDataCrypt.php b/WeMini/crypt/wxBizDataCrypt.php deleted file mode 100644 index fde8d27..0000000 --- a/WeMini/crypt/wxBizDataCrypt.php +++ /dev/null @@ -1,57 +0,0 @@ -appid = $appid; - $this->sessionKey = $sessionKey; - include_once __DIR__ . DIRECTORY_SEPARATOR . "errorCode.php"; - } - - /** - * 检验数据的真实性,并且获取解密后的明文. - * @param $encryptedData string 加密的用户数据 - * @param $iv string 与用户数据一同返回的初始向量 - * @param $data string 解密后的原文 - * - * @return int 成功0,失败返回对应的错误码 - */ - public function decryptData($encryptedData, $iv, &$data) - { - if (strlen($this->sessionKey) != 24) { - return ErrorCode::$IllegalAesKey; - } - $aesKey = base64_decode($this->sessionKey); - if (strlen($iv) != 24) { - return ErrorCode::$IllegalIv; - } - $aesIV = base64_decode($iv); - $aesCipher = base64_decode($encryptedData); - $result = openssl_decrypt($aesCipher, "AES-128-CBC", $aesKey, 1, $aesIV); - $dataObj = json_decode($result); - if ($dataObj == null) { - return ErrorCode::$IllegalBuffer; - } - // 兼容新版本无 watermark 的情况 - if (isset($dataObj->watermark) && $dataObj->watermark->appid != $this->appid) { - return ErrorCode::$IllegalBuffer; - } - $data = $result; - return ErrorCode::$OK; - } - -} - diff --git a/WePay/Bill.php b/WePay/Bill.php deleted file mode 100644 index 2e4a6c9..0000000 --- a/WePay/Bill.php +++ /dev/null @@ -1,64 +0,0 @@ -params->set('sign_type', 'MD5'); - $params = $this->params->merge($options); - $params['sign'] = $this->getPaySign($params, 'MD5'); - $result = Tools::post('https://api.mch.weixin.qq.com/pay/downloadbill', Tools::arr2xml($params)); - if (is_array($jsonData = Tools::xml3arr($result))) { - if ($jsonData['return_code'] !== 'SUCCESS') { - throw new InvalidResponseException($jsonData['return_msg'], '0'); - } - } - return is_null($outType) ? $result : $outType($result); - } - - - /** - * 拉取订单评价数据(需证书) - * @param array $options 查询参数(bill_date, offset, limit 等) - * @return array 评价数据 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function comment(array $options) - { - $url = 'https://api.mch.weixin.qq.com/billcommentsp/batchquerycomment'; - return $this->callPostApi($url, $options, true); - } -} \ No newline at end of file diff --git a/WePay/Coupon.php b/WePay/Coupon.php deleted file mode 100644 index f31101e..0000000 --- a/WePay/Coupon.php +++ /dev/null @@ -1,66 +0,0 @@ -callPostApi($url, $options, true, 'MD5'); - } - - /** - * 查询代金券批次 - * @param array $options 查询参数(coupon_stock_id 等) - * @return array 批次详情 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function queryStock(array $options) - { - $url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/query_coupon_stock"; - return $this->callPostApi($url, $options, false); - } - - /** - * 查询单个代金券信息 - * @param array $options 查询参数(coupon_id, openid, stock_id 等) - * @return array 代金券详情 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function queryInfo(array $options) - { - $url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/query_coupon_stock"; - return $this->callPostApi($url, $options, false); - } - -} \ No newline at end of file diff --git a/WePay/Custom.php b/WePay/Custom.php deleted file mode 100644 index 8466833..0000000 --- a/WePay/Custom.php +++ /dev/null @@ -1,68 +0,0 @@ -callPostApi($url, $options, false, 'MD5', false, false); - } - - /** - * 海关申报:订单附加信息查询 - * @param array $options 查询参数(transaction_id 或 out_trade_no,customs,mch_customs_no 等) - * @return array 申报状态 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function get(array $options = []) - { - $url = 'https://api.mch.weixin.qq.com/cgi-bin/mch/customs/customdeclarequery'; - return $this->callPostApi($url, $options, false, 'MD5', true, false); - } - - - /** - * 海关申报:重推申报信息 - * @param array $options 重推参数(transaction_id 或 out_trade_no,customs,mch_customs_no 等) - * @return array 重推结果 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function reset(array $options = []) - { - $url = 'https://api.mch.weixin.qq.com/cgi-bin/mch/newcustoms/customdeclareredeclare'; - return $this->callPostApi($url, $options, false, 'MD5', true, false); - } - -} \ No newline at end of file diff --git a/WePay/Order.php b/WePay/Order.php deleted file mode 100644 index 9d3a17e..0000000 --- a/WePay/Order.php +++ /dev/null @@ -1,174 +0,0 @@ -callPostApi($url, $options, false, 'MD5'); - } - - /** - * 刷卡支付接口(被扫支付) - * @param array $options 支付参数(auth_code, out_trade_no, body, total_fee等) - * @return array 支付结果 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function micropay(array $options) - { - $url = 'https://api.mch.weixin.qq.com/pay/micropay'; - return $this->callPostApi($url, $options, false, 'MD5'); - } - - /** - * 查询订单接口 - * @param array $options 查询参数(transaction_id或out_trade_no二选一) - * @return array 订单详情 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function query(array $options) - { - $url = 'https://api.mch.weixin.qq.com/pay/orderquery'; - return $this->callPostApi($url, $options); - } - - /** - * 关闭订单接口 - * @param string $outTradeNo 商户订单号 - * @return array 关闭结果 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function close($outTradeNo) - { - $url = 'https://api.mch.weixin.qq.com/pay/closeorder'; - return $this->callPostApi($url, ['out_trade_no' => $outTradeNo]); - } - - /** - * 生成JSAPI支付参数(用于前端调起支付) - * @param string $prepayId 统一下单返回的prepay_id - * @return array JSAPI支付所需参数(appId, timeStamp, nonceStr, package, signType, paySign) - */ - public function jsapiParams($prepayId) - { - $option = []; - $option["appId"] = $this->config->get('appid'); - $option["timeStamp"] = (string)time(); - $option["nonceStr"] = Tools::createNoncestr(); - $option["package"] = "prepay_id={$prepayId}"; - $option["signType"] = "MD5"; - $option["paySign"] = $this->getPaySign($option, 'MD5'); - $option['timestamp'] = $option['timeStamp']; - return $option; - } - - /** - * 生成Native支付二维码URL - * @param string $productId 商品ID或订单号 - * @return string 支付二维码URL(weixin://wxpay/bizpayurl?xxx) - */ - public function qrcParams($productId) - { - $data = [ - 'appid' => $this->config->get('appid'), - 'mch_id' => $this->config->get('mch_id'), - 'time_stamp' => (string)time(), - 'nonce_str' => Tools::createNoncestr(), - 'product_id' => (string)$productId, - ]; - $data['sign'] = $this->getPaySign($data, 'MD5'); - return "weixin://wxpay/bizpayurl?" . http_build_query($data); - } - - /** - * 生成APP支付参数(用于移动应用调起支付) - * @param string $prepayId 统一下单返回的prepay_id - * @return array APP支付所需参数(appid, partnerid, prepayid, package, timestamp, noncestr, sign) - */ - public function appParams($prepayId) - { - $data = [ - 'appid' => $this->config->get('appid'), - 'partnerid' => $this->config->get('mch_id'), - 'prepayid' => (string)$prepayId, - 'package' => 'Sign=WXPay', - 'timestamp' => (string)time(), - 'noncestr' => Tools::createNoncestr(), - ]; - $data['sign'] = $this->getPaySign($data, 'MD5'); - return $data; - } - - /** - * 撤销订单接口(刷卡支付专用,需要证书) - * @param array $options 撤销参数(transaction_id或out_trade_no二选一) - * @return array 撤销结果 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function reverse(array $options) - { - $url = 'https://api.mch.weixin.qq.com/secapi/pay/reverse'; - return $this->callPostApi($url, $options, true); - } - - /** - * 授权码查询openid接口 - * @param string $authCode 授权码(用户微信中的条码或二维码信息) - * @return array 包含openid等信息 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function queryAuthCode($authCode) - { - $url = 'https://api.mch.weixin.qq.com/tools/authcodetoopenid'; - return $this->callPostApi($url, ['auth_code' => $authCode], false, 'MD5', false); - } - - /** - * 交易保障接口(用于上报交易数据) - * @param array $options 上报参数(interface_url, execute_time, return_code, result_code等) - * @return array 上报结果 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function report(array $options) - { - $url = 'https://api.mch.weixin.qq.com/payitil/report'; - return $this->callPostApi($url, $options); - } -} \ No newline at end of file diff --git a/WePay/ProfitSharing.php b/WePay/ProfitSharing.php deleted file mode 100644 index 8039e01..0000000 --- a/WePay/ProfitSharing.php +++ /dev/null @@ -1,144 +0,0 @@ -callPostApi($url, $options, true); - } - - /** - * 请求多次分账(需证书) - * @param array $options 分账参数(transaction_id, out_order_no, receivers 等) - * @return array 分账结果 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function multiProfitSharing(array $options) - { - $url = 'https://api.mch.weixin.qq.com/secapi/pay/multiprofitsharing'; - return $this->callPostApi($url, $options, true); - } - - /** - * 查询分账结果 - * @param array $options 查询参数(transaction_id 与 out_order_no) - * @return array 分账状态 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function profitSharingQuery(array $options) - { - $url = 'https://api.mch.weixin.qq.com/pay/profitsharingquery'; - return $this->callPostApi($url, $options); - } - - /** - * 添加分账接收方 - * @param array $options 接收方信息(type, account, name, relation_type 等) - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function profitSharingAddReceiver(array $options) - { - $url = 'https://api.mch.weixin.qq.com/pay/profitsharingaddreceiver'; - return $this->callPostApi($url, $options); - } - - /** - * 删除分账接收方 - * @param array $options 接收方信息(type, account) - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function profitSharingRemoveReceiver(array $options) - { - $url = 'https://api.mch.weixin.qq.com/pay/profitsharingremovereceiver'; - return $this->callPostApi($url, $options); - } - - /** - * 完结分账(需证书) - * @param array $options 完结参数(transaction_id, out_order_no, description 等) - * @return array 完结结果 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function profitSharingFinish(array $options) - { - $url = 'https://api.mch.weixin.qq.com/secapi/pay/profitsharingfinish'; - return $this->callPostApi($url, $options, true); - } - - /** - * 查询订单待分账金额 - * @param array $options 查询参数(transaction_id) - * @return array 待分账金额 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function profitSharingOrderAmountQuery(array $options) - { - $url = 'https://api.mch.weixin.qq.com/pay/profitsharingorderamountquery'; - return $this->callPostApi($url, $options); - } - - /** - * 分账回退(需证书) - * @param array $options 回退参数(out_return_no, out_order_no, return_account_type 等) - * @return array 回退结果 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function profitSharingReturn(array $options) - { - $url = 'https://api.mch.weixin.qq.com/secapi/pay/profitsharingreturn'; - return $this->callPostApi($url, $options, true); - } - - /** - * 回退结果查询 - * @param array $options 查询参数(out_return_no 与 out_order_no) - * @return array 回退状态 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function profitSharingReturnQuery(array $options) - { - $url = 'https://api.mch.weixin.qq.com/pay/profitsharingreturnquery'; - return $this->callPostApi($url, $options); - } -} diff --git a/WePay/Redpack.php b/WePay/Redpack.php deleted file mode 100644 index c7ccfad..0000000 --- a/WePay/Redpack.php +++ /dev/null @@ -1,73 +0,0 @@ -params->offsetUnset('appid'); - $this->params->set('wxappid', $this->config->get('appid')); - $url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack"; - return $this->callPostApi($url, $options, true, 'MD5', false); - } - - /** - * 发放裂变红包(需证书) - * @param array $options 红包参数(mch_billno, send_name, re_openid, total_amount, total_num 等,total_num>1) - * @return array 红包发送结果 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function groups(array $options) - { - $this->params->offsetUnset('appid'); - $this->params->set('wxappid', $this->config->get('appid')); - $url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendgroupredpack"; - return $this->callPostApi($url, $options, true, 'MD5', false); - } - - /** - * 查询红包记录 - * @param string $mchBillno 商户红包订单号 - * @return array 红包状态信息 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function query($mchBillno) - { - $this->params->offsetUnset('wxappid'); - $this->params->set('appid', $this->config->get('appid')); - $url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/gethbinfo"; - return $this->callPostApi($url, ['mch_billno' => $mchBillno, 'bill_type' => 'MCHT'], true, 'MD5', false); - } - -} \ No newline at end of file diff --git a/WePay/Refund.php b/WePay/Refund.php deleted file mode 100644 index b5f5ece..0000000 --- a/WePay/Refund.php +++ /dev/null @@ -1,80 +0,0 @@ -callPostApi($url, $options, true); - } - - /** - * 查询退款接口 - * @param array $options 查询参数(transaction_id, out_trade_no, out_refund_no, refund_id四选一) - * @return array 退款详情 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function query(array $options) - { - $url = 'https://api.mch.weixin.qq.com/pay/refundquery'; - return $this->callPostApi($url, $options); - } - - /** - * 解析退款通知(自动解密req_info) - * @param string|array $xml 退款通知XML数据,为空则从POST获取 - * @return array 解密后的退款通知数据 - * @throws \WeChat\Exceptions\InvalidDecryptException - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function getNotify($xml = '') - { - $data = is_array($xml) ? $xml : Tools::xml2arr(empty($xml) ? Tools::getRawInput() : $xml); - if (!isset($data['return_code']) || $data['return_code'] !== 'SUCCESS') { - throw new InvalidResponseException('获取退款通知XML失败!'); - } - try { - $key = md5($this->config->get('mch_key')); - $decrypt = base64_decode($data['req_info']); - $response = openssl_decrypt($decrypt, 'aes-256-ecb', $key, OPENSSL_RAW_DATA); - $data['result'] = Tools::xml2arr($response); - return $data; - } catch (\Exception $exception) { - throw new InvalidDecryptException($exception->getMessage(), $exception->getCode()); - } - } -} \ No newline at end of file diff --git a/WePay/Transfers.php b/WePay/Transfers.php deleted file mode 100644 index 0d9248e..0000000 --- a/WePay/Transfers.php +++ /dev/null @@ -1,62 +0,0 @@ -params->offsetUnset('appid'); - $this->params->offsetUnset('mch_id'); - $this->params->set('mchid', $this->config->get('mch_id')); - $this->params->set('mch_appid', $this->config->get('appid')); - $url = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers'; - return $this->callPostApi($url, $options, true, 'MD5', false); - } - - /** - * 查询企业付款到零钱结果 - * @param string $partnerTradeNo 商户付款单号 - * @return array 付款状态 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function query($partnerTradeNo) - { - $this->params->offsetUnset('mchid'); - $this->params->offsetUnset('mch_appid'); - $this->params->set('appid', $this->config->get('appid')); - $this->params->set('mch_id', $this->config->get('mch_id')); - $url = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo'; - return $this->callPostApi($url, ['partner_trade_no' => $partnerTradeNo], true, 'MD5', false); - } - -} \ No newline at end of file diff --git a/WePay/TransfersBank.php b/WePay/TransfersBank.php deleted file mode 100644 index 3d4c296..0000000 --- a/WePay/TransfersBank.php +++ /dev/null @@ -1,125 +0,0 @@ -params->offsetUnset('appid'); - return $this->callPostApi('https://api.mch.weixin.qq.com/mmpaysptrans/pay_bank', [ - 'amount' => $options['amount'], - 'bank_code' => $options['bank_code'], - 'partner_trade_no' => $options['partner_trade_no'], - 'enc_bank_no' => $this->rsaEncode($options['enc_bank_no']), - 'enc_true_name' => $this->rsaEncode($options['enc_true_name']), - 'desc' => isset($options['desc']) ? $options['desc'] : '', - ], true, 'MD5', false); - } - - /** - * RSA 加密银行卡号/姓名 - * @param string $string 待加密字符串 - * @param string $encrypted - * @return string base64 编码密文 - * @throws \WeChat\Exceptions\InvalidDecryptException - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - private function rsaEncode($string, $encrypted = '') - { - $search = ['-----BEGIN RSA PUBLIC KEY-----', '-----END RSA PUBLIC KEY-----', "\n", "\r"]; - $pkc1 = str_replace($search, '', $this->getRsaContent()); - $publicKey = '-----BEGIN PUBLIC KEY-----' . PHP_EOL . - wordwrap('MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A' . $pkc1, 64, PHP_EOL, true) . PHP_EOL . - '-----END PUBLIC KEY-----'; - if (!openssl_public_encrypt("{$string}", $encrypted, $publicKey, OPENSSL_PKCS1_OAEP_PADDING)) { - throw new InvalidDecryptException('Rsa Encrypt Error.'); - } - return base64_encode($encrypted); - } - - /** - * 获取 RSA 公钥内容 - * @return string 公钥内容 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - private function getRsaContent() - { - $cacheKey = "pub_ras_key_" . $this->config->get('mch_id'); - if (($pub_key = Tools::getCache($cacheKey))) { - return $pub_key; - } - $data = $this->callPostApi('https://fraud.mch.weixin.qq.com/risk/getpublickey', [], true, 'MD5'); - if (!isset($data['return_code']) || $data['return_code'] !== 'SUCCESS' || $data['result_code'] !== 'SUCCESS') { - $error = 'ResultError:' . $data['return_msg']; - $error .= isset($data['err_code_des']) ? ' - ' . $data['err_code_des'] : ''; - throw new InvalidResponseException($error, 20000, $data); - } - Tools::setCache($cacheKey, $data['pub_key'], 600); - return $data['pub_key']; - } - - /** - * 查询企业付款到银行卡结果 - * @param string $partnerTradeNo 商户订单号 - * @return array 付款状态 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function query($partnerTradeNo) - { - $this->params->offsetUnset('appid'); - $url = 'https://api.mch.weixin.qq.com/mmpaysptrans/query_bank'; - return $this->callPostApi($url, ['partner_trade_no' => $partnerTradeNo], true, 'MD5', false); - } -} \ No newline at end of file diff --git a/WePayV3/Cert.php b/WePayV3/Cert.php deleted file mode 100644 index 32256e3..0000000 --- a/WePayV3/Cert.php +++ /dev/null @@ -1,66 +0,0 @@ -doRequest('GET', '/v3/certificates'); - if (empty($result['data']) && !empty($result['message'])) { - throw new InvalidResponseException($result['message']); - } - $decrypt = new DecryptAes($this->config['mch_v3_key']); - foreach ($result['data'] as $vo) { - $certs[$vo['serial_no']] = [ - 'expire' => strtotime($vo['expire_time']), - 'serial' => $vo['serial_no'], - 'content' => $decrypt->decryptToString( - $vo['encrypt_certificate']['associated_data'], - $vo['encrypt_certificate']['nonce'], - $vo['encrypt_certificate']['ciphertext'] - ) - ]; - } - $this->tmpFile("{$this->config['mch_id']}_certs", $certs); - } catch (\Exception $exception) { - throw new InvalidResponseException($exception->getMessage(), $exception->getCode()); - } - } -} \ No newline at end of file diff --git a/WePayV3/Complaints.php b/WePayV3/Complaints.php deleted file mode 100644 index 156ec96..0000000 --- a/WePayV3/Complaints.php +++ /dev/null @@ -1,146 +0,0 @@ -config['mch_id']; - $pathinfo = "/v3/merchant-service/complaints-v2?limit={$limit}&offset={$offset}&begin_date={$begin_date}&end_date={$end_date}&complainted_mchid={$mchId}"; - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 查询投诉详情 - * @param string $complaint_id 投诉单号 - * @return array|string 投诉详情 - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function complaintDetails($complaint_id) - { - $pathinfo = "/v3/merchant-service/complaints-v2/{$complaint_id}"; - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 查询投诉协商历史 - * @param string $complaint_id 投诉单号 - * @return array|string 协商记录 - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function negotiationHistory($complaint_id) - { - $pathinfo = "/v3/merchant-service/complaints-v2/{$complaint_id}/negotiation-historys"; - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 创建投诉通知回调地址 - * @param string $url 回调地址 - * @return array|string - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function CreateComplaintsNotify($url) - { - return $this->doRequest('POST', '/v3/merchant-service/complaint-notifications', json_encode(['url' => $url], JSON_UNESCAPED_UNICODE), true); - - } - - /** - * 查询投诉通知回调地址 - * @return array|string - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function queryComplaintsNotify() - { - return $this->doRequest('GET', '/v3/merchant-service/complaint-notifications', '', true); - - } - - /** - * 更新投诉通知回调地址 - * @param string $url 回调地址 - * @return array|string - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function updateComplaintsNotify($url) - { - return $this->doRequest('PUT', '/v3/merchant-service/complaint-notifications', json_encode(['url' => $url], JSON_UNESCAPED_UNICODE), true); - } - - /** - * 删除投诉通知回调地址 - * @return array|string - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function deleteComplaintsNotify() - { - return $this->doRequest('DELETE', '/v3/merchant-service/complaint-notifications', '', true); - } - - /** - * 回复投诉 - * @param string $complaint_id 投诉单号 - * @param array $content 回复内容(含跳转链接、文本等) - * @return array|string - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function replyInfo($complaint_id, array $content) - { - $content['complainted_mchid'] = $this->config['mch_id']; - $pathinfo = "/v3/merchant-service/complaints-v2/{$complaint_id}/response"; - return $this->doRequest('POST', $pathinfo, json_encode($content, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 标记投诉处理完成 - * @param string $complaint_id 投诉单号 - * @return array|string - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function completeComplaints($complaint_id) - { - $mchId = $this->config['mch_id']; - $pathinfo = "/v3/merchant-service/complaints-v2/{$complaint_id}/complete"; - return $this->doRequest('POST', $pathinfo, json_encode(['complainted_mchid' => $mchId], JSON_UNESCAPED_UNICODE), true); - } - - /** - * 下载投诉图片 - * @param string $pathinfo 文件路径(接口返回的下载地址 path) - * @return array|string - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function downLoadImg($pathinfo) - { - return $this->doRequest('GET', $pathinfo, '', true, false); - } - - /** - * 上传投诉相关图片 - * @param array $imginfo 图片上传参数(文件流对应的 media 字段等) - * @return array|string - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function uploadImg(array $imginfo) - { - return $this->doRequest('POST', '/v3/merchant-service/images/upload', json_encode($imginfo, JSON_UNESCAPED_UNICODE), true); - } -} \ No newline at end of file diff --git a/WePayV3/Contracts/BasicWePay.php b/WePayV3/Contracts/BasicWePay.php deleted file mode 100644 index b880f1f..0000000 --- a/WePayV3/Contracts/BasicWePay.php +++ /dev/null @@ -1,535 +0,0 @@ - '', // 微信绑定APPID,需配置 - 'mch_id' => '', // 微信商户编号,需要配置 - 'mch_v3_key' => '', // 微信商户密钥,需要配置 - 'cert_serial' => '', // 商户证书序号,无需配置 - 'cert_public' => '', // 商户公钥内容,需要配置 - 'cert_private' => '', // 商户密钥内容,需要配置 - 'cert_package' => [], // 平台证书或支付证书配置 - 'mp_cert_serial' => '', // 平台证书序号,无需配置 ( 指定平台证书或支付公钥 ) - 'mp_cert_content' => '', // 平台证书内容,无需配置 ( 指定平台证书或支付公钥 ) - ]; - - /** - * BasicWePayV3 constructor. - * @param array $options [mch_id, mch_v3_key, cert_public, cert_private] - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function __construct(array $options = []) - { - if (empty($options['mch_id'])) { - throw new InvalidArgumentException("Missing Config -- [mch_id]"); - } - if (empty($options['mch_v3_key'])) { - throw new InvalidArgumentException("Missing Config -- [mch_v3_key]"); - } - if (empty($options['cert_public'])) { - throw new InvalidArgumentException("Missing Config -- [cert_public]"); - } - if (empty($options['cert_private'])) { - throw new InvalidArgumentException("Missing Config -- [cert_private]"); - } - - if (stripos($options['cert_public'], '-----BEGIN CERTIFICATE-----') === false) { - if (file_exists($options['cert_public'])) { - $options['cert_public'] = file_get_contents($options['cert_public']); - } else { - throw new InvalidArgumentException("File Non-Existent -- [cert_public]"); - } - } - - if (stripos($options['cert_private'], '-----BEGIN PRIVATE KEY-----') === false) { - if (file_exists($options['cert_private'])) { - $options['cert_private'] = file_get_contents($options['cert_private']); - } else { - throw new InvalidArgumentException("File Non-Existent -- [cert_private]"); - } - } - - $this->config['appid'] = isset($options['appid']) ? $options['appid'] : ''; - $this->config['mch_id'] = $options['mch_id']; - $this->config['mch_v3_key'] = $options['mch_v3_key']; - $this->config['cert_public'] = $options['cert_public']; - $this->config['cert_private'] = $options['cert_private']; - if (empty($options['cert_serial'])) { - $this->config['cert_serial'] = openssl_x509_parse($this->config['cert_public'], true)['serialNumberHex']; - } else { - $this->config['cert_serial'] = $options['cert_serial']; - } - if (empty($this->config['cert_serial'])) { - throw new InvalidArgumentException('Failed to parse certificate public key'); - } - - if (!empty($options['cache_path'])) { - Tools::$cache_path = $options['cache_path']; - } - - // 批量设置自定义证书 - if (isset($options['cert_package']) && is_array($options['cert_package'])) { - foreach ($options['cert_package'] as $key => $cert) { - $this->withCertContent($key, $cert); - } - } - - // 自动配置平台证书或支付公钥 - if (empty($options['mp_cert_serial']) || empty($options['mp_cert_content'])) { - if ($this->autoCert && !$this->withCertPayment()) { - $this->_autoCert(); - } - } elseif ($this->withCertContent($options['mp_cert_serial'], $options['mp_cert_content'])) { - $this->config['mp_cert_serial'] = $options['mp_cert_serial']; - $this->config['mp_cert_content'] = $options['mp_cert_content']; - } - - // 服务商参数支持 -// if (!empty($options['sp_appid'])) { -// $this->config['sp_appid'] = $options['sp_appid']; -// } -// if (!empty($options['sp_mchid'])) { -// $this->config['sp_mchid'] = $options['sp_mchid']; -// } -// if (!empty($options['sub_appid'])) { -// $this->config['sub_appid'] = $options['sub_appid']; -// } -// if (!empty($options['sub_mch_id'])) { -// $this->config['sub_mch_id'] = $options['sub_mch_id']; -// } - } - - /** - * 设置证书内容 - * @param string $key 证书ID或序号 - * @param string $cert 证书文本内容 - * @return string - * @throws \WeChat\Exceptions\InvalidResponseException - */ - private function withCertContent($key, $cert) - { - if (substr(trim($cert), 0, 5) == '-----') { - $this->config['cert_package'][$key] = $cert; - } elseif (file_exists($cert)) { - $this->config['cert_package'][$key] = file_get_contents($cert); - } else { - throw new InvalidResponseException("证书设置失败!"); - } - return $cert; - } - - /** - * 获取支付证书 - * @return mixed|string - */ - private function withCertPayment() - { - foreach ($this->config['cert_package'] as $key => $cert) { - if (strpos($key, 'PUB_KEY_ID_') === 0) { - if (empty($this->config['mp_cert_serial']) || empty($this->config['mp_cert_content'])) { - $this->config['mp_cert_serial'] = $key; - $this->config['mp_cert_content'] = $cert; - } - return $cert; - } - } - return ''; - } - - /** - * 自动配置平台证书 - * @return void - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - protected function _autoCert() - { - $certs = $this->tmpFile("{$this->config['mch_id']}_certs"); - if (is_array($certs)) foreach ($certs as $k => $v) { - if ($v['expire'] < time()) unset($certs[$k]); - } - if (empty($certs)) { - Cert::instance($this->config)->download(); - $certs = $this->tmpFile("{$this->config['mch_id']}_certs"); - } - if (empty($certs) || !is_array($certs)) { - throw new InvalidResponseException("读取平台证书失败!"); - } - foreach ($certs as $k => $v) if ($v['expire'] > time() + 10) { - $this->config['cert_package'][$k] = $v['content']; - if (empty($this->config['mp_cert_serial'])) { - $this->config['mp_cert_serial'] = $k; - $this->config['mp_cert_content'] = $v['content']; - } - } - if (empty($this->config['cert_package'])) { - throw new InvalidResponseException("自动配置平台证书失败!"); - } - } - - /** - * 写入或读取临时文件 - * @param string $name - * @param null|array|string $content - * @param integer $expire - * @return array|string - * @throws \WeChat\Exceptions\LocalCacheException - */ - protected function tmpFile($name, $content = null, $expire = 7200) - { - if (is_null($content)) { - $text = Tools::getCache($name); - if (empty($text)) return ''; - $json = json_decode(Tools::getCache($name) ?: '', true); - return isset($json[0]) ? $json[0] : ''; - } else { - return Tools::setCache($name, json_encode([$content], JSON_UNESCAPED_UNICODE), $expire); - } - } - - /** - * 静态创建对象 - * @param array $config - * @return static - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public static function instance($config) - { - $key = md5(get_called_class() . serialize($config)); - if (isset(self::$cache[$key])) return self::$cache[$key]; - return self::$cache[$key] = new static($config); - } - - /** - * 模拟发起上传请求 - * @param string $pathinfo 请求路由 - * @param string $filename 文件本地路径 - * @param boolean $verify 是否验证 - * @param boolean $isjson 返回JSON - * @return array|string - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function doUpload($pathinfo, $filename, $verify = false, $isjson = true) - { - $filedata = file_get_contents($filename); - $fileinfo = [ - 'sha256' => hash("sha256", $filedata), - 'filename' => basename($filename) - ]; - $jsondata = json_encode($fileinfo); - list($time, $nonce) = [time(), uniqid() . rand(1000, 9999)]; - $signstr = join("\n", ['POST', $pathinfo, $time, $nonce, $jsondata, '']); - // 生成签名 - $sign = $this->signBuild($signstr); - // 生成数据签名TOKEN - $token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"', - $this->config['mch_id'], $nonce, $time, $this->config['cert_serial'], $sign - ); - $location = (preg_match('|^https?://|', $pathinfo) ? '' : $this->base) . $pathinfo; - $boundary = mt_rand(100000000000000000, 999999999999999999); - $header = [ - 'Accept: application/json', - "Content-Type: multipart/form-data; boundary={$boundary}", - 'User-Agent: https://thinkadmin.top', - "Authorization: WECHATPAY2-SHA256-RSA2048 {$token}", - "serial_no: {$this->config['mp_cert_serial']}", - "nonce_str: {$nonce}", - "signature: {$sign}" - ]; - $line = []; - $line[] = "--{$boundary}"; - $line[] = "Content-Disposition: form-data; name=\"meta\""; - $line[] = "Content-Type: application/json"; - $line[] = ""; - $line[] = $jsondata; - $line[] = "--{$boundary}"; - $line[] = "Content-Disposition: form-data; name=\"file\"; filename=\"{$fileinfo['filename']}\";"; - $line[] = "Content-Type: image/jpg"; - $line[] = ""; - $line[] = $filedata; - $line[] = "--{$boundary}--"; - $postdata = join("\r\n", $line); - list($header, $content) = $this->_doRequestCurl('POST', $location, [ - 'data' => $postdata, 'header' => $header, - ]); - if ($verify) { - $headers = []; - foreach (explode("\n", $header) as $line) { - if (stripos($line, 'Wechatpay') !== false) { - list($name, $value) = explode(':', $line); - list(, $keys) = explode('wechatpay-', strtolower($name)); - $headers[$keys] = trim($value); - } - } - try { - if (empty($headers)) { - return $isjson ? json_decode($content, true) : $content; - } - $string = join("\n", [$headers['timestamp'], $headers['nonce'], $content, '']); - if (!$this->signVerify($string, $headers['signature'], $headers['serial'])) { - throw new InvalidResponseException('验证响应签名失败'); - } - } catch (\Exception $exception) { - throw new InvalidResponseException($exception->getMessage(), $exception->getCode()); - } - } - return $isjson ? json_decode($content, true) : $content; - } - - /** - * 生成数据签名 - * @param string $data 签名内容 - * @return string - */ - protected function signBuild($data) - { - $pkeyid = openssl_pkey_get_private($this->config['cert_private']); - if ($pkeyid === false) { - throw new InvalidArgumentException("Invalid private key -- [cert_private]"); - } - $signature = ''; - if (!openssl_sign($data, $signature, $pkeyid, 'sha256WithRSAEncryption')) { - $this->freeKey($pkeyid); - throw new InvalidArgumentException("Failed to sign data with private key"); - } - $this->freeKey($pkeyid); - return base64_encode($signature); - } - - /** - * 兼容释放 OpenSSL 密钥资源 - * @param mixed $pkey - * @return void - */ - protected function freeKey($pkey) - { - // PHP 8+ 中 openssl_free_key 已废弃,交由运行时回收即可。 - if (version_compare(PHP_VERSION, '8.0.0', '<') && function_exists('openssl_free_key')) { - openssl_free_key($pkey); - } - } - - /** - * 通过CURL模拟网络请求 - * @param string $method 请求方法 - * @param string $location 请求方法 - * @param array $options 请求参数 [data, header] - * @return array [header,content] - */ - private function _doRequestCurl($method, $location, $options = []) - { - $curl = curl_init(); - // POST数据设置 - if (strtolower($method) === 'post') { - curl_setopt($curl, CURLOPT_POST, true); - curl_setopt($curl, CURLOPT_POSTFIELDS, $options['data']); - } - // CURL头信息设置 - if (!empty($options['header'])) { - curl_setopt($curl, CURLOPT_HTTPHEADER, $options['header']); - } - curl_setopt($curl, CURLOPT_URL, $location); - curl_setopt($curl, CURLOPT_HEADER, true); - curl_setopt($curl, CURLOPT_TIMEOUT, 60); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); - $content = curl_exec($curl); - $headerSize = curl_getinfo($curl, CURLINFO_HEADER_SIZE); - curl_close($curl); - return [substr($content, 0, $headerSize), substr($content, $headerSize)]; - } - - /** - * 验证内容签名 - * @param string $data 签名内容 - * @param string $sign 原签名值 - * @param string $serial 证书序号 - * @return int - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - protected function signVerify($data, $sign, $serial) - { - if (stripos($serial, 'PUB_KEY_ID_') !== false && !empty($this->config['cert_package'][$serial])) { - return openssl_verify($data, base64_decode($sign), $this->config['cert_package'][$serial], OPENSSL_ALGO_SHA256); - } else { - return openssl_verify($data, base64_decode($sign), openssl_x509_read($this->_getCert($serial)), 'sha256WithRSAEncryption'); - } - } - - /** - * 获取平台证书 - * @param string $serial - * @return string - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - protected function _getCert($serial = '') - { - $certs = $this->tmpFile("{$this->config['mch_id']}_certs"); - if (empty($certs) || empty($certs[$serial]['serial']) || empty($certs[$serial]['content'])) { - Cert::instance($this->config)->download(); - $certs = $this->tmpFile("{$this->config['mch_id']}_certs"); - } - foreach ($certs as $cert) { - if (!empty($cert['serial']) && !empty($cert['content']) && $cert['expire'] > time()) { - $this->config['cert_package'][$cert['serial']] = $cert['content']; - if (empty($this->config['mp_cert_serial']) && (empty($serial) || $serial === $cert['serial'])) { - $this->config['mp_cert_serial'] = $cert['serial']; - $this->config['mp_cert_content'] = $cert['content']; - } - } - } - if (isset($this->config['cert_package'][$serial])) { - return $this->config['cert_package'][$serial]; - } elseif (!empty($this->config['mp_cert_content'])) { - return $this->config['mp_cert_content']; - } elseif ($cert = $this->withCertPayment()) { - return $cert; - } else { - throw new InvalidResponseException("读取平台证书失败!"); - } - } - - /** - * 通用接口调用(微信支付 V3) - * @param string $url 完整 URL 或相对路径(如 /v3/pay/transactions/jsapi) - * @param array|string $data 请求体,数组自动 JSON,字符串原样发送 - * @param string $method GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS,默认 POST - * @param bool $verify 是否验证返回签名 - * @return array 解析后的数组 - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function callApi($url, $data = '', $method = 'POST', $verify = false) - { - $method = strtoupper($method); - - // 处理数据 - if (is_array($data)) { - $data = json_encode($data, JSON_UNESCAPED_UNICODE); - } - - return $this->doRequest($method, $url, $data, $verify, true); - } - - /** - * 模拟发起请求 - * @param string $method 请求访问 - * @param string $pathinfo 请求路由 - * @param string $jsondata 请求数据 - * @param boolean $verify 是否验证 - * @param boolean $isjson 返回JSON - * @return array|string - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function doRequest($method, $pathinfo, $jsondata = '', $verify = false, $isjson = true) - { - list($time, $nonce) = [time(), uniqid() . rand(1000, 9999)]; - $signstr = join("\n", [$method, $pathinfo, $time, $nonce, $jsondata, '']); - - // 生成数据签名TOKEN - $token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"', - $this->config['mch_id'], $nonce, $time, $this->config['cert_serial'], $this->signBuild($signstr) - ); - $location = (preg_match('|^https?://|', $pathinfo) ? '' : $this->base) . $pathinfo; - list($header, $content) = $this->_doRequestCurl($method, $location, [ - 'data' => $jsondata, 'header' => [ - 'Accept: application/json', - 'Content-Type: application/json', - 'User-Agent: https://thinkadmin.top', - "Authorization: WECHATPAY2-SHA256-RSA2048 {$token}", - "Wechatpay-Serial: {$this->config['mp_cert_serial']}" - ], - ]); - - if ($verify) { - $headers = []; - foreach (explode("\n", $header) as $line) { - if (stripos($line, 'Wechatpay') !== false) { - list($name, $value) = explode(':', $line); - list(, $keys) = explode('wechatpay-', strtolower($name)); - $headers[$keys] = trim($value); - } - } - try { - if (empty($headers)) { - return $isjson ? json_decode($content, true) : $content; - } - $string = join("\n", [$headers['timestamp'], $headers['nonce'], $content, '']); - if (!$this->signVerify($string, $headers['signature'], $headers['serial'])) { - throw new InvalidResponseException('验证响应签名失败'); - } - } catch (\Exception $exception) { - throw new InvalidResponseException($exception->getMessage(), $exception->getCode()); - } - } - - return $isjson ? json_decode($content, true) : $content; - } - - /** - * RSA加密处理-平台证书 - * @param string $string - * @return string - * @throws \WeChat\Exceptions\InvalidDecryptException - */ - protected function rsaEncode($string) - { - $publicKey = $this->config['mp_cert_content']; - if (openssl_public_encrypt($string, $encrypted, $publicKey, OPENSSL_PKCS1_OAEP_PADDING)) { - return base64_encode($encrypted); - } else { - throw new InvalidDecryptException('Rsa Encrypt Error.'); - } - } -} diff --git a/WePayV3/Contracts/DecryptAes.php b/WePayV3/Contracts/DecryptAes.php deleted file mode 100644 index e4dfd75..0000000 --- a/WePayV3/Contracts/DecryptAes.php +++ /dev/null @@ -1,81 +0,0 @@ -aesKey = $aesKey; - } - - /** - * Decrypt AEAD_AES_256_GCM ciphertext - * @param string $associatedData AES GCM additional authentication data - * @param string $nonceStr AES GCM nonce - * @param string $ciphertext AES GCM cipher text - * @return string|bool Decrypted string on success or FALSE on failure - * @throws \WeChat\Exceptions\InvalidDecryptException - */ - public function decryptToString($associatedData, $nonceStr, $ciphertext) - { - $ciphertext = \base64_decode($ciphertext); - if (strlen($ciphertext) <= self::AUTH_TAG_LENGTH_BYTE) { - return false; - } - try { - // ext-sodium (default installed on >= PHP 7.2) - if (function_exists('\sodium_crypto_aead_aes256gcm_is_available') && \sodium_crypto_aead_aes256gcm_is_available()) { - return \sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->aesKey); - } - // ext-libsodium (need install libsodium-php 1.x via pecl) - if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') && \Sodium\crypto_aead_aes256gcm_is_available()) { - return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->aesKey); - } - // openssl (PHP >= 7.1 support AEAD) - if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) { - $ctext = substr($ciphertext, 0, -self::AUTH_TAG_LENGTH_BYTE); - $authTag = substr($ciphertext, -self::AUTH_TAG_LENGTH_BYTE); - return \openssl_decrypt($ctext, 'aes-256-gcm', $this->aesKey, \OPENSSL_RAW_DATA, $nonceStr, $authTag, $associatedData); - } - } catch (\Exception $exception) { - throw new InvalidDecryptException($exception->getMessage(), $exception->getCode()); - } catch (\SodiumException $exception) { - throw new InvalidDecryptException($exception->getMessage(), $exception->getCode()); - } - throw new InvalidDecryptException('AEAD_AES_256_GCM 需要 PHP 7.1 以上或者安装 libsodium-php'); - } -} \ No newline at end of file diff --git a/WePayV3/Coupon.php b/WePayV3/Coupon.php deleted file mode 100644 index 3f83d45..0000000 --- a/WePayV3/Coupon.php +++ /dev/null @@ -1,155 +0,0 @@ -doRequest('POST', $path, json_encode($data), true); - } - - /** - * 激活代金券批次 - * @param string $stock_id 批次号 - * @param string $stock_creator_mchid 创建批次的商户号 - * @return array|string - * @throws InvalidResponseException - */ - public function stocksStart($stock_id, $stock_creator_mchid) - { - $path = "/v3/marketing/favor/stocks/{$stock_id}/start"; - return $this->doRequest('POST', $path, json_encode(['stock_creator_mchid' => $stock_creator_mchid]), true); - } - - /** - * 暂停代金券批次 - * @param string $stock_id 批次号 - * @param string $stock_creator_mchid 创建批次的商户号 - * @return array|string - * @throws InvalidResponseException - */ - public function stocksPause($stock_id, $stock_creator_mchid) - { - $path = "/v3/marketing/favor/stocks/{$stock_id}/pause"; - return $this->doRequest('POST', $path, json_encode(['stock_creator_mchid' => $stock_creator_mchid]), true); - } - - /** - * 重启代金券批次 - * @param string $stock_id 批次号 - * @param string $stock_creator_mchid 创建批次的商户号 - * @return array|string - * @throws InvalidResponseException - */ - public function stocksRestart($stock_id, $stock_creator_mchid) - { - $path = "/v3/marketing/favor/stocks/{$stock_id}/restart"; - return $this->doRequest('POST', $path, json_encode(['stock_creator_mchid' => $stock_creator_mchid]), true); - } - - /** - * 查询批次详情 - * @param string $stock_id 批次号 - * @param string $stock_creator_mchid 创建批次的商户号 - * @return array|string 批次详情 - * @throws InvalidResponseException - */ - public function stocksDetail($stock_id, $stock_creator_mchid) - { - $path = "/v3/marketing/favor/stocks/{$stock_id}?stock_creator_mchid={$stock_creator_mchid}"; - return $this->doRequest('GET', $path, '', true); - } - - /** - * 代金券批次可用商品 - * @param array $param 包含 stock_id 等 - * @return array|string - * @throws InvalidResponseException - */ - public function stocksItems(array $param) - { - $path = "/v3/marketing/favor/stocks/{$param['stock_id']}/items "; - return $this->doRequest('POST', $path, json_encode($param), true); - } - - /** - * 设置消息通知地址 - * @param array $param 回调参数(notify_url, switch 等) - * @return array|string - * @throws InvalidResponseException - */ - public function setCallbacks(array $param) - { - $path = "/v3/marketing/favor/callbacks"; - return $this->doRequest('POST', $path, json_encode($param), true); - } - - /** - * 发放代金券 - * @param array $param 请求参数(openid, stock_id 等) - * @return array|string - * @throws InvalidResponseException - */ - public function couponsSend(array $param) - { - $path = "/v3/marketing/favor/users/{$param['openid']}/coupons"; - return $this->doRequest('POST', $path, json_encode($param), true); - } - - /** - * 查询用户名下券(按商户号) - * @param array $param 请求参数(openid, appid, stock_id 等) - * @return array|string 券列表 - * @throws InvalidResponseException - */ - public function couponsList(array $param) - { - $path = "/v3/marketing/favor/users/{$param['openid']}/coupons"; - return $this->doRequest('POST', $path, json_encode($param), true); - } - - /** - * 查询单张代金券详情 - * @param string $openid 用户 openid - * @param string $coupon_id 代金券 id - * @param string $appid 公众账号或小程序 appid - * @return array|string - * @throws InvalidResponseException - */ - public function couponsDetail($openid, $coupon_id, $appid) - { - $path = "/v3/marketing/favor/users/{$openid}/coupons/{$coupon_id}?appid={$appid}"; - return $this->doRequest('GET', $path, '', true); - } -} diff --git a/WePayV3/Ecommerce.php b/WePayV3/Ecommerce.php deleted file mode 100644 index e500ce1..0000000 --- a/WePayV3/Ecommerce.php +++ /dev/null @@ -1,775 +0,0 @@ -rsaEncode($data['id_card_info']['id_card_name']); - if (isset($data['id_card_info']['id_card_number'])) $data['id_card_info']['id_card_number'] = $this->rsaEncode($data['id_card_info']['id_card_number']); - } - if (isset($data['id_doc_info'])) { - if (isset($data['id_doc_info']['id_doc_name'])) $data['id_doc_info']['id_doc_name'] = $this->rsaEncode($data['id_doc_info']['id_doc_name']); - if (isset($data['id_doc_info']['id_doc_number'])) $data['id_doc_info']['id_doc_number'] = $this->rsaEncode($data['id_doc_info']['id_doc_number']); - } - if (isset($data['contact_info'])) { - if (isset($data['contact_info']['contact_name'])) $data['contact_info']['contact_name'] = $this->rsaEncode($data['contact_info']['contact_name']); - if (isset($data['contact_info']['contact_id_card_number'])) $data['contact_info']['contact_id_card_number'] = $this->rsaEncode($data['contact_info']['contact_id_card_number']); - if (isset($data['contact_info']['mobile_phone'])) $data['contact_info']['mobile_phone'] = $this->rsaEncode($data['contact_info']['mobile_phone']); - } - if (isset($data['account_info'])) { - if (isset($data['account_info']['account_name'])) $data['account_info']['account_name'] = $this->rsaEncode($data['account_info']['account_name']); - if (isset($data['account_info']['account_number'])) $data['account_info']['account_number'] = $this->rsaEncode($data['account_info']['account_number']); - } - if (!empty($data['ubo_info_list'])) { - $data['ubo_info_list'] = array_map(function ($item) { - $item['ubo_id_doc_name'] = $this->rsaEncode($item['ubo_id_doc_name']); - $item['ubo_id_doc_number'] = $this->rsaEncode($item['ubo_id_doc_number']); - $item['ubo_id_doc_address'] = $this->rsaEncode($item['ubo_id_doc_address']); - return $item; - }, $data['ubo_info_list']); - } - return $this->doRequest('POST', '/v3/ecommerce/applyments/', json_encode($data, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 按业务申请编号查询进件状态 - * @param string $out_request_no 业务申请编号 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function ecommerceApplymentsByRequestNo($out_request_no) - { - $pathinfo = "/v3/ecommerce/applyments/out-request-no/{$out_request_no}"; - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 按申请单 ID 查询进件状态 - * @param string $applyment_id 微信支付申请单号 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function ecommerceApplymentsByApplymentId($applyment_id) - { - $pathinfo = "/v3/ecommerce/applyments/{$applyment_id}"; - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 修改结算账户 - * @param string $sub_mchid 特约/二级商户号 - * @param array $data 账户参数(敏感字段需 RSA) - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function modifySettlement($sub_mchid, $data) - { - $pathinfo = "/v3/apply4sub/sub_merchants/{$sub_mchid}/modify-settlement"; - return $this->doRequest('POST', $pathinfo, json_encode($data, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 查询结算账户修改状态 - * @param string $sub_mchid 特约/二级商户号 - * @param string $application_no 修改结算账户申请单号 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function modifySettlementResult($sub_mchid, $application_no) - { - $pathinfo = "/v3/apply4sub/sub_merchants/{$sub_mchid}/application/{$application_no}"; - return $this->doRequest('GET', $pathinfo, '', true); - } - - - /** - * 查询结算账户 - * @param string $sub_mchid 特约/二级商户号 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function getSettlement($sub_mchid) - { - $pathinfo = "/v3/apply4sub/sub_merchants/{$sub_mchid}/settlement"; - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 上传进件所需图片/文件 - * @param string $filename 本地文件路径 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function mediaUpload($filename) - { - return $this->doUpload('/v3/merchant/media/upload', $filename, true); - } - - /** - * 合作方 APP 下单 - * @param array $data 下单参数(sp_mchid, sub_mchid, description, out_trade_no, notify_url 等) - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function transactionsApp($data) - { - $pathinfo = "/v3/pay/partner/transactions/app"; - return $this->doRequest('POST', $pathinfo, json_encode($data, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 合作方 JSAPI 下单 - * @param array $data 下单参数(含 payer.openid 等) - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function transactionsJsapi($data) - { - $pathinfo = "/v3/pay/partner/transactions/jsapi"; - return $this->doRequest('POST', $pathinfo, json_encode($data, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 合作方 Native 下单 - * @param array $data 下单参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function transactionsNative($data) - { - $pathinfo = "/v3/pay/partner/transactions/native"; - return $this->doRequest('POST', $pathinfo, json_encode($data, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 合作方 H5 下单 - * @param array $data 下单参数(含 scene_info 等) - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function transactionsH5($data) - { - $pathinfo = "/v3/pay/partner/transactions/h5"; - return $this->doRequest('POST', $pathinfo, json_encode($data, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 订单查询(按微信交易号) - * @param string $transaction_id 微信订单号 - * @param string $sub_mchid 二级商户号 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function getTransactionsById($transaction_id, $sub_mchid) - { - $pathinfo = "/v3/pay/partner/transactions/id/{$transaction_id}"; - $pathinfo = $pathinfo . "?sp_mchid={$this->config['mch_id']}&sub_mchid={$sub_mchid}"; - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 订单查询(按商户订单号) - * @param string $out_trade_no 商户订单号 - * @param string $sub_mchid 二级商户号 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function getTransactionsByTradeNo($out_trade_no, $sub_mchid) - { - $pathinfo = "/v3/pay/partner/transactions/out-trade-no/{$out_trade_no}"; - $pathinfo = $pathinfo . "?sp_mchid={$this->config['mch_id']}&sub_mchid={$sub_mchid}"; - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 关闭订单 - * @param string $out_trade_no 商户订单号 - * @param array $data 包含 sub_mchid 等 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function transactionsClose($out_trade_no, $data) - { - $pathinfo = "/v3/pay/partner/transactions/out-trade-no/{$out_trade_no}/close"; - return $this->doRequest('POST', $pathinfo, json_encode($data, JSON_UNESCAPED_UNICODE), true); - - } - - /** - * 合单 JSAPI 下单 - * @param array $data 合单参数(combine_out_trade_no, sub_orders 等) - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function combineTransactionsJsapi($data) - { - $pathinfo = "/v3/combine-transactions/jsapi"; - return $this->doRequest('POST', $pathinfo, json_encode($data, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 合单 APP 下单 - * @param array $data 合单参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function combineTransactionsApp($data) - { - $pathinfo = "/v3/combine-transactions/app"; - return $this->doRequest('POST', $pathinfo, json_encode($data, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 合单 H5 下单 - * @param array $data 合单参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function combineTransactionsH5($data) - { - $pathinfo = "/v3/combine-transactions/h5"; - return $this->doRequest('POST', $pathinfo, json_encode($data, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 合单 Native 下单 - * @param array $data 合单参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function combineTransactionsNative($data) - { - $pathinfo = "/v3/combine-transactions/native"; - return $this->doRequest('POST', $pathinfo, json_encode($data, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 合单查询 - * @param string $combine_out_trade_no 合单商户订单号 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function getCombineTransactionsByTradeNo($combine_out_trade_no) - { - $pathinfo = "/v3/combine-transactions/out-trade-no/{$combine_out_trade_no}"; - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 合单关单 - * @param string $combine_out_trade_no 合单商户订单号 - * @param array $data 包含 sub_orders 等 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function combineTransactionsClose($combine_out_trade_no, $data) - { - $pathinfo = "/v3/combine-transactions/out-trade-no/{$combine_out_trade_no}/close"; - return $this->doRequest('POST', $pathinfo, json_encode($data, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 电商平台发起分账 - * @param array $data 分账参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function profitsharingOrders($data) - { - $pathinfo = "/v3/ecommerce/profitsharing/orders"; - return $this->doRequest('POST', $pathinfo, json_encode($data, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 查询分账结果 - * @param array $param 查询参数(sub_mchid, transaction_id, out_order_no 等) - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function queryProfitsharingOrders($param = []) - { - $pathinfo = "/v3/ecommerce/profitsharing/orders?" . http_build_query($param); - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 请求分账回退 - * @param array $data 回退参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function profitsharingReturnOrders($data) - { - $pathinfo = "/v3/ecommerce/profitsharing/returnorders"; - return $this->doRequest('POST', $pathinfo, json_encode($data, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 查询分账回退结果 - * @param array $param 查询参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function queryProfitsharingReturnOrders($param = []) - { - $pathinfo = "/v3/ecommerce/profitsharing/returnorders?" . http_build_query($param); - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 完结分账 - * @param array $data 完结参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function profitsharingFinishOrder($data) - { - $pathinfo = "/v3/ecommerce/profitsharing/finish-order"; - return $this->doRequest('POST', $pathinfo, json_encode($data, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 查询订单待分金额 - * @param string $transaction_id 微信订单号 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function profitsharingReturnOrdersAmounts($transaction_id) - { - $pathinfo = "/v3/ecommerce/profitsharing/orders/{$transaction_id}/amounts"; - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 添加分账接收方 - * @param array $data 接收方参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function profitsharingReceiversAdd($data) - { - $pathinfo = "/v3/ecommerce/profitsharing/receivers/add"; - return $this->doRequest('POST', $pathinfo, json_encode($data, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 删除分账接收方 - * @param array $data 接收方参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function profitsharingReceiversDelete($data) - { - $pathinfo = "/v3/ecommerce/profitsharing/receivers/delete"; - return $this->doRequest('POST', $pathinfo, json_encode($data, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 请求补差 - * @param array $data 补差参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function subsidiesCreate($data) - { - $pathinfo = "/v3/ecommerce/subsidies/create"; - return $this->doRequest('POST', $pathinfo, json_encode($data, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 补差回退 - * @param array $data 回退参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function subsidiesReturn($data) - { - $pathinfo = "/v3/ecommerce/subsidies/return"; - return $this->doRequest('POST', $pathinfo, json_encode($data, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 取消补差 - * @param array $data 取消参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function subsidiesCancel($data) - { - $pathinfo = "/v3/ecommerce/subsidies/cancel"; - return $this->doRequest('POST', $pathinfo, json_encode($data, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 申请退款 - * @param array $data 退款参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function refundsApply($data) - { - $pathinfo = "/v3/ecommerce/refunds/apply"; - return $this->doRequest('POST', $pathinfo, json_encode($data, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 查询单笔退款(按微信退款号) - * @param string $refund_id 微信退款号 - * @param string $sub_mchid 二级商户号 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function queryRefundsById($refund_id, $sub_mchid) - { - $pathinfo = "/v3/ecommerce/refunds/id/{$refund_id}?sub_mchid={$$sub_mchid}"; - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 查询单笔退款(按商户退款号) - * @param string $out_refund_no 商户退款单号 - * @param string $sub_mchid 二级商户号 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function queryRefundsByNo($out_refund_no, $sub_mchid) - { - $pathinfo = "/v3/ecommerce/refunds/out-refund-no/{$out_refund_no}?sub_mchid={$sub_mchid}"; - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 查询垫付回补结果 - * @param string $refund_id 微信退款号 - * @param string $sub_mchid 二级商户号 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function queryRefundsReturnAdvance($refund_id, $sub_mchid) - { - $pathinfo = "/v3/ecommerce/refunds/{$refund_id}/return-advance?sub_mchid={$sub_mchid}"; - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 垫付退款回补 - * @param string $refund_id 微信退款号 - * @param array $data 回补参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function refundsReturnAdvance($refund_id, $data) - { - $pathinfo = "/v3/ecommerce/refunds/{$refund_id}/return-advance"; - return $this->doRequest('POST', $pathinfo, json_encode($data, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 查询二级商户账户实时余额API(余额查询) - * @param string $sub_mchid - * @param string $account_type 二级商户账户类型 BASIC: 基本账户 FEES: 手续费账户 OPERATION: 运营账户 DEPOSIT: 保证金账户 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function fundBalance($sub_mchid, $account_type = 'BASIC') - { - $pathinfo = "/v3/ecommerce/fund/balance/{$sub_mchid}?account_type={$account_type}"; - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 查询二级商户账户日终余额API(余额查询) - * @param string $sub_mchid - * @param array $query - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function fundEnddayBalance($sub_mchid, $query) - { - $pathinfo = "/v3/ecommerce/fund/enddaybalance/{$sub_mchid}?" . http_build_query($query); - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 查询收付通平台账户实时余额API(余额查询) - * @param string $account_type - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function merchantFundBalance($account_type) - { - $pathinfo = "/v3/merchant/fund/balance/{$account_type}"; - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 查询收付通平台账户日终余额API(余额查询) - * @param string $account_type - * @param array $query - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function merchantEnddayBalance($account_type, $query) - { - $pathinfo = "/v3/merchant/fund/enddaybalance/{$account_type}?" . http_build_query($query); - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 二级商户预约提现(商户提现) - * @param array $data POST请求参数 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function fundWithdraw($data) - { - $pathinfo = "/v3/ecommerce/fund/withdraw"; - return $this->doRequest('POST', $pathinfo, json_encode($data, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 二级商户查询预约提现状态(根据商户预约提现单号查询)(商户提现) - * @param string $out_request_no - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function queryFundWithdrawByNo($out_request_no) - { - $pathinfo = "/v3/ecommerce/fund/withdraw/out-request-no/{$out_request_no}"; - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 二级商户查询预约提现状态(根据微信支付预约提现单号查询)(商户提现) - * @param string $withdraw_id - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function queryFundWithdrawById($withdraw_id) - { - $pathinfo = "/v3/ecommerce/fund/withdraw/{$withdraw_id}"; - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 收付通平台预约提现(商户提现) - * @param array $data - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function merchantFundWithdraw($data) - { - $pathinfo = "/v3/merchant/fund/withdraw"; - return $this->doRequest('POST', $pathinfo, json_encode($data, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 收付通平台查询预约提现状态(根据商户预约提现单号查询)(商户提现) - * @param string $out_request_no - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function queryMerchantFundWithdrawByNo($out_request_no) - { - $pathinfo = "/v3/merchant/fund/withdraw/out-request-no/{$out_request_no}"; - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 收付通平台查询预约提现状态(根据微信支付预约提现单号查询)(商户提现) - * @param string $withdraw_id - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function queryMerchantFundWithdrawById($withdraw_id) - { - $pathinfo = "/v3/merchant/fund/withdraw/withdraw-id/{$withdraw_id}"; - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 按日下载提现异常文件(商户提现) - * @param $bill_type - * @param array $param - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function fundWithdrawBill($bill_type, $param = []) - { - $pathinfo = "/v3/merchant/fund/withdraw/bill-type/{$bill_type}?" . http_build_query($param); - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 申请交易账单(下载账单) - * @param $bill_type - * @param array $param - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function tradeBill($bill_type, $param = []) - { - $pathinfo = "/v3/bill/tradebill?" . http_build_query($param); - return $this->doRequest('GET', $pathinfo, '', true); - } - - - /** - * 申请资金账单(下载账单) - * @param $bill_type - * @param array $param - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function fundFlowBill($bill_type, $param = []) - { - $pathinfo = "/v3/bill/fundflowbill?" . http_build_query($param); - return $this->doRequest('GET', $pathinfo, '', true); - } - - - /** - * 申请分账账单(下载账单) - * @param string $bill_type - * @param array $param - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function profitsharingBill($bill_type, $param = []) - { - $pathinfo = "/v3/profitsharing/bills?" . http_build_query($param); - return $this->doRequest('GET', $pathinfo, '', true); - } - - - /** - * 申请二级商户资金账单(下载账单) - * @param string $bill_type - * @param array $param - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function subFundFlowBill($bill_type, $param = []) - { - $pathinfo = "/v3/ecommerce/bill/fundflowbill?" . http_build_query($param); - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 查询支持个人业务的银行列表 - * @param int $offset - * @param int $limit - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function personalBanking($offset, $limit) - { - $pathinfo = "/v3/capital/capitallhh/banks/personal-banking?" . http_build_query(['offset' => $offset, 'limit' => $limit]); - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 查询省份列表 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function provinces() - { - $pathinfo = "/v3/capital/capitallhh/areas/provinces"; - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 查询城市列表 - * @param int $code - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function cities($code) - { - $pathinfo = "/v3/capital/capitallhh/areas/provinces/{$code}/cities"; - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 查询支行列表 - * @param int $code - * @param $city - * @param $offset - * @param $limit - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function branches($code, $city, $offset, $limit) - { - $pathinfo = "/v3/capital/capitallhh/banks/{$code}/branches?" . http_build_query(['city_code' => $city, 'offset' => $offset, 'limit' => $limit]); - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 查询支持对公业务的银行列表 - * @param int $offset - * @param int $limit - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function corporateBanking($offset, $limit) - { - $pathinfo = "/v3/capital/capitallhh/banks/corporate-banking?" . http_build_query(['offset' => $offset, 'limit' => $limit]); - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 通过支付预订单ID获取支付参数 - * @param string $prepay_id 支付预订单ID - * @param string $type 类型 - * @return array - */ - public function getJsApiParameters($prepay_id, $type = 'jsapi') - { - // 支付参数签名 - $time = strval(time()); - $appid = $this->config['appid']; - $nonceStr = Tools::createNoncestr(); - if ($type === Order::WXPAY_APP) { - $sign = $this->signBuild(join("\n", [$appid, $time, $nonceStr, $prepay_id, ''])); - return ['partnerId' => $this->config['mch_id'], 'prepayId' => $prepay_id, 'package' => 'Sign=WXPay', 'nonceStr' => $nonceStr, 'timeStamp' => $time, 'sign' => $sign]; - } elseif ($type === Order::WXPAY_JSAPI) { - $sign = $this->signBuild(join("\n", [$appid, $time, $nonceStr, "prepay_id={$prepay_id}", ''])); - return ['appId' => $appid, 'timestamp' => $time, 'timeStamp' => $time, 'nonceStr' => $nonceStr, 'package' => "prepay_id={$prepay_id}", 'signType' => 'RSA', 'paySign' => $sign]; - } else { - return []; - } - } -} \ No newline at end of file diff --git a/WePayV3/Order.php b/WePayV3/Order.php deleted file mode 100644 index 2b18d25..0000000 --- a/WePayV3/Order.php +++ /dev/null @@ -1,208 +0,0 @@ - '/v3/pay/transactions/h5', - 'app' => '/v3/pay/transactions/app', - 'jsapi' => '/v3/pay/transactions/jsapi', - 'native' => '/v3/pay/transactions/native', - ]; - if (empty($types[$type])) { - throw new InvalidArgumentException("Payment {$type} not defined."); - } else { - // 自动填充 mchid 和 appid(如果未提供) - if (empty($data['mchid']) && !empty($this->config['mch_id'])) { - $data['mchid'] = $this->config['mch_id']; - } - if (empty($data['appid']) && !empty($this->config['appid'])) { - $data['appid'] = $this->config['appid']; - } - // 创建预支付码 - $result = $this->doRequest('POST', $types[$type], json_encode($data, JSON_UNESCAPED_UNICODE), true); - if (empty($result['h5_url']) && empty($result['code_url']) && empty($result['prepay_id'])) { - $message = isset($result['code']) ? "[ {$result['code']} ] " : ''; - $message .= isset($result['message']) ? $result['message'] : json_encode($result, JSON_UNESCAPED_UNICODE); - throw new InvalidResponseException($message); - } - // 支付参数签名 - $time = strval(time()); - $appid = $this->config['appid']; - $nonceStr = Tools::createNoncestr(); - if ($type === self::WXPAY_APP) { - $sign = $this->signBuild(join("\n", [$appid, $time, $nonceStr, $result['prepay_id'], ''])); - return ['appId' => $appid, 'partnerId' => $this->config['mch_id'], 'prepayId' => $result['prepay_id'], 'package' => 'Sign=WXPay', 'nonceStr' => $nonceStr, 'timeStamp' => $time, 'sign' => $sign]; - } elseif ($type === self::WXPAY_JSAPI) { - $sign = $this->signBuild(join("\n", [$appid, $time, $nonceStr, "prepay_id={$result['prepay_id']}", ''])); - return ['appId' => $appid, 'timestamp' => $time, 'timeStamp' => $time, 'nonceStr' => $nonceStr, 'package' => "prepay_id={$result['prepay_id']}", 'signType' => 'RSA', 'paySign' => $sign]; - } else { - return $result; - } - } - } - - /** - * 支付订单查询 - * @param string $tradeNo 商户订单号 out_trade_no - * @return array 订单详情 - * @throws \WeChat\Exceptions\InvalidResponseException - * @document https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_2.shtml - */ - public function query($tradeNo) - { - $pathinfo = "/v3/pay/transactions/out-trade-no/{$tradeNo}"; - return $this->doRequest('GET', "{$pathinfo}?mchid={$this->config['mch_id']}", '', true); - } - - /** - * 关闭支付订单 - * @param string $tradeNo 商户订单号 out_trade_no - * @return array 关闭结果 - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function close($tradeNo) - { - $data = ['mchid' => $this->config['mch_id']]; - $path = "/v3/pay/transactions/out-trade-no/{$tradeNo}/close"; - return $this->doRequest('POST', $path, json_encode($data, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 创建退款订单(V3) - * @param array $data 退款参数(out_trade_no 或 transaction_id,out_refund_no,amount 等) - * @return array 退款结果 - * @throws \WeChat\Exceptions\InvalidResponseException - * @document https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_9.shtml - */ - public function createRefund($data) - { - $path = '/v3/refund/domestic/refunds'; - return $this->doRequest('POST', $path, json_encode($data, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 退款订单查询 - * @param string $refundNo 商户退款单号 out_refund_no - * @return array 退款详情 - * @throws \WeChat\Exceptions\InvalidResponseException - * @document https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_10.shtml - */ - public function queryRefund($refundNo) - { - $path = "/v3/refund/domestic/refunds/{$refundNo}"; - return $this->doRequest('GET', $path, '', true); - } - - /** - * 获取退款通知 - * @param mixed $data - * @return array - * @throws \WeChat\Exceptions\InvalidDecryptException - * @deprecated 直接使用 Notify 方法 - */ - public function notifyRefund($data = []) - { - return $this->notify($data); - } - - /** - * 支付/退款通知解析(自动解密 resource) - * @param array|null $data 通知原文,为空则从输入流读取 - * @return array 解析后的通知数据,包含 result 字段(明文) - * @throws \WeChat\Exceptions\InvalidDecryptException - */ - public function notify($data = []) - { - if (empty($data)) { - $data = json_decode(Tools::getRawInput(), true); - } - if (isset($data['resource'])) { - $aes = new DecryptAes($this->config['mch_v3_key']); - $data['result'] = $aes->decryptToString( - $data['resource']['associated_data'], - $data['resource']['nonce'], - $data['resource']['ciphertext'] - ); - } - return $data; - } - - /** - * 申请交易账单 - * @param array|string $params 账单参数(bill_date, bill_type 等)或已拼好的查询串 - * @return array 含下载地址的申请结果 - * @throws \WeChat\Exceptions\InvalidResponseException - * @document https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_3_6.shtml - */ - public function tradeBill($params) - { - $path = '/v3/bill/tradebill?' . is_array($params) ? http_build_query($params) : $params; - return $this->doRequest('GET', $path, '', true); - } - - /** - * 申请资金账单 - * @param array|string $params 账单参数(bill_date, account_type 等)或已拼好的查询串 - * @return array 含下载地址的申请结果 - * @throws \WeChat\Exceptions\InvalidResponseException - * @document https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_3_7.shtml - */ - public function fundflowBill($params) - { - $path = '/v3/bill/fundflowbill?' . is_array($params) ? http_build_query($params) : $params; - return $this->doRequest('GET', $path, '', true); - } - - /** - * 下载账单文件 - * @param string $fileurl 申请账单返回的 download_url - * @return string 二进制内容(gzip/CSV/Excel) - * @throws \WeChat\Exceptions\InvalidResponseException - * @document https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_6_1.shtml - */ - public function downloadBill($fileurl) - { - return $this->doRequest('GET', $fileurl, '', false, false); - } -} diff --git a/WePayV3/ProfitSharing.php b/WePayV3/ProfitSharing.php deleted file mode 100644 index 3fc63b9..0000000 --- a/WePayV3/ProfitSharing.php +++ /dev/null @@ -1,114 +0,0 @@ -config['appid']; - return $this->doRequest('POST', '/v3/profitsharing/orders', json_encode($options, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 查询分账结果 - * @param string $outOrderNo 商户分账单号 - * @param string $transactionId 微信订单号 - * @return array 分账状态 - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function query($outOrderNo, $transactionId) - { - $pathinfo = "/v3/profitsharing/orders/{$outOrderNo}?&transaction_id={$transactionId}"; - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 解冻剩余资金 - * @param array $options 解冻参数(transaction_id, out_order_no, description) - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function unfreeze(array $options) - { - return $this->doRequest('POST', '/v3/profitsharing/orders/unfreeze', json_encode($options, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 查询剩余待分金额 - * @param string $transactionId 微信订单号 - * @return array 待分金额 - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function amounts($transactionId) - { - $pathinfo = "/v3/profitsharing/transactions/{$transactionId}/amounts"; - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 添加分账接收方 - * @param array $options 接收方信息(type, account, name 等,name 需 RSA) - * @return array - * @throws \WeChat\Exceptions\InvalidDecryptException - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function addReceiver(array $options) - { - $options['appid'] = $this->config['appid']; - if (isset($options['name'])) { - $options['name'] = $this->rsaEncode($options['name']); - } - return $this->doRequest('POST', "/v3/profitsharing/receivers/add", json_encode($options, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 删除分账接收方 - * @param array $options 接收方信息(type, account) - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function deleteReceiver(array $options) - { - $options['appid'] = $this->config['appid']; - return $this->doRequest('POST', "/v3/profitsharing/receivers/delete", json_encode($options, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 请求分账回退 - * @param array $options 回退参数(out_return_no, out_order_no, return_account 等) - * @return array 回退结果 - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function backspace(array $options) - { - $options['appid'] = $this->config['appid']; - return $this->doRequest('POST', "/v3/profitsharing/return-orders", json_encode($options, JSON_UNESCAPED_UNICODE), true); - } -} diff --git a/WePayV3/Refund.php b/WePayV3/Refund.php deleted file mode 100644 index 9795039..0000000 --- a/WePayV3/Refund.php +++ /dev/null @@ -1,82 +0,0 @@ -config)->createRefund($data); - // return $this->doRequest('POST', '/v3/ecommerce/refunds/apply', json_encode($data, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 退款订单查询(兼容入口) - * @param string $refundNo 商户退款单号 out_refund_no - * @return array 退款详情 - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function query($refundNo) - { - return Order::instance($this->config)->queryRefund($refundNo); - // $pathinfo = "/v3/ecommerce/refunds/out-refund-no/{$refundNo}"; - // return $this->doRequest('GET', "{$pathinfo}?sub_mchid={$this->config['mch_id']}", '', true); - } - - /** - * 获取退款通知(兼容入口,转调 Order::notify) - * @param mixed $xml 通知原文 - * @return array 解密后的通知数据 - * @throws \WeChat\Exceptions\InvalidDecryptException - * @throws \WeChat\Exceptions\InvalidResponseException - * @throws \WeChat\Exceptions\LocalCacheException - */ - public function notify($xml = []) - { - return Order::instance($this->config)->notify($xml); -// $data = Tools::xml2arr(empty($xml) ? Tools::getRawInput() : $xml); -// if (!isset($data['return_code']) || $data['return_code'] !== 'SUCCESS') { -// throw new InvalidResponseException('获取退款通知XML失败!'); -// } -// try { -// $key = md5($this->config['mch_v3_key']); -// $decrypt = base64_decode($data['req_info']); -// $response = openssl_decrypt($decrypt, 'aes-256-ecb', $key, OPENSSL_RAW_DATA); -// $data['result'] = Tools::xml2arr($response); -// return $data; -// } catch (\Exception $exception) { -// throw new InvalidDecryptException($exception->getMessage(), $exception->getCode()); -// } - } -} \ No newline at end of file diff --git a/WePayV3/Transfers.php b/WePayV3/Transfers.php deleted file mode 100644 index 43cfe9b..0000000 --- a/WePayV3/Transfers.php +++ /dev/null @@ -1,137 +0,0 @@ -config['appid']; - } - if (!empty($body['user_name'])) { - $body['user_name'] = $this->rsaEncode($body['user_name']); - } - return $this->doRequest('POST', '/v3/fund-app/mch-transfer/transfer-bills', json_encode($body, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 查询转账结果 - * @param string $out_bill_no 商户转账单号 - * @return array|string 转账状态 - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function billsQuery($out_bill_no) - { - return $this->doRequest('GET', "/v3/fund-app/mch-transfer/transfer-bills/out-bill-no/{$out_bill_no}", '', true); - } - - /** - * 发起商家批量转账 - * @param array $body 批量参数(out_batch_no, transfer_detail_list 等,姓名需 RSA) - * @return array - * @throws \WeChat\Exceptions\InvalidDecryptException - * @throws \WeChat\Exceptions\InvalidResponseException - * @link https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_3_1.shtml - */ - public function batchs($body) - { - if (empty($body['appid'])) { - $body['appid'] = $this->config['appid']; - } - if (isset($body['transfer_detail_list']) && is_array($body['transfer_detail_list'])) { - foreach ($body['transfer_detail_list'] as &$item) if (isset($item['user_name'])) { - $item['user_name'] = $this->rsaEncode($item['user_name']); - } - if (empty($body['total_num'])) { - $body['total_num'] = count($body['transfer_detail_list']); - } - if (empty($body['total_amount'])) { - $body['total_amount'] = array_sum(array_column($body['transfer_detail_list'], 'transfer_amount')); - } - } - return $this->doRequest('POST', '/v3/transfer/batches', json_encode($body, JSON_UNESCAPED_UNICODE), true); - } - - /** - * 查询批量转账批次 - * @param string $batchId 微信批次单号(二选一) - * @param string $outBatchNo 商户批次单号(二选一) - * @param bool $needQueryDetail 是否拉取明细 - * @param int $offset 明细起始位置 - * @param int $limit 最大明细条数 - * @param string $detailStatus 明细状态 ALL|SUCCESS|FAIL - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function query($batchId = '', $outBatchNo = '', $needQueryDetail = true, $offset = 0, $limit = 20, $detailStatus = 'ALL') - { - if (empty($batchId)) { - $pathinfo = "/v3/transfer/batches/out-batch-no/{$outBatchNo}"; - } else { - $pathinfo = "/v3/transfer/batches/batch-id/{$batchId}"; - } - $params = http_build_query([ - 'limit' => $limit, - 'offset' => $offset, - 'detail_status' => $detailStatus, - 'need_query_detail' => $needQueryDetail ? 'true' : 'false', - ]); - return $this->doRequest('GET', "{$pathinfo}?{$params}", '', true); - } - - /** - * 通过微信明细单号查询明细 - * @param string $batchId 微信批次单号 - * @param string $detailId 微信明细单号 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function detailBatchId($batchId, $detailId) - { - $pathinfo = "/v3/transfer/batches/batch-id/{$batchId}/details/detail-id/{$detailId}"; - return $this->doRequest('GET', $pathinfo, '', true); - } - - /** - * 通过商家明细单号查询明细 - * @param string $outBatchNo 商户批次单号 - * @param string $outDetailNo 商户明细单号 - * @return array - * @throws \WeChat\Exceptions\InvalidResponseException - */ - public function detailOutBatchNo($outBatchNo, $outDetailNo) - { - $pathinfo = "/v3/transfer/batches/out-batch-no/{$outBatchNo}/details/out-detail-no/{$outDetailNo}"; - return $this->doRequest('GET', $pathinfo, '', true); - } -} diff --git a/_test/alipay-app.php b/_test/alipay-app.php deleted file mode 100644 index 155d3b4..0000000 --- a/_test/alipay-app.php +++ /dev/null @@ -1,46 +0,0 @@ -apply([ - 'out_trade_no' => strval(time()), // 商户订单号 - 'total_amount' => '1', // 支付金额 - 'subject' => '支付宝订单标题', // 支付订单描述 - ]); - echo $result . PHP_EOL . '

    ' . PHP_EOL; - - // 请求关闭订单 - $result = $pay->close([ - 'out_trade_no' => strval(time()) - ]); - echo PHP_EOL . PHP_EOL . $result; -} catch (\Exception $e) { - echo $e->getMessage(); -} - - diff --git a/_test/alipay-bill.php b/_test/alipay-bill.php deleted file mode 100644 index 3a8ffca..0000000 --- a/_test/alipay-bill.php +++ /dev/null @@ -1,47 +0,0 @@ -apply([ - 'bill_date' => date('Y-m-d', strtotime('-1 month')), // 账单时间(日账单yyyy-MM-dd,月账单 yyyy-MM) - 'bill_type' => 'trade', - ]); - echo '
    ';
    -    var_export($result);
    -} catch (Exception $e) {
    -    echo $e->getMessage();
    -}
    \ No newline at end of file
    diff --git a/_test/alipay-callapi-test.php b/_test/alipay-callapi-test.php
    deleted file mode 100644
    index 9ba45bd..0000000
    --- a/_test/alipay-callapi-test.php
    +++ /dev/null
    @@ -1,163 +0,0 @@
    -=== 支付宝通用接口测试 ===";
    -
    -    // ============================================
    -    // 测试1: GET请求 - 查询订单
    -    // ============================================
    -    echo "

    测试1: GET请求 - 查询订单

    "; - try { - $result = $alipay->callApi( - 'alipay.trade.query', // API方法名(必填,第一参数) - [ - 'out_trade_no' => time() - 3600, // 使用一个可能存在的订单号 - ], - 'GET', - false // 不验证响应签名 - ); - echo '
    ';
    -        var_export($result);
    -        echo '
    '; - } catch (Exception $e) { - echo "错误: " . $e->getMessage() . PHP_EOL; - } - - // ============================================ - // 测试2: GET请求 - 查询订单(验证签名) - // ============================================ - echo "

    测试2: GET请求 - 查询订单(验证签名)

    "; - try { - $result = $alipay->callApi( - 'alipay.trade.query', // API方法名 - [ - 'out_trade_no' => time() - 3600, - ], - 'GET', - true // 验证响应签名 - ); - echo '
    ';
    -        var_export($result);
    -        echo '
    '; - } catch (Exception $e) { - echo "错误: " . $e->getMessage() . PHP_EOL; - } - - // ============================================ - // 测试3: POST请求 - 订单退款 - // ============================================ - echo "

    测试3: POST请求 - 订单退款

    "; - echo "

    注意:此测试需要有效的订单号

    "; - try { - $result = $alipay->callApi( - 'alipay.trade.refund', // API方法名(第一参数) - [ - 'out_trade_no' => time() - 3600, - 'refund_amount' => '0.01', - ], - 'POST', - false // 不验证响应签名 - ); - echo '
    ';
    -        var_export($result);
    -        echo '
    '; - } catch (Exception $e) { - echo "错误: " . $e->getMessage() . PHP_EOL; - echo "

    这是正常的,因为退款需要有效的订单号

    "; - } - - // ============================================ - // 测试4: GET请求 - 退款查询 - // ============================================ - echo "

    测试4: GET请求 - 退款查询

    "; - try { - $result = $alipay->callApi( - 'alipay.trade.fastpay.refund.query', // API方法名 - [ - 'out_trade_no' => time() - 3600, - 'out_request_no' => time() - 3600, - ], - 'GET', - false // 不验证响应签名 - ); - echo '
    ';
    -        var_export($result);
    -        echo '
    '; - } catch (Exception $e) { - echo "错误: " . $e->getMessage() . PHP_EOL; - } - - // ============================================ - // 测试5: POST请求 - 关闭订单 - // ============================================ - echo "

    测试5: POST请求 - 关闭订单

    "; - try { - $result = $alipay->callApi( - 'alipay.trade.close', // API方法名 - [ - 'out_trade_no' => time() - 3600, - ], - 'POST', - false // 不验证响应签名 - ); - echo '
    ';
    -        var_export($result);
    -        echo '
    '; - } catch (Exception $e) { - echo "错误: " . $e->getMessage() . PHP_EOL; - } - - // ============================================ - // 测试6: PUT请求 - 更新资源(示例) - // ============================================ - echo "

    测试6: PUT请求 - 更新资源(示例)

    "; - echo "

    注意:此示例仅展示PUT请求用法,实际API可能不支持

    "; - try { - // PUT请求示例(如果API支持) - // $result = $alipay->callApi( - // 'alipay.some.method', // API方法名 - // ['key' => 'value'], - // 'PUT', - // false - // ); - echo '
    PUT请求用法示例(已注释)
    '; - } catch (Exception $e) { - echo "错误: " . $e->getMessage() . PHP_EOL; - } - - echo "

    === 所有测试完成 ===

    "; -} catch (Exception $e) { - - // 出错啦,处理下吧 - echo "

    错误: " . $e->getMessage() . "

    "; - echo "
    " . $e->getTraceAsString() . "
    "; -} diff --git a/_test/alipay-notify.php b/_test/alipay-notify.php deleted file mode 100644 index 26b84f5..0000000 --- a/_test/alipay-notify.php +++ /dev/null @@ -1,41 +0,0 @@ -notify(); - if (in_array($data['trade_status'], ['TRADE_SUCCESS', 'TRADE_FINISHED'])) { - // @todo 更新订单状态,支付完成 - file_put_contents('notify.txt', "收到来自支付宝的异步通知\r\n", FILE_APPEND); - file_put_contents('notify.txt', '订单号:' . $data['out_trade_no'] . "\r\n", FILE_APPEND); - file_put_contents('notify.txt', '订单金额:' . $data['total_amount'] . "\r\n\r\n", FILE_APPEND); - } else { - file_put_contents('notify.txt', "收到异步通知\r\n", FILE_APPEND); - } -} catch (\Exception $e) { - // 异常处理 - echo $e->getMessage(); -} diff --git a/_test/alipay-pos.php b/_test/alipay-pos.php deleted file mode 100644 index 0849795..0000000 --- a/_test/alipay-pos.php +++ /dev/null @@ -1,43 +0,0 @@ -apply([ - 'out_trade_no' => '4312412343', // 订单号 - 'total_amount' => '13', // 订单金额,单位:元 - 'subject' => '订单商品标题', // 订单商品标题 - 'auth_code' => '123456', // 授权码 - ]); - - echo '
    ';
    -    var_export($result);
    -} catch (Exception $e) {
    -    echo $e->getMessage();
    -}
    -
    -
    diff --git a/_test/alipay-refund.php b/_test/alipay-refund.php
    deleted file mode 100644
    index d880bab..0000000
    --- a/_test/alipay-refund.php
    +++ /dev/null
    @@ -1,41 +0,0 @@
    -refund($out_trade_no, $refund_fee);
    -
    -    echo '
    ';
    -    var_export($result);
    -} catch (Exception $e) {
    -    echo $e->getMessage();
    -}
    \ No newline at end of file
    diff --git a/_test/alipay-scan.php b/_test/alipay-scan.php
    deleted file mode 100644
    index 649fa91..0000000
    --- a/_test/alipay-scan.php
    +++ /dev/null
    @@ -1,42 +0,0 @@
    -apply([
    -        'out_trade_no' => '14321412', // 订单号
    -        'total_amount' => '13', // 订单金额,单位:元
    -        'subject'      => '订单商品标题', // 订单商品标题
    -    ]);
    -
    -    echo '
    ';
    -    var_export($result);
    -} catch (Exception $e) {
    -    echo $e->getMessage();
    -}
    -
    -
    diff --git a/_test/alipay-transfer-account.php b/_test/alipay-transfer-account.php
    deleted file mode 100644
    index ae5a86f..0000000
    --- a/_test/alipay-transfer-account.php
    +++ /dev/null
    @@ -1,39 +0,0 @@
    -queryAccount([
    -        'alipay_user_id'     => $config['appid'], // 订单号
    -        'account_scene_code' => 'SCENE_000_000_000',
    -    ]);
    -    echo '
    ';
    -    var_export($result);
    -} catch (Exception $e) {
    -    echo $e->getMessage();
    -}
    -
    diff --git a/_test/alipay-transfer-create.php b/_test/alipay-transfer-create.php
    deleted file mode 100644
    index 9bf4faf..0000000
    --- a/_test/alipay-transfer-create.php
    +++ /dev/null
    @@ -1,46 +0,0 @@
    -create([
    -        'out_biz_no'   => time(), // 订单号
    -        'trans_amount' => '10', // 转账金额
    -        'product_code' => 'TRANS_ACCOUNT_NO_PWD',
    -        'biz_scene'    => 'DIRECT_TRANSFER',
    -        'payee_info'   => [
    -            'identity'      => 'zoujingli@qq.com',
    -            'identity_type' => 'ALIPAY_LOGON_ID',
    -            'name'          => '邹景立',
    -        ],
    -    ]);
    -    echo '
    ';
    -    var_export($result);
    -} catch (Exception $e) {
    -    echo $e->getMessage();
    -}
    -
    diff --git a/_test/alipay-transfer-query.php b/_test/alipay-transfer-query.php
    deleted file mode 100644
    index 434e0cc..0000000
    --- a/_test/alipay-transfer-query.php
    +++ /dev/null
    @@ -1,40 +0,0 @@
    -queryResult([
    -        'out_biz_no'   => '201808080001', // 订单号
    -        'product_code' => 'TRANS_ACCOUNT_NO_PWD',
    -        'biz_scene'    => 'DIRECT_TRANSFER',
    -    ]);
    -    echo '
    ';
    -    var_export($result);
    -} catch (Exception $e) {
    -    echo $e->getMessage();
    -}
    -
    diff --git a/_test/alipay-transfer.php b/_test/alipay-transfer.php
    deleted file mode 100644
    index 48feac7..0000000
    --- a/_test/alipay-transfer.php
    +++ /dev/null
    @@ -1,45 +0,0 @@
    -apply([
    -        'out_biz_no'      => time(), // 订单号
    -        'payee_type'      => 'ALIPAY_LOGONID', // 收款方账户类型(ALIPAY_LOGONID | ALIPAY_USERID)
    -        'payee_account'   => 'yvvfcr3065@sandbox.com', // 收款方账户
    -        'amount'          => '10', // 转账金额
    -        'payer_show_name' => '未寒', // 付款方姓名
    -        'payee_real_name' => 'yvvfcr3065', // 收款方真实姓名
    -        'remark'          => '张三', // 转账备注
    -    ]);
    -
    -    echo '
    ';
    -    var_export($result);
    -} catch (Exception $e) {
    -    echo $e->getMessage();
    -}
    -
    diff --git a/_test/alipay-wap.php b/_test/alipay-wap.php
    deleted file mode 100644
    index bcb232b..0000000
    --- a/_test/alipay-wap.php
    +++ /dev/null
    @@ -1,44 +0,0 @@
    -apply([
    -        'out_trade_no' => time(), // 商户订单号
    -        'total_amount' => '1', // 支付金额
    -        'subject'      => '支付订单描述', // 支付订单描述
    -    ]);
    -
    -    echo $result;
    -} catch (Exception $e) {
    -    echo $e->getMessage();
    -}
    -
    -
    diff --git a/_test/alipay-web.php b/_test/alipay-web.php
    deleted file mode 100644
    index de3cee8..0000000
    --- a/_test/alipay-web.php
    +++ /dev/null
    @@ -1,45 +0,0 @@
    -apply([
    -        'out_trade_no' => time(), // 商户订单号
    -        'total_amount' => '1', // 支付金额
    -        'subject'      => '支付订单描述', // 支付订单描述
    -    ]);
    -
    -    echo $result;
    -} catch (Exception $e) {
    -    echo $e->getMessage();
    -}
    -
    -
    diff --git a/_test/alipay.php b/_test/alipay.php
    deleted file mode 100644
    index 28ec6f1..0000000
    --- a/_test/alipay.php
    +++ /dev/null
    @@ -1,58 +0,0 @@
    - true,
    -    // 签名类型 ( RSA|RSA2 )
    -    'sign_type'        => 'RSA2',
    -    // 应用ID
    -    'appid'            => '2021000122667306',
    -    // 应用私钥内容 ( 需1行填写,特别注意:这里的应用私钥通常由支付宝密钥管理工具生成 )
    -    'private_key'      => 'MIIEowIBAAKCAQEAndH26KVe3Iy+8GxVxDuG9ZolYrqGNm8Jpdi9GrQdM81ad4pPyul2NVO+9C2Kr6a6jK6Qw1gyzcwYxtkUC7xoLZUSPpmSH7sH3sD6r2B7Mf5FsrVSa29lcm1+3UkyFgZjYTkx45lfbLmAFHOzOl0WfGkMW0Sq3N/5OMr074E4EnYtALdE3jVQCDf8bzqN3j/Kwe7f10Aglvxili2BrFM564silqcbiJ8U1zDmTdZvmEkP7ia/YVkmt5w3rh7ZBoaubtcM/rVGYXL2hQPwr/pquNCTu7Eh1RcWfpcnbuw+gOnaNyXmNFmZkeNlegXIifcunt1GK6a1pX090R8eFN3LjQIDAQABAoIBACrLY4OETCvL8n6pMbyLU7ZHfTm/UGN0So5xLh4OlxiT56MgmzBvjAE72zzFGKU2tcEuGM0Pnn8Vh+ZruLbR+QHbOV5GMExwX9r0Q0XJCL7uryGdb2L4iu6zaEJC9dTpGIulgbSwwyJtTqC9Gu2Jjm5f4dzhyt8n0KGozzAevwCqI9RaJSD96gGWLbMlHCyWKGy1OdBP4V/+agPyHAGZ9gqpfKY7y4L0My8gUxhWzQWOwihtFACjV66ULhutUYT2bro3j1k9UekKlX7IiWrssPmmmw2vfUbrKiNugF6zkfyStPt7jGJ0CdzAHWe3pyF72TyO5NU2NGcX8eKgYlY2crUCgYEAzOcg5Zot9X+Ao+fYH/Oq/3eGZd6krzByiXfcjuRco7mGODwmUnzt3PpPT1fPry9TxarTajt+A9LuxqWawfQ9eWAfrTGAbtDJB0LYo6CynDqUoRqBukROuNaLQiUqEreOQqt08o6VVblgVLv8475ij8s4z/6C2NSSjgUJHf0PL38CgYEAxS0bcXGI+WtempZ4Q3QMTUmp/+B3zuw9JzSV1gvbVi7MleI9V62V2IXHPSXL5mRhYOuQWR0MnVOhbo69fkEA8HpdSd1q2JjaeS+OiZ0ditcJISQJbqWtvmF2+XtQcbVwfID69GWGxyBEHHTW8AtAzIPc6T7x2izyzBw0lXDHSvMCgYEAi+Y61ckhG/9EC6TeMWKjG+21u5P6CQshCK7nzkAo6DhhZb/bwnI9zaSxxdCEom3D2rA5zMx1y5KXKNYlBcwGtPpmZk/oCsFOoECJvZ6YlIaCuERq0oyU2yrQxgat5T2iSe7a2El1uKPrG6+GiNCSZu8wCQMSv4zTy1ew0+LWHW0CgYAvX7ESRpcEZjmqprBqdH1oLGS9566hdr0SqF2/ucWPJVteP6dBY6F3Dl1aYbRlvIRxBuf9oS8gtbE5oO4CYZfaL2wujRZYyBDlwPlcMvWgIB4/aish/IiMD1rIgkpHp7JJF6w0ABiryyLSO3hQ4ENHX/85wzfUlawYQkaYCSq45QKBgCdqrv58KD8tDYn4JnaHJNE+5TgKK5cNhYLZLAsYz7x1KfPdkiC7y/hnenn3TWkm4xw8Tw1rJ1ZIJ24iZgTCTO7EEsB7jZegvg4z/4zVbSK2Y4VI1lJ7jlyqmwg0ArimXTNZFoy66h9c9t2smG40YEZCmmmTLEqVlWgyR1MU5iM5',
    -    // 公钥模式,支付宝公钥内容 ( 需1行填写,特别注意:这里不是应用公钥而是支付宝公钥,通常是上传应用公钥换取支付宝公钥,在网页可以复制 )
    -    'public_key'       => '',
    -    // 证书模式,应用公钥证书路径 ( 新版资金类接口转 app_cert_sn,如文件 appCertPublicKey.crt )
    -    'app_cert_path'    => __DIR__ . '/alipay/appPublicCert.crt', // 'app_cert' => '证书内容',
    -    // 证书模式,支付宝根证书路径 ( 新版资金类接口转 alipay_root_cert_sn,如文件 alipayRootCert.crt )
    -    'alipay_root_path' => __DIR__ . '/alipay/alipayRootCert.crt', // 'root_cert' => '证书内容',
    -    // 证书模式,支付宝公钥证书路径 ( 未填写 public_key 时启用此参数,如文件 alipayPublicCert.crt )
    -    'alipay_cert_path' => __DIR__ . '/alipay/alipayPublicCert.crt', // 'public_key' => '证书内容'
    -    // 支付成功通知地址
    -    'notify_url'       => '',
    -    // 网页支付回跳地址
    -    'return_url'       => '',
    -];
    \ No newline at end of file
    diff --git a/_test/alipay/alipayPublicCert.crt b/_test/alipay/alipayPublicCert.crt
    deleted file mode 100644
    index 0d7d612..0000000
    --- a/_test/alipay/alipayPublicCert.crt
    +++ /dev/null
    @@ -1,38 +0,0 @@
    ------BEGIN CERTIFICATE-----
    -MIIDszCCApugAwIBAgIQICMFBicVvB1uC0Lc3VFbITANBgkqhkiG9w0BAQsFADCBkTELMAkGA1UE
    -BhMCQ04xGzAZBgNVBAoMEkFudCBGaW5hbmNpYWwgdGVzdDElMCMGA1UECwwcQ2VydGlmaWNhdGlv
    -biBBdXRob3JpdHkgdGVzdDE+MDwGA1UEAww1QW50IEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1
    -dGhvcml0eSBDbGFzcyAyIFIxIHRlc3QwHhcNMjMwNTA2MDYwMTA3WhcNMjQwNTA1MDYwMTA3WjCB
    -hDELMAkGA1UEBhMCQ04xHzAdBgNVBAoMFndtaWFrdTY3NDlAc2FuZGJveC5jb20xDzANBgNVBAsM
    -BkFsaXBheTFDMEEGA1UEAww65pSv5LuY5a6dKOS4reWbvSnnvZHnu5zmioDmnK/mnInpmZDlhazl
    -j7gtMjA4ODcyMTAwMjg0MzQxNDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALkO9F4+
    -AB7CLI47hSthHsZFFQxnOiggc4R8pbeb24BkyRc6xJVekYtzuJ8cLSsy1spEX4zguTPc+b7eza5k
    -nN1j2pdAfNkzbdEg1Tt4A0b5xbMAfaQtVhwU0aohhLF+i6TTgospMmBwJnN2++Eda6LccrTqS7ff
    -x8I2bhkraLlEO4C6pxUcGCyorPLVvRWOTRC/RzxDURHEaGlBPMxxOpeIzYuYNg77OK+Sqp0zb5nk
    -U3PO2cpSrOCT4UlrJDmqgSZgwHE0e+9MdBuveLSo1ubG5uGvz8Vjld3hBvVywOAnEuoMBxtSrEbv
    -nkz7M0MJBDmQk/D8WSekm2lMGbFDuGUCAwEAAaMSMBAwDgYDVR0PAQH/BAQDAgTwMA0GCSqGSIb3
    -DQEBCwUAA4IBAQC29uyA9W6uQivhMqc3YyGXPvi4LIOThU1ijeOSpovHiRUGVfaO/qIY4eAQ2ivF
    -iKIrUEqcJnWdNN8LZwWdWd6UmInyngq2i+Pf4h3a2MLkV3ZufZNZkJP5GZJxbcjkKlnKuCTKFUd5
    -wJ0zo369E+mPdNGlrPLvNXw4ziUpUb4KXmEn1yOVAkQMsBP43K6QB2QVIODrtp4O+rEs80KHgUQh
    -cmla+PWGyX2nuwHURxUtEeIeJblra+ntyy+bTYfCVVG8jh4BN5bPExDprRa100aZGsqyExO1xtxk
    -4pu0Jag5XRvGyZpmqH27SfQX2oKcsobuDyGCwff/yvYKOZk53JZD
    ------END CERTIFICATE-----
    ------BEGIN CERTIFICATE-----
    -MIIDszCCApugAwIBAgIQIBkIGbgVxq210KxLJ+YA/TANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UE
    -BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxJTAjBgNVBAsMHENlcnRpZmljYXRpb24gQXV0
    -aG9yaXR5IHRlc3QxNjA0BgNVBAMMLUFudCBGaW5hbmNpYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
    -dHkgUjEgdGVzdDAeFw0xOTA4MTkxMTE2MDBaFw0yNDA4MDExMTE2MDBaMIGRMQswCQYDVQQGEwJD
    -TjEbMBkGA1UECgwSQW50IEZpbmFuY2lhbCB0ZXN0MSUwIwYDVQQLDBxDZXJ0aWZpY2F0aW9uIEF1
    -dGhvcml0eSB0ZXN0MT4wPAYDVQQDDDVBbnQgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y
    -aXR5IENsYXNzIDIgUjEgdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMh4FKYO
    -ZyRQHD6eFbPKZeSAnrfjfU7xmS9Yoozuu+iuqZlb6Z0SPLUqqTZAFZejOcmr07ln/pwZxluqplxC
    -5+B48End4nclDMlT5HPrDr3W0frs6Xsa2ZNcyil/iKNB5MbGll8LRAxntsKvZZj6vUTMb705gYgm
    -VUMILwi/ZxKTQqBtkT/kQQ5y6nOZsj7XI5rYdz6qqOROrpvS/d7iypdHOMIM9Iz9DlL1mrCykbBi
    -t25y+gTeXmuisHUwqaRpwtCGK4BayCqxRGbNipe6W73EK9lBrrzNtTr9NaysesT/v+l25JHCL9tG
    -wpNr1oWFzk4IHVOg0ORiQ6SUgxZUTYcCAwEAAaMSMBAwDgYDVR0PAQH/BAQDAgTwMA0GCSqGSIb3
    -DQEBCwUAA4IBAQBWThEoIaQoBX2YeRY/I8gu6TYnFXtyuCljANnXnM38ft+ikhE5mMNgKmJYLHvT
    -yWWWgwHoSAWEuml7EGbE/2AK2h3k0MdfiWLzdmpPCRG/RJHk6UB1pMHPilI+c0MVu16OPpKbg5Vf
    -LTv7dsAB40AzKsvyYw88/Ezi1osTXo6QQwda7uefvudirtb8FcQM9R66cJxl3kt1FXbpYwheIm/p
    -j1mq64swCoIYu4NrsUYtn6CV542DTQMI5QdXkn+PzUUly8F6kDp+KpMNd0avfWNL5+O++z+F5Szy
    -1CPta1D7EQ/eYmMP+mOQ35oifWIoFCpN6qQVBS/Hob1J/UUyg7BW
    ------END CERTIFICATE-----
    diff --git a/_test/alipay/alipayRootCert.crt b/_test/alipay/alipayRootCert.crt
    deleted file mode 100644
    index 76417c5..0000000
    --- a/_test/alipay/alipayRootCert.crt
    +++ /dev/null
    @@ -1,88 +0,0 @@
    ------BEGIN CERTIFICATE-----
    -MIIBszCCAVegAwIBAgIIaeL+wBcKxnswDAYIKoEcz1UBg3UFADAuMQswCQYDVQQG
    -EwJDTjEOMAwGA1UECgwFTlJDQUMxDzANBgNVBAMMBlJPT1RDQTAeFw0xMjA3MTQw
    -MzExNTlaFw00MjA3MDcwMzExNTlaMC4xCzAJBgNVBAYTAkNOMQ4wDAYDVQQKDAVO
    -UkNBQzEPMA0GA1UEAwwGUk9PVENBMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE
    -MPCca6pmgcchsTf2UnBeL9rtp4nw+itk1Kzrmbnqo05lUwkwlWK+4OIrtFdAqnRT
    -V7Q9v1htkv42TsIutzd126NdMFswHwYDVR0jBBgwFoAUTDKxl9kzG8SmBcHG5Yti
    -W/CXdlgwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFEwysZfZ
    -MxvEpgXBxuWLYlvwl3ZYMAwGCCqBHM9VAYN1BQADSAAwRQIgG1bSLeOXp3oB8H7b
    -53W+CKOPl2PknmWEq/lMhtn25HkCIQDaHDgWxWFtnCrBjH16/W3Ezn7/U/Vjo5xI
    -pDoiVhsLwg==
    ------END CERTIFICATE-----
    -
    ------BEGIN CERTIFICATE-----
    -MIIF0zCCA7ugAwIBAgIIH8+hjWpIDREwDQYJKoZIhvcNAQELBQAwejELMAkGA1UE
    -BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmlj
    -YXRpb24gQXV0aG9yaXR5MTEwLwYDVQQDDChBbnQgRmluYW5jaWFsIENlcnRpZmlj
    -YXRpb24gQXV0aG9yaXR5IFIxMB4XDTE4MDMyMTEzNDg0MFoXDTM4MDIyODEzNDg0
    -MFowejELMAkGA1UEBhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNV
    -BAsMF0NlcnRpZmljYXRpb24gQXV0aG9yaXR5MTEwLwYDVQQDDChBbnQgRmluYW5j
    -aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFIxMIICIjANBgkqhkiG9w0BAQEF
    -AAOCAg8AMIICCgKCAgEAtytTRcBNuur5h8xuxnlKJetT65cHGemGi8oD+beHFPTk
    -rUTlFt9Xn7fAVGo6QSsPb9uGLpUFGEdGmbsQ2q9cV4P89qkH04VzIPwT7AywJdt2
    -xAvMs+MgHFJzOYfL1QkdOOVO7NwKxH8IvlQgFabWomWk2Ei9WfUyxFjVO1LVh0Bp
    -dRBeWLMkdudx0tl3+21t1apnReFNQ5nfX29xeSxIhesaMHDZFViO/DXDNW2BcTs6
    -vSWKyJ4YIIIzStumD8K1xMsoaZBMDxg4itjWFaKRgNuPiIn4kjDY3kC66Sl/6yTl
    -YUz8AybbEsICZzssdZh7jcNb1VRfk79lgAprm/Ktl+mgrU1gaMGP1OE25JCbqli1
    -Pbw/BpPynyP9+XulE+2mxFwTYhKAwpDIDKuYsFUXuo8t261pCovI1CXFzAQM2w7H
    -DtA2nOXSW6q0jGDJ5+WauH+K8ZSvA6x4sFo4u0KNCx0ROTBpLif6GTngqo3sj+98
    -SZiMNLFMQoQkjkdN5Q5g9N6CFZPVZ6QpO0JcIc7S1le/g9z5iBKnifrKxy0TQjtG
    -PsDwc8ubPnRm/F82RReCoyNyx63indpgFfhN7+KxUIQ9cOwwTvemmor0A+ZQamRe
    -9LMuiEfEaWUDK+6O0Gl8lO571uI5onYdN1VIgOmwFbe+D8TcuzVjIZ/zvHrAGUcC
    -AwEAAaNdMFswCwYDVR0PBAQDAgEGMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFF90
    -tATATwda6uWx2yKjh0GynOEBMB8GA1UdIwQYMBaAFF90tATATwda6uWx2yKjh0Gy
    -nOEBMA0GCSqGSIb3DQEBCwUAA4ICAQCVYaOtqOLIpsrEikE5lb+UARNSFJg6tpkf
    -tJ2U8QF/DejemEHx5IClQu6ajxjtu0Aie4/3UnIXop8nH/Q57l+Wyt9T7N2WPiNq
    -JSlYKYbJpPF8LXbuKYG3BTFTdOVFIeRe2NUyYh/xs6bXGr4WKTXb3qBmzR02FSy3
    -IODQw5Q6zpXj8prYqFHYsOvGCEc1CwJaSaYwRhTkFedJUxiyhyB5GQwoFfExCVHW
    -05ZFCAVYFldCJvUzfzrWubN6wX0DD2dwultgmldOn/W/n8at52mpPNvIdbZb2F41
    -T0YZeoWnCJrYXjq/32oc1cmifIHqySnyMnavi75DxPCdZsCOpSAT4j4lAQRGsfgI
    -kkLPGQieMfNNkMCKh7qjwdXAVtdqhf0RVtFILH3OyEodlk1HYXqX5iE5wlaKzDop
    -PKwf2Q3BErq1xChYGGVS+dEvyXc/2nIBlt7uLWKp4XFjqekKbaGaLJdjYP5b2s7N
    -1dM0MXQ/f8XoXKBkJNzEiM3hfsU6DOREgMc1DIsFKxfuMwX3EkVQM1If8ghb6x5Y
    -jXayv+NLbidOSzk4vl5QwngO/JYFMkoc6i9LNwEaEtR9PhnrdubxmrtM+RjfBm02
    -77q3dSWFESFQ4QxYWew4pHE0DpWbWy/iMIKQ6UZ5RLvB8GEcgt8ON7BBJeMc+Dyi
    -kT9qhqn+lw==
    ------END CERTIFICATE-----
    -
    ------BEGIN CERTIFICATE-----
    -MIICiDCCAgygAwIBAgIIQX76UsB/30owDAYIKoZIzj0EAwMFADB6MQswCQYDVQQG
    -EwJDTjEWMBQGA1UECgwNQW50IEZpbmFuY2lhbDEgMB4GA1UECwwXQ2VydGlmaWNh
    -dGlvbiBBdXRob3JpdHkxMTAvBgNVBAMMKEFudCBGaW5hbmNpYWwgQ2VydGlmaWNh
    -dGlvbiBBdXRob3JpdHkgRTEwHhcNMTkwNDI4MTYyMDQ0WhcNNDkwNDIwMTYyMDQ0
    -WjB6MQswCQYDVQQGEwJDTjEWMBQGA1UECgwNQW50IEZpbmFuY2lhbDEgMB4GA1UE
    -CwwXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxMTAvBgNVBAMMKEFudCBGaW5hbmNp
    -YWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgRTEwdjAQBgcqhkjOPQIBBgUrgQQA
    -IgNiAASCCRa94QI0vR5Up9Yr9HEupz6hSoyjySYqo7v837KnmjveUIUNiuC9pWAU
    -WP3jwLX3HkzeiNdeg22a0IZPoSUCpasufiLAnfXh6NInLiWBrjLJXDSGaY7vaokt
    -rpZvAdmjXTBbMAsGA1UdDwQEAwIBBjAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBRZ
    -4ZTgDpksHL2qcpkFkxD2zVd16TAfBgNVHSMEGDAWgBRZ4ZTgDpksHL2qcpkFkxD2
    -zVd16TAMBggqhkjOPQQDAwUAA2gAMGUCMQD4IoqT2hTUn0jt7oXLdMJ8q4vLp6sg
    -wHfPiOr9gxreb+e6Oidwd2LDnC4OUqCWiF8CMAzwKs4SnDJYcMLf2vpkbuVE4dTH
    -Rglz+HGcTLWsFs4KxLsq7MuU+vJTBUeDJeDjdA==
    ------END CERTIFICATE-----
    -
    ------BEGIN CERTIFICATE-----
    -MIIDxTCCAq2gAwIBAgIUEMdk6dVgOEIS2cCP0Q43P90Ps5YwDQYJKoZIhvcNAQEF
    -BQAwajELMAkGA1UEBhMCQ04xEzARBgNVBAoMCmlUcnVzQ2hpbmExHDAaBgNVBAsM
    -E0NoaW5hIFRydXN0IE5ldHdvcmsxKDAmBgNVBAMMH2lUcnVzQ2hpbmEgQ2xhc3Mg
    -MiBSb290IENBIC0gRzMwHhcNMTMwNDE4MDkzNjU2WhcNMzMwNDE4MDkzNjU2WjBq
    -MQswCQYDVQQGEwJDTjETMBEGA1UECgwKaVRydXNDaGluYTEcMBoGA1UECwwTQ2hp
    -bmEgVHJ1c3QgTmV0d29yazEoMCYGA1UEAwwfaVRydXNDaGluYSBDbGFzcyAyIFJv
    -b3QgQ0EgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOPPShpV
    -nJbMqqCw6Bz1kehnoPst9pkr0V9idOwU2oyS47/HjJXk9Rd5a9xfwkPO88trUpz5
    -4GmmwspDXjVFu9L0eFaRuH3KMha1Ak01citbF7cQLJlS7XI+tpkTGHEY5pt3EsQg
    -wykfZl/A1jrnSkspMS997r2Gim54cwz+mTMgDRhZsKK/lbOeBPpWtcFizjXYCqhw
    -WktvQfZBYi6o4sHCshnOswi4yV1p+LuFcQ2ciYdWvULh1eZhLxHbGXyznYHi0dGN
    -z+I9H8aXxqAQfHVhbdHNzi77hCxFjOy+hHrGsyzjrd2swVQ2iUWP8BfEQqGLqM1g
    -KgWKYfcTGdbPB1MCAwEAAaNjMGEwHQYDVR0OBBYEFG/oAMxTVe7y0+408CTAK8hA
    -uTyRMB8GA1UdIwQYMBaAFG/oAMxTVe7y0+408CTAK8hAuTyRMA8GA1UdEwEB/wQF
    -MAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBLnUTfW7hp
    -emMbuUGCk7RBswzOT83bDM6824EkUnf+X0iKS95SUNGeeSWK2o/3ALJo5hi7GZr3
    -U8eLaWAcYizfO99UXMRBPw5PRR+gXGEronGUugLpxsjuynoLQu8GQAeysSXKbN1I
    -UugDo9u8igJORYA+5ms0s5sCUySqbQ2R5z/GoceyI9LdxIVa1RjVX8pYOj8JFwtn
    -DJN3ftSFvNMYwRuILKuqUYSHc2GPYiHVflDh5nDymCMOQFcFG3WsEuB+EYQPFgIU
    -1DHmdZcz7Llx8UOZXX2JupWCYzK1XhJb+r4hK5ncf/w8qGtYlmyJpxk3hr1TfUJX
    -Yf4Zr0fJsGuv
    ------END CERTIFICATE-----
    \ No newline at end of file
    diff --git a/_test/alipay/appPublicCert.crt b/_test/alipay/appPublicCert.crt
    deleted file mode 100644
    index 586f341..0000000
    --- a/_test/alipay/appPublicCert.crt
    +++ /dev/null
    @@ -1,19 +0,0 @@
    ------BEGIN CERTIFICATE-----
    -MIIDmTCCAoGgAwIBAgIQICMFBmpzKUKQGs/81YT4UDANBgkqhkiG9w0BAQsFADCBkTELMAkGA1UE
    -BhMCQ04xGzAZBgNVBAoMEkFudCBGaW5hbmNpYWwgdGVzdDElMCMGA1UECwwcQ2VydGlmaWNhdGlv
    -biBBdXRob3JpdHkgdGVzdDE+MDwGA1UEAww1QW50IEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1
    -dGhvcml0eSBDbGFzcyAyIFIxIHRlc3QwHhcNMjMwNTA2MDYwMTA3WhcNMjQwNTEwMDYwMTA3WjBr
    -MQswCQYDVQQGEwJDTjEfMB0GA1UECgwWd21pYWt1Njc0OUBzYW5kYm94LmNvbTEPMA0GA1UECwwG
    -QWxpcGF5MSowKAYDVQQDDCEyMDg4NzIxMDAyODQzNDE0LTIwMjEwMDAxMjI2NjczMDYwggEiMA0G
    -CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCd0fbopV7cjL7wbFXEO4b1miViuoY2bwml2L0atB0z
    -zVp3ik/K6XY1U770LYqvprqMrpDDWDLNzBjG2RQLvGgtlRI+mZIfuwfewPqvYHsx/kWytVJrb2Vy
    -bX7dSTIWBmNhOTHjmV9suYAUc7M6XRZ8aQxbRKrc3/k4yvTvgTgSdi0At0TeNVAIN/xvOo3eP8rB
    -7t/XQCCW/GKWLYGsUznriyKWpxuInxTXMOZN1m+YSQ/uJr9hWSa3nDeuHtkGhq5u1wz+tUZhcvaF
    -A/Cv+mq40JO7sSHVFxZ+lydu7D6A6do3JeY0WZmR42V6BciJ9y6e3UYrprWlfT3RHx4U3cuNAgMB
    -AAGjEjAQMA4GA1UdDwEB/wQEAwIE8DANBgkqhkiG9w0BAQsFAAOCAQEAiLA1wk7or0uqxPSD9z31
    -BLJRnamf51Uz2YOSDPjUivu9VJrkjf1PsCleK9RSgOcXcpy9QWZSFIIyakFqCCWylgBdjmhSvzAv
    -po86ycJXEBrd7klOM/6VFh68BqpmK1CJl0g29JOJ1fNvIKYJ/WS4ue988NSGrpsVhXbNGALhgiBL
    -Gqs7KkZQSsIAySx2HNCSknFM5G5xG6IR6wUJ619khba6rMaKOJ3D7mxuv0Vjyu7DrFBsPLOSdjP1
    -Jzi5WmutetJGVrbOKayVjL4xDcU7lgOWTrhTs7S54Z6GnxqRLwy8gD7RmDbXtnVyGP/mUcAgnFtE
    -BPFZBe7qjqsCCkf+Zw==
    ------END CERTIFICATE-----
    \ No newline at end of file
    diff --git a/_test/alipay2.php b/_test/alipay2.php
    deleted file mode 100644
    index e0fdd20..0000000
    --- a/_test/alipay2.php
    +++ /dev/null
    @@ -1,58 +0,0 @@
    - true,
    -    // 签名类型 ( RSA|RSA2 )
    -    'sign_type'        => 'RSA2',
    -    // 应用ID
    -    'appid'            => '2021000122667306',
    -    // 应用私钥内容 ( 需1行填写,特别注意:这里的应用私钥通常由支付宝密钥管理工具生成 )
    -    'private_key'      => 'MIIEowIBAAKCAQEAndH26KVe3Iy+8GxVxDuG9ZolYrqGNm8Jpdi9GrQdM81ad4pPyul2NVO+9C2Kr6a6jK6Qw1gyzcwYxtkUC7xoLZUSPpmSH7sH3sD6r2B7Mf5FsrVSa29lcm1+3UkyFgZjYTkx45lfbLmAFHOzOl0WfGkMW0Sq3N/5OMr074E4EnYtALdE3jVQCDf8bzqN3j/Kwe7f10Aglvxili2BrFM564silqcbiJ8U1zDmTdZvmEkP7ia/YVkmt5w3rh7ZBoaubtcM/rVGYXL2hQPwr/pquNCTu7Eh1RcWfpcnbuw+gOnaNyXmNFmZkeNlegXIifcunt1GK6a1pX090R8eFN3LjQIDAQABAoIBACrLY4OETCvL8n6pMbyLU7ZHfTm/UGN0So5xLh4OlxiT56MgmzBvjAE72zzFGKU2tcEuGM0Pnn8Vh+ZruLbR+QHbOV5GMExwX9r0Q0XJCL7uryGdb2L4iu6zaEJC9dTpGIulgbSwwyJtTqC9Gu2Jjm5f4dzhyt8n0KGozzAevwCqI9RaJSD96gGWLbMlHCyWKGy1OdBP4V/+agPyHAGZ9gqpfKY7y4L0My8gUxhWzQWOwihtFACjV66ULhutUYT2bro3j1k9UekKlX7IiWrssPmmmw2vfUbrKiNugF6zkfyStPt7jGJ0CdzAHWe3pyF72TyO5NU2NGcX8eKgYlY2crUCgYEAzOcg5Zot9X+Ao+fYH/Oq/3eGZd6krzByiXfcjuRco7mGODwmUnzt3PpPT1fPry9TxarTajt+A9LuxqWawfQ9eWAfrTGAbtDJB0LYo6CynDqUoRqBukROuNaLQiUqEreOQqt08o6VVblgVLv8475ij8s4z/6C2NSSjgUJHf0PL38CgYEAxS0bcXGI+WtempZ4Q3QMTUmp/+B3zuw9JzSV1gvbVi7MleI9V62V2IXHPSXL5mRhYOuQWR0MnVOhbo69fkEA8HpdSd1q2JjaeS+OiZ0ditcJISQJbqWtvmF2+XtQcbVwfID69GWGxyBEHHTW8AtAzIPc6T7x2izyzBw0lXDHSvMCgYEAi+Y61ckhG/9EC6TeMWKjG+21u5P6CQshCK7nzkAo6DhhZb/bwnI9zaSxxdCEom3D2rA5zMx1y5KXKNYlBcwGtPpmZk/oCsFOoECJvZ6YlIaCuERq0oyU2yrQxgat5T2iSe7a2El1uKPrG6+GiNCSZu8wCQMSv4zTy1ew0+LWHW0CgYAvX7ESRpcEZjmqprBqdH1oLGS9566hdr0SqF2/ucWPJVteP6dBY6F3Dl1aYbRlvIRxBuf9oS8gtbE5oO4CYZfaL2wujRZYyBDlwPlcMvWgIB4/aish/IiMD1rIgkpHp7JJF6w0ABiryyLSO3hQ4ENHX/85wzfUlawYQkaYCSq45QKBgCdqrv58KD8tDYn4JnaHJNE+5TgKK5cNhYLZLAsYz7x1KfPdkiC7y/hnenn3TWkm4xw8Tw1rJ1ZIJ24iZgTCTO7EEsB7jZegvg4z/4zVbSK2Y4VI1lJ7jlyqmwg0ArimXTNZFoy66h9c9t2smG40YEZCmmmTLEqVlWgyR1MU5iM5',
    -    // 公钥模式,支付宝公钥内容 ( 需1行填写,特别注意:这里不是应用公钥而是支付宝公钥,通常是上传应用公钥换取支付宝公钥,在网页可以复制 )
    -    'public_key'       => 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuQ70Xj4AHsIsjjuFK2EexkUVDGc6KCBzhHylt5vbgGTJFzrElV6Ri3O4nxwtKzLWykRfjOC5M9z5vt7NrmSc3WPal0B82TNt0SDVO3gDRvnFswB9pC1WHBTRqiGEsX6LpNOCiykyYHAmc3b74R1rotxytOpLt9/HwjZuGStouUQ7gLqnFRwYLKis8tW9FY5NEL9HPENREcRoaUE8zHE6l4jNi5g2Dvs4r5KqnTNvmeRTc87ZylKs4JPhSWskOaqBJmDAcTR770x0G694tKjW5sbm4a/PxWOV3eEG9XLA4CcS6gwHG1KsRu+eTPszQwkEOZCT8PxZJ6SbaUwZsUO4ZQIDAQAB',
    -    // 证书模式,应用公钥证书路径 ( 新版资金类接口转 app_cert_sn,如文件 appCertPublicKey.crt )
    -    'app_cert_path'    => __DIR__ . '/alipay/appPublicCert.crt', // 'app_cert' => '证书内容',
    -    // 证书模式,支付宝根证书路径 ( 新版资金类接口转 alipay_root_cert_sn,如文件 alipayRootCert.crt )
    -    'alipay_root_path' => __DIR__ . '/alipay/alipayRootCert.crt', // 'root_cert' => '证书内容',
    -    // 证书模式,支付宝公钥证书路径 ( 未填写 public_key 时启用此参数,如文件 alipayPublicCert.crt )
    -    // 'alipay_cert_path' => __DIR__ . '/alipay/alipayPublicCert.crt', // 'public_key' => '证书内容'
    -    // 支付成功通知地址
    -    'notify_url'       => '',
    -    // 网页支付回跳地址
    -    'return_url'       => '',
    -];
    \ No newline at end of file
    diff --git a/_test/config.php b/_test/config.php
    deleted file mode 100644
    index cee0e0a..0000000
    --- a/_test/config.php
    +++ /dev/null
    @@ -1,52 +0,0 @@
    - function ($name, $value, $expired = 360) {
    -//        var_dump(func_get_args());
    -//         return $value;
    -//    },
    -//    'get' => function ($name) {
    -//        var_dump(func_get_args());
    -//        return $value;
    -//    },
    -//    'del' => function ($name) {
    -//        var_dump(func_get_args());
    -//        return true;
    -//    },
    -//    'put' => function ($name) {
    -//        var_dump(func_get_args());
    -//        return $filePath;
    -//    },
    -// ];
    -
    -return [
    -    'token'          => 'test',
    -    'appid'          => 'wx60a43dd8161666d4',
    -    'appsecret'      => 'b4e28746f1bd73b5c6684f5e01883c36',
    -    'encodingaeskey' => 'BJIUzE0gqlWy0GxfPp4J1oPTBmOrNDIGPNav1YFH5Z5',
    -    // 配置缓存目录,需要拥有写权限
    -    'cache_path'     => '',
    -    // 其他支付参数可以合并在这里
    -    // ...
    -];
    \ No newline at end of file
    diff --git a/_test/mini-login.php b/_test/mini-login.php
    deleted file mode 100644
    index 885e28c..0000000
    --- a/_test/mini-login.php
    +++ /dev/null
    @@ -1,38 +0,0 @@
    - 'wx6bb7b70258da09c6',
    -    'appsecret' => '78b7b8d65bd67b078babf951d4342b42',
    -];
    -
    -// 解码数据
    -$iv = 'ltM/wT7hsAl0TijEBI4v/g==';
    -$code = '013LyiTR0TwjC92QjJRR0mEsTR0LyiT3';
    -$decode = 'eIoVtIC2YzLCnrwiIs1IBbXMvC0vyL8bo1IhD38fUQIRbk3lgTWa0Hdw/Ty7NTs3iu7YlqqZBti+cxd6dCfeXBUQwTO2QpbHg0WTeDAdrihsHRHm4dCWdfTx8rzDloGbNOIsKdRElIhUH5YFdiTr5AYiufUDb34cwJ4GNWLAUq4bR0dmFeVEi+3nfwe2MAjGYDl4aq719VLsHodOggK6lXZvM5wjoDyuZsK2dPqJr3/Ji30Z0mdyFq32R4uR3rtJH/h+Rj0+/QmE9QYG7Y6Z48hgPE8cpnhRQNwH49jnC/zKZ9wtDkQ/J8J3Ed2i58zcuY01v8IV+pZ8oBUKXfO5ha+APOxtBSTzyHraU/2RGo8UWtOF6h64OQZhd/UQQy362eyc/qoq8sF9JnEFRP0mRmTDJ+u9oyDhxswCu6x8V73ERWaJeEGSCyjiGpep7/DxZ6eSSBq36OB0BWBkJqsq9Q==';
    -$sessionKey = 'OetNxl86B/yMpbwG6wtMEw==';
    -
    -// $mini = \We::WeMiniCrypt($config);
    -// $mini = new WeMini\Crypt($config);
    -$mini = \WeMini\Crypt::instance($config);
    -
    -echo '
    ';
    -//print_r($mini->session($code));
    -print_r($mini->decode($iv, $sessionKey, $decode));
    -//print_r($mini->userInfo($code, $iv, $decode));
    \ No newline at end of file
    diff --git a/_test/mini-qrc.php b/_test/mini-qrc.php
    deleted file mode 100644
    index 9516669..0000000
    --- a/_test/mini-qrc.php
    +++ /dev/null
    @@ -1,39 +0,0 @@
    - 'wx6bb7b70258da09c6',
    -    'appsecret' => '78b7b8d65bd67b078babf951d4342b42',
    -];
    -
    -//We::config($config);
    -
    -// $mini = We::WeMiniQrcode($config);
    -// $mini = new WeMini\Qrcode($config);
    -$mini = \WeMini\Qrcode::instance($config);
    -
    -//echo '
    ';
    -try {
    -    header('Content-type:image/jpeg'); //输出的类型
    -//    echo $mini->createDefault('pages/index?query=1');
    -//    echo $mini->createMiniScene('432432', 'pages/index/index');
    -    echo $mini->createMiniPath('pages/index?query=1');
    -} catch (Exception $e) {
    -    var_dump($e->getMessage());
    -}
    diff --git a/_test/pay-callapi-test.php b/_test/pay-callapi-test.php
    deleted file mode 100644
    index f6913ab..0000000
    --- a/_test/pay-callapi-test.php
    +++ /dev/null
    @@ -1,169 +0,0 @@
    -=== 微信支付V2通用接口测试 ===";
    -
    -    // ============================================
    -    // 测试1: POST请求 - 统一下单(自动签名)
    -    // ============================================
    -    echo "

    测试1: POST请求 - 统一下单(自动签名)

    "; - try { - $result = $pay->callApi( - 'https://api.mch.weixin.qq.com/pay/unifiedorder', - [ - 'body' => '测试商品', - 'out_trade_no' => time(), - 'total_fee' => '1', - 'openid' => 'o38gpszoJoC9oJYz3UHHf6bEp0Lo', // 请替换为实际的openid - 'trade_type' => 'JSAPI', - 'notify_url' => 'https://your-domain.com/notify.php', - 'spbill_create_ip' => '127.0.0.1', - ], - 'POST', - false, // 不需要证书 - 'MD5' // 签名类型:MD5 - ); - echo '
    ';
    -        var_export($result);
    -        echo '
    '; - } catch (Exception $e) { - echo "错误: " . $e->getMessage() . PHP_EOL; - } - - // ============================================ - // 测试2: GET请求 - 查询订单(不自动签名) - // ============================================ - echo "

    测试2: GET请求 - 查询订单(不自动签名)

    "; - try { - $result = $pay->callApi( - 'https://api.mch.weixin.qq.com/pay/orderquery', - [ - 'out_trade_no' => time() - 3600, // 使用一个可能存在的订单号 - ], - 'GET' - ); - echo '
    ';
    -        var_export($result);
    -        echo '
    '; - } catch (Exception $e) { - echo "错误: " . $e->getMessage() . PHP_EOL; - } - - // ============================================ - // 测试3: POST请求 - 使用HMAC-SHA256签名 - // ============================================ - echo "

    测试3: POST请求 - 使用HMAC-SHA256签名

    "; - try { - $result = $pay->callApi( - 'https://api.mch.weixin.qq.com/pay/unifiedorder', - [ - 'body' => '测试商品', - 'out_trade_no' => time() + 1, - 'total_fee' => '1', - 'openid' => 'o38gpszoJoC9oJYz3UHHf6bEp0Lo', - 'trade_type' => 'JSAPI', - 'notify_url' => 'https://your-domain.com/notify.php', - 'spbill_create_ip' => '127.0.0.1', - ], - 'POST', - false, // 不需要证书 - 'HMAC-SHA256' // 使用HMAC-SHA256签名 - ); - echo '
    ';
    -        var_export($result);
    -        echo '
    '; - } catch (Exception $e) { - echo "错误: " . $e->getMessage() . PHP_EOL; - } - - // ============================================ - // 测试4: POST请求 - 需要证书的接口(如退款) - // ============================================ - echo "

    测试4: POST请求 - 需要证书的接口(如退款)

    "; - echo "

    注意:此测试需要配置证书,如果未配置证书会报错

    "; - try { - $result = $pay->callApi( - 'https://api.mch.weixin.qq.com/secapi/pay/refund', - [ - 'out_trade_no' => time() - 3600, - 'out_refund_no' => time(), - 'total_fee' => '1', - 'refund_fee' => '1', - ], - 'POST', - true, // 需要证书 - 'MD5' // 签名类型:MD5 - ); - echo '
    ';
    -        var_export($result);
    -        echo '
    '; - } catch (Exception $e) { - echo "错误: " . $e->getMessage() . PHP_EOL; - echo "

    这是正常的,因为退款接口需要证书且需要有效的订单号

    "; - } - - // ============================================ - // 测试5: POST请求 - 不自动签名(手动处理) - // ============================================ - echo "

    测试5: POST请求 - 不自动签名(手动处理签名)

    "; - try { - // 手动构建参数和签名 - $data = [ - 'body' => '测试商品', - 'out_trade_no' => time() + 2, - 'total_fee' => '1', - 'openid' => 'o38gpszoJoC9oJYz3UHHf6bEp0Lo', - 'trade_type' => 'JSAPI', - 'notify_url' => 'https://your-domain.com/notify.php', - 'spbill_create_ip' => '127.0.0.1', - ]; - - // 手动添加签名 - $data['sign'] = $pay->getPaySign($data, 'MD5'); - - // 注意:手动签名后,callApi仍会自动签名,所以此示例需要调整 - // 实际使用中,如果已经手动签名,应该直接使用 Tools::post 发送 - echo '
    注意:手动签名后,callApi仍会自动签名,建议直接使用 Tools::post 发送
    '; - echo '
    ';
    -        var_export($result);
    -        echo '
    '; - } catch (Exception $e) { - echo "错误: " . $e->getMessage() . PHP_EOL; - } - - echo "

    === 所有测试完成 ===

    "; -} catch (Exception $e) { - - // 出错啦,处理下吧 - echo "

    错误: " . $e->getMessage() . "

    "; - echo "
    " . $e->getTraceAsString() . "
    "; -} diff --git a/_test/pay-config.php b/_test/pay-config.php deleted file mode 100644 index f772a05..0000000 --- a/_test/pay-config.php +++ /dev/null @@ -1,54 +0,0 @@ - function ($name, $value, $expired = 360) { -// var_dump(func_get_args()); -// return $value; -// }, -// 'get' => function ($name) { -// var_dump(func_get_args()); -// return $value; -// }, -// 'del' => function ($name) { -// var_dump(func_get_args()); -// return true; -// }, -// 'put' => function ($name) { -// var_dump(func_get_args()); -// return $filePath; -// }, -// ]; - -return [ - 'appid' => 'wx60a43dd8161666d4', - // 配置商户支付参数 - 'mch_id' => "1332187001", - 'mch_key' => 'A82DC5BD1F3359081049C568D8502BC5', - // 配置商户支付双向证书目录 (p12 | key,cert 二选一,两者都配置时p12优先) - 'ssl_p12' => __DIR__ . DIRECTORY_SEPARATOR . 'cert' . DIRECTORY_SEPARATOR . '1332187001_20181030_cert.p12', - // 'ssl_key' => __DIR__ . DIRECTORY_SEPARATOR . 'cert' . DIRECTORY_SEPARATOR . '1332187001_20181030_key.pem', - // 'ssl_cer' => __DIR__ . DIRECTORY_SEPARATOR . 'cert' . DIRECTORY_SEPARATOR . '1332187001_20181030_cert.pem', - // 配置缓存目录,需要拥有写权限 - 'cache_path' => '', -]; \ No newline at end of file diff --git a/_test/pay-download-bill.php b/_test/pay-download-bill.php deleted file mode 100644 index e1d491f..0000000 --- a/_test/pay-download-bill.php +++ /dev/null @@ -1,44 +0,0 @@ - '20171001', - 'bill_type' => 'ALL', - ]; - $result = $wechat->billDownload($options); - - var_export($result); - -} catch (Exception $e) { - - // 出错啦,处理下吧 - echo $e->getMessage() . PHP_EOL; - -} \ No newline at end of file diff --git a/_test/pay-order-close.php b/_test/pay-order-close.php deleted file mode 100644 index 229f78c..0000000 --- a/_test/pay-order-close.php +++ /dev/null @@ -1,41 +0,0 @@ -closeOrder($options); - - var_export($result); - -} catch (Exception $e) { - - // 出错啦,处理下吧 - echo $e->getMessage() . PHP_EOL; - -} \ No newline at end of file diff --git a/_test/pay-order-create.php b/_test/pay-order-create.php deleted file mode 100644 index 13aa563..0000000 --- a/_test/pay-order-create.php +++ /dev/null @@ -1,85 +0,0 @@ - '测试商品', - 'out_trade_no' => time(), - 'total_fee' => '1', - 'openid' => 'o38gpszoJoC9oJYz3UHHf6bEp0Lo', - 'trade_type' => 'JSAPI', // JSAPI--JSAPI支付(服务号或小程序支付)、NATIVE--Native 支付、APP--APP支付,MWEB--H5支付 - 'notify_url' => 'https://a.com/text.html', - 'spbill_create_ip' => '127.0.0.1', - ]; - - // 生成预支付码 - $result = $wechat->createOrder($options); - - echo '
    ';
    -    if ($options['trade_type'] === 'NATIVE') {
    -        echo '

    二维码 NATIVE 支付,直接使用 code_url 生成二维码即可

    '; - var_export($result); - return; - } - - // 创建JSAPI参数签名 - $options = $wechat->createParamsForJsApi($result['prepay_id']); - - echo "

    --- 创建 JSAPI 预支付码 ---

    "; - var_export($result); -// array( -// 'return_code' => 'SUCCESS', -// 'return_msg' => 'OK', -// 'result_code' => 'SUCCESS', -// 'mch_id' => '1332187001', -// 'appid' => 'wx60a43dd8161666d4', -// 'nonce_str' => 'YIPDbGWT1jpLLM5R', -// 'sign' => '7EBBA1B5F196CF122C920D10FA768D96', -// 'prepay_id' => 'wx211858080224615a10c2fc9f6c824f0000', -// 'trade_type' => 'JSAPI', -// ) - - echo "

    --- 生成 JSAPI 及 H5 支付参数 ---

    "; - var_export($options); -// array( -// 'appId' => 'wx60a43dd8161666d4', -// 'timeStamp' => '1669028299', -// 'nonceStr' => '5s7h0dyp0nmzylbqytb462fpnb0tmrjg', -// 'package' => 'prepay_id=wx21185819502283c23cca162e9d787f0000', -// 'signType' => 'MD5', -// 'paySign' => 'BBE0F426B8E1EEC9E45AC4459E8AE9D6', -// 'timestamp' => '1669028299', -// ) - -} catch (Exception $exception) { - - // 出错啦,处理下吧 - echo $exception->getMessage() . PHP_EOL; - -} \ No newline at end of file diff --git a/_test/pay-order-notify.php b/_test/pay-order-notify.php deleted file mode 100644 index cd1fc2e..0000000 --- a/_test/pay-order-notify.php +++ /dev/null @@ -1,46 +0,0 @@ -getNotify(); - if ($data['return_code'] === 'SUCCESS' && $data['result_code'] === 'SUCCESS') { - // @todo 去更新下原订单的支付状态 - $order_no = $data['out_trade_no']; - - // 返回接收成功的回复 - ob_clean(); - echo $wechat->getNotifySuccessReply(); - } - -} catch (Exception $e) { - - // 出错啦,处理下吧 - echo $e->getMessage() . PHP_EOL; - -} diff --git a/_test/pay-order-query.php b/_test/pay-order-query.php deleted file mode 100644 index fd75c74..0000000 --- a/_test/pay-order-query.php +++ /dev/null @@ -1,44 +0,0 @@ - '1008450740201411110005820873', -// 'out_trade_no' => '商户订单号', - ]; - $result = $wechat->queryOrder($options); - - var_export($result); - -} catch (Exception $e) { - - // 出错啦,处理下吧 - echo $e->getMessage() . PHP_EOL; - -} \ No newline at end of file diff --git a/_test/pay-redpack-create.php b/_test/pay-redpack-create.php deleted file mode 100644 index 0830fb3..0000000 --- a/_test/pay-redpack-create.php +++ /dev/null @@ -1,55 +0,0 @@ - time(), - 're_openid' => 'o38gps3vNdCqaggFfrBRCRikwlWY', - 'send_name' => '商户名称😍', - 'act_name' => '活动名称', - 'total_amount' => '100', - 'total_num' => '1', - 'wishing' => '感谢您参加猜灯谜活动,祝您元宵节快乐!', - 'remark' => '猜越多得越多,快来抢!', - 'client_ip' => '127.0.0.1', - ]; - // 发送红包记录 - $result = $wechat->create($options); - echo '
    ';
    -    var_export($result);
    -    // 查询红包记录
    -    $result = $wechat->query($options['mch_billno']);
    -    var_export($result);
    -
    -} catch (Exception $e) {
    -
    -    // 出错啦,处理下吧
    -    echo $e->getMessage() . PHP_EOL;
    -
    -}
    \ No newline at end of file
    diff --git a/_test/pay-refund-create.php b/_test/pay-refund-create.php
    deleted file mode 100644
    index b9e6765..0000000
    --- a/_test/pay-refund-create.php
    +++ /dev/null
    @@ -1,46 +0,0 @@
    - '1008450740201411110005820873',
    -        'out_refund_no'  => '3241251235123',
    -        'total_fee'      => '1',
    -        'refund_fee'     => '1',
    -    ];
    -    $result = $wechat->createRefund($options);
    -
    -    var_export($result);
    -
    -} catch (Exception $e) {
    -
    -    // 出错啦,处理下吧
    -    echo $e->getMessage() . PHP_EOL;
    -
    -}
    \ No newline at end of file
    diff --git a/_test/pay-refund-query.php b/_test/pay-refund-query.php
    deleted file mode 100644
    index 0b290d6..0000000
    --- a/_test/pay-refund-query.php
    +++ /dev/null
    @@ -1,46 +0,0 @@
    - '1008450740201411110005820873',
    -        // 'out_trade_no'   => '商户订单号',
    -        // 'out_refund_no' => '商户退款单号'
    -        // 'refund_id' => '微信退款单号',
    -    ];
    -    $result = $wechat->queryRefund($options);
    -
    -    var_export($result);
    -
    -} catch (Exception $e) {
    -
    -    // 出错啦,处理下吧
    -    echo $e->getMessage() . PHP_EOL;
    -
    -}
    \ No newline at end of file
    diff --git a/_test/pay-transfers-create.php b/_test/pay-transfers-create.php
    deleted file mode 100644
    index 9f45ccf..0000000
    --- a/_test/pay-transfers-create.php
    +++ /dev/null
    @@ -1,50 +0,0 @@
    - time(),
    -        'openid'           => 'o38gps3vNdCqaggFfrBRCRikwlWY',
    -        'check_name'       => 'NO_CHECK',
    -        'amount'           => '100',
    -        'desc'             => '企业付款操作说明信息',
    -        'spbill_create_ip' => '127.0.0.1',
    -    ];
    -    $result = $wechat->createTransfers($options);
    -    echo '
    ';
    -    var_export($result);
    -    $result = $wechat->queryTransfers($options['partner_trade_no']);
    -    var_export($result);
    -
    -} catch (Exception $e) {
    -
    -    // 出错啦,处理下吧
    -    echo $e->getMessage() . PHP_EOL;
    -
    -}
    \ No newline at end of file
    diff --git a/_test/pay-transfersbank-create.php b/_test/pay-transfersbank-create.php
    deleted file mode 100644
    index 664da8d..0000000
    --- a/_test/pay-transfersbank-create.php
    +++ /dev/null
    @@ -1,48 +0,0 @@
    - time(),
    -        'enc_bank_no'      => '6212263602037318102',
    -        'enc_true_name'    => '邹景立',
    -        'bank_code'        => '1002',
    -        'amount'           => '100',
    -        'desc'             => '打款测试',
    -    ];
    -    echo '
    ';
    -    $result = $wechat->createTransfersBank($options);
    -    var_export($result);
    -
    -} catch (Exception $e) {
    -
    -    // 出错啦,处理下吧
    -    echo $e->getMessage() . PHP_EOL;
    -
    -}
    \ No newline at end of file
    diff --git a/_test/pay-v3-callapi-test.php b/_test/pay-v3-callapi-test.php
    deleted file mode 100644
    index 7f941a7..0000000
    --- a/_test/pay-v3-callapi-test.php
    +++ /dev/null
    @@ -1,205 +0,0 @@
    -=== 微信支付V3通用接口测试 ===";
    -
    -    // ============================================
    -    // 测试1: POST请求 - 使用相对路径创建订单
    -    // ============================================
    -    echo "

    测试1: POST请求 - 创建JSAPI支付订单(相对路径)

    "; - try { - $result = $payment->callApi( - '/v3/pay/transactions/jsapi', - [ - 'appid' => $config['appid'], - 'mchid' => $config['mch_id'], - 'description' => '测试商品', - 'out_trade_no' => (string)time(), - 'notify_url' => 'https://your-domain.com/notify.php', - 'payer' => [ - 'openid' => 'o38gpszoJoC9oJYz3UHHf6bEp0Lo', // 请替换为实际的openid - ], - 'amount' => [ - 'total' => 1, - 'currency' => 'CNY' - ], - ], - 'POST', - false // 不验证响应签名 - ); - echo '
    ';
    -        var_export($result);
    -        echo '
    '; - } catch (Exception $e) { - echo "错误: " . $e->getMessage() . PHP_EOL; - } - - // ============================================ - // 测试2: GET请求 - 查询订单(完整URL) - // ============================================ - echo "

    测试2: GET请求 - 查询订单(完整URL)

    "; - try { - $outTradeNo = time() - 3600; // 使用一个可能存在的订单号 - $result = $payment->callApi( - "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{$outTradeNo}?mchid={$config['mch_id']}", - '', // GET请求数据为空 - 'GET', - false // 不验证响应签名 - ); - echo '
    ';
    -        var_export($result);
    -        echo '
    '; - } catch (Exception $e) { - echo "错误: " . $e->getMessage() . PHP_EOL; - } - - // ============================================ - // 测试3: POST请求 - 创建H5支付订单 - // ============================================ - echo "

    测试3: POST请求 - 创建H5支付订单

    "; - try { - $result = $payment->callApi( - '/v3/pay/transactions/h5', - [ - 'appid' => $config['appid'], - 'mchid' => $config['mch_id'], - 'description' => '测试商品', - 'out_trade_no' => (string)(time() + 1), - 'notify_url' => 'https://your-domain.com/notify.php', - 'amount' => [ - 'total' => 1, - 'currency' => 'CNY' - ], - 'scene_info' => [ - 'h5_info' => [ - 'type' => 'Wap' - ], - 'payer_client_ip' => '127.0.0.1', - ], - ], - 'POST', - false // 不验证响应签名 - ); - echo '
    ';
    -        var_export($result);
    -        echo '
    '; - } catch (Exception $e) { - echo "错误: " . $e->getMessage() . PHP_EOL; - } - - // ============================================ - // 测试4: POST请求 - 创建退款(需要证书) - // ============================================ - echo "

    测试4: POST请求 - 创建退款

    "; - echo "

    注意:此测试需要有效的订单号

    "; - try { - $result = $payment->callApi( - '/v3/refund/domestic/refunds', - [ - 'out_trade_no' => (string)(time() - 3600), - 'out_refund_no' => (string)time(), - 'amount' => [ - 'refund' => 1, - 'total' => 1, - 'currency' => 'CNY' - ], - ], - 'POST', - false // 不验证响应签名 - ); - echo '
    ';
    -        var_export($result);
    -        echo '
    '; - } catch (Exception $e) { - echo "错误: " . $e->getMessage() . PHP_EOL; - echo "

    这是正常的,因为退款需要有效的订单号

    "; - } - - // ============================================ - // 测试5: GET请求 - 查询退款 - // ============================================ - echo "

    测试5: GET请求 - 查询退款

    "; - try { - $outRefundNo = time() - 3600; - $result = $payment->callApi( - "/v3/refund/domestic/refunds/{$outRefundNo}", - '', - 'GET', - false // 不验证响应签名 - ); - echo '
    ';
    -        var_export($result);
    -        echo '
    '; - } catch (Exception $e) { - echo "错误: " . $e->getMessage() . PHP_EOL; - } - - // ============================================ - // 测试6: POST请求 - 使用数组数据(自动JSON编码) - // ============================================ - echo "

    测试6: POST请求 - 使用数组数据(自动JSON编码)

    "; - try { - $data = [ - 'appid' => $config['appid'], - 'mchid' => $config['mch_id'], - 'description' => '测试商品', - 'out_trade_no' => (string)(time() + 2), - 'notify_url' => 'https://your-domain.com/notify.php', - 'amount' => [ - 'total' => 1, - 'currency' => 'CNY' - ], - ]; - - // callApi会自动将数组转为JSON - $result = $payment->callApi( - '/v3/pay/transactions/jsapi', - $data, - 'POST' - ); - echo '
    ';
    -        var_export($result);
    -        echo '
    '; - } catch (Exception $e) { - echo "错误: " . $e->getMessage() . PHP_EOL; - } - - echo "

    === 所有测试完成 ===

    "; - -} catch (Exception $e) { - - // 出错啦,处理下吧 - echo "

    错误: " . $e->getMessage() . "

    "; - echo "
    " . $e->getTraceAsString() . "
    "; - -} - diff --git a/_test/pay-v3-config-cert.php b/_test/pay-v3-config-cert.php deleted file mode 100644 index d6393a7..0000000 --- a/_test/pay-v3-config-cert.php +++ /dev/null @@ -1,31 +0,0 @@ -download(); - -} catch (\Exception $exception) { - // 出错啦,处理下吧 - echo $exception->getMessage() . PHP_EOL; -} \ No newline at end of file diff --git a/_test/pay-v3-config.php b/_test/pay-v3-config.php deleted file mode 100644 index 26149da..0000000 --- a/_test/pay-v3-config.php +++ /dev/null @@ -1,92 +0,0 @@ - function ($name, $value, $expired = 360) { -// var_dump(func_get_args()); -// return $value; -// }, -// 'get' => function ($name) { -// var_dump(func_get_args()); -// return $value; -// }, -// 'del' => function ($name) { -// var_dump(func_get_args()); -// return true; -// }, -// 'put' => function ($name) { -// var_dump(func_get_args()); -// return $filePath; -// }, -// ]; - -return [ - // 公众号 APPID(可选) - 'appid' => 'wx3760xxxxxxxxxxxx', - // 微信商户号(必填) - 'mch_id' => '15293xxxxxx', - // 微信商户 V3 接口密钥(必填) - 'mch_v3_key' => '98b7fxxxxxxxxxxxxxxxxxxxxxxxxxxxx', - - // 商户证书序列号(可选):用于请求签名 - 'cert_serial' => '49055D67B2XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', - // 微信商户证书公钥(必填):可填写证书内容或文件路径,仅用于提取序列号 - 'cert_public' => $certPublic, - // 微信商户证书私钥(必填):可填写证书内容或文件路径,用于请求数据签名 - 'cert_private' => $certPrivate, - - // 自定义证书包:支持平台证书或支付公钥(可填写文件路径或证书内容) - 'cert_package' => [ - 'PUB_KEY_ID_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' => $certPayment - ], - - // 微信平台证书或支付证书序列号(可选) - // 'mp_cert_serial' => 'PUB_KEY_ID_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', - - // 微信平台证书或支付证书内容(可选) - // 'mp_cert_content' => $certPayment, - - // 运行时文件缓存路径(可选) - 'cache_path' => '' -]; \ No newline at end of file diff --git a/_test/pay-v3-order-app.php b/_test/pay-v3-order-app.php deleted file mode 100644 index 9e804a4..0000000 --- a/_test/pay-v3-order-app.php +++ /dev/null @@ -1,64 +0,0 @@ -create('app', [ - 'appid' => $config['appid'], - 'mchid' => $config['mch_id'], - 'description' => '商品描述', - 'out_trade_no' => $order, - 'notify_url' => 'https://thinkadmin.top', - 'amount' => ['total' => 2, 'currency' => 'CNY'], - ]); - - echo '
    ';
    -    echo "\n--- 创建支付参数 ---\n";
    -    var_export($result);
    -
    -    // 创建退款
    -    $out_refund_no = strval(time());
    -    $result = $payment->createRefund([
    -        'out_trade_no'  => $order,
    -        'out_refund_no' => $out_refund_no,
    -        'amount'        => [
    -            'refund'   => 2,
    -            'total'    => 2,
    -            'currency' => 'CNY'
    -        ]
    -    ]);
    -    echo "\n--- 创建退款订单 ---\n";
    -    var_export($result);
    -
    -    $result = $payment->queryRefund($out_refund_no);
    -
    -    echo "\n--- 查询退款订单 ---\n";
    -    var_export($result);
    -
    -} catch (\Exception $exception) {
    -    // 出错啦,处理下吧
    -    echo $exception->getMessage() . PHP_EOL;
    -}
    \ No newline at end of file
    diff --git a/_test/pay-v3-order-h5.php b/_test/pay-v3-order-h5.php
    deleted file mode 100644
    index 9284445..0000000
    --- a/_test/pay-v3-order-h5.php
    +++ /dev/null
    @@ -1,50 +0,0 @@
    -create('h5', [
    -        'appid'        => $config['appid'],
    -        'mchid'        => $config['mch_id'],
    -        'description'  => '商品描述',
    -        'out_trade_no' => (string)time(),
    -        'notify_url'   => 'https://thinkadmin.top',
    -        'amount'       => ['total' => 2, 'currency' => 'CNY'],
    -        'scene_info'   => [
    -            'h5_info'         => [
    -                'type' => 'Wap',
    -            ],
    -            'payer_client_ip' => '14.23.150.211',
    -        ],
    -    ]);
    -
    -    echo '
    ';
    -    echo "\n--- 创建支付参数 ---\n";
    -    var_export($result);
    -
    -} catch (\Exception $exception) {
    -    // 出错啦,处理下吧
    -    echo $exception->getMessage() . PHP_EOL;
    -}
    \ No newline at end of file
    diff --git a/_test/pay-v3-order-jsapi.php b/_test/pay-v3-order-jsapi.php
    deleted file mode 100644
    index 5f2eacc..0000000
    --- a/_test/pay-v3-order-jsapi.php
    +++ /dev/null
    @@ -1,89 +0,0 @@
    -create('jsapi', [
    -        'appid'        => $config['appid'],
    -        'mchid'        => $config['mch_id'],
    -        'description'  => '商品描述',
    -        'out_trade_no' => $order,
    -        'notify_url'   => 'https://thinkadmin.top',
    -        'payer'        => ['openid' => 'o38gps3vNdCqaggFfrBRCRikwlWY'],
    -        'amount'       => ['total' => 2, 'currency' => 'CNY'],
    -    ]);
    -
    -    echo '
    ';
    -    echo "\n--- 创建支付参数 ---\n";
    -    var_export($result);
    -
    -//    array(
    -//        'appId'     => 'wx60a43dd8161666d4',
    -//        'timeStamp' => '1669027650',
    -//        'nonceStr'  => 'dfscg4lm02uqy448kjd1kjs2eo26joom',
    -//        'package'   => 'prepay_id=wx211847302881094d83b1917194ca880000',
    -//        'signType'  => 'RSA',
    -//        'paySign'   => '1wvvi4vmcJmP3GXB0H52mxp8lOhyqE4BtLmyi3Flg8DVKCES4fsb6+0z/L9sYkbp/TNinsnK0k7mUpTe2Yo86P1DLg18fR7zsIn5u1+3tI58boHk3VsAJl4Uhlti9ME3T7kRq1bEb4DGxp16+ixRynOqndkIkYXxrREhsrZIQlsGMfNCV0K1707s7jBTgqIm1vlkpIjNEg8nbcuG88Vzly4dR1a9K6Fux+sm0gu2rMroRwIo2R/0rgHGDANmnAZj6YEfLZlRrGTbr9r0V1+HHQPvV4BJLvTG8KXVJmJSJzBWSgq31PwrLWdOwdtpNKk7wJbY7yoScYUysYqqzM4DTQ==',
    -//    );
    -
    -    echo "\n\n--- 查询支付参数 ---\n";
    -    $result = $payment->query($order);
    -    var_export($result);
    -
    -//    array(
    -//        'amount'           => array('payer_currency' => 'CNY', 'total' => 2),
    -//        'appid'            => 'wx60a43dd8161666d4',
    -//        'mchid'            => '1332187001',
    -//        'out_trade_no'     => '1669027802',
    -//        'promotion_detail' => array(),
    -//        'scene_info'       => array('device_id' => ''),
    -//        'trade_state'      => 'NOTPAY',
    -//        'trade_state_desc' => '订单未支付',
    -//    );
    -
    -    // 创建退款
    -    $out_refund_no = strval(time());
    -    $result = $payment->createRefund([
    -        'out_trade_no'  => $order,
    -        'out_refund_no' => $out_refund_no,
    -        'amount'        => [
    -            'refund'   => 2,
    -            'total'    => 2,
    -            'currency' => 'CNY'
    -        ]
    -    ]);
    -    echo "\n--- 创建退款订单2 ---\n";
    -    var_export($result);
    -
    -    $result = $payment->queryRefund($out_refund_no);
    -
    -    echo "\n--- 查询退款订单2 ---\n";
    -    var_export($result);
    -
    -} catch (\Exception $exception) {
    -    // 出错啦,处理下吧
    -    echo $exception->getMessage() . PHP_EOL;
    -}
    \ No newline at end of file
    diff --git a/_test/pay-v3-order-native.php b/_test/pay-v3-order-native.php
    deleted file mode 100644
    index ab720e5..0000000
    --- a/_test/pay-v3-order-native.php
    +++ /dev/null
    @@ -1,82 +0,0 @@
    -create('native', [
    -        'appid'        => $config['appid'],
    -        'mchid'        => $config['mch_id'],
    -        'description'  => '商品描述',
    -        'out_trade_no' => $order,
    -        'notify_url'   => 'https://thinkadmin.top',
    -        'amount'       => ['total' => 2, 'currency' => 'CNY'],
    -    ]);
    -
    -    echo '
    ';
    -    echo "\n--- 创建支付参数 ---\n";
    -    var_export($result);
    -
    -//  array('code_url' => 'weixin://wxpay/bizpayurl?pr=cdJXOVDzz');
    -
    -
    -    echo "\n\n--- 查询支付参数 ---\n";
    -    $result = $payment->query($order);
    -    var_export($result);
    -
    -//    array(
    -//        'amount'           => array('payer_currency' => 'CNY', 'total' => 2),
    -//        'appid'            => 'wx60a43dd8161666d4',
    -//        'mchid'            => '1332187001',
    -//        'out_trade_no'     => '1669027871',
    -//        'promotion_detail' => array(),
    -//        'scene_info'       => array('device_id' => ''),
    -//        'trade_state'      => 'NOTPAY',
    -//        'trade_state_desc' => '订单未支付',
    -//    );
    -
    -    // 创建退款
    -    $out_refund_no = strval(time());
    -    $result = $payment->createRefund([
    -        'out_trade_no'  => $order,
    -        'out_refund_no' => $out_refund_no,
    -        'amount'        => [
    -            'refund'   => 2,
    -            'total'    => 2,
    -            'currency' => 'CNY'
    -        ]
    -    ]);
    -    echo "\n--- 创建退款订单2 ---\n";
    -    var_export($result);
    -
    -    $result = $payment->queryRefund($out_refund_no);
    -
    -    echo "\n--- 查询退款订单2 ---\n";
    -    var_export($result);
    -
    -} catch (\Exception $exception) {
    -    // 出错啦,处理下吧
    -    echo $exception->getMessage() . PHP_EOL;
    -}
    \ No newline at end of file
    diff --git a/_test/pay-v3-transfer.php b/_test/pay-v3-transfer.php
    deleted file mode 100644
    index 6998c79..0000000
    --- a/_test/pay-v3-transfer.php
    +++ /dev/null
    @@ -1,48 +0,0 @@
    -batchs([
    -        'out_batch_no'         => 'plfk2020042013',
    -        'batch_name'           => '2019年1月深圳分部报销单',
    -        'batch_remark'         => '2019年1月深圳分部报销单',
    -        'total_amount'         => 100,
    -        'transfer_detail_list' => [
    -            [
    -                'out_detail_no'   => 'x23zy545Bd5436',
    -                'transfer_amount' => 100,
    -                'transfer_remark' => '2020年4月报销',
    -                'openid'          => 'o-MYE42l80oelYMDE34nYD456Xoy',
    -                'user_name'       => '小小邹'
    -            ]
    -        ]
    -    ]);
    -
    -    echo "\n--- 批量打款 ---\n";
    -    var_export($result);
    -
    -} catch (\Exception $exception) {
    -    // 出错啦,处理下吧
    -    echo $exception->getMessage() . PHP_EOL;
    -}
    \ No newline at end of file
    diff --git a/_test/wechat-callapi-test.php b/_test/wechat-callapi-test.php
    deleted file mode 100644
    index 1f3cf74..0000000
    --- a/_test/wechat-callapi-test.php
    +++ /dev/null
    @@ -1,139 +0,0 @@
    -=== 微信公众号通用接口测试 ===";
    -
    -    // ============================================
    -    // 测试1: GET请求 - 使用完整URL(自动处理ACCESS_TOKEN)
    -    // ============================================
    -    echo "

    测试1: GET请求 - 获取用户列表(完整URL,自动处理ACCESS_TOKEN)

    "; - try { - $result = $wechat->callApi( - 'https://api.weixin.qq.com/cgi-bin/user/get?ACCESS_TOKEN', - [], // GET参数(URL中已包含) - 'GET' - ); - echo '
    ';
    -        var_export($result);
    -        echo '
    '; - } catch (Exception $e) { - echo "错误: " . $e->getMessage() . PHP_EOL; - } - - // ============================================ - // 测试2: GET请求 - 使用完整URL带参数 - // ============================================ - echo "

    测试2: GET请求 - 获取用户信息(完整URL带参数)

    "; - try { - $result = $wechat->callApi( - 'https://api.weixin.qq.com/cgi-bin/user/info?ACCESS_TOKEN', - [ - 'openid' => 'o38gpszoJoC9oJYz3UHHf6bEp0Lo', // 请替换为实际的openid - 'lang' => 'zh_CN' - ], - 'GET' - ); - echo '
    ';
    -        var_export($result);
    -        echo '
    '; - } catch (Exception $e) { - echo "错误: " . $e->getMessage() . PHP_EOL; - } - - // ============================================ - // 测试3: POST请求 - 批量获取用户信息 - // ============================================ - echo "

    测试3: POST请求 - 批量获取用户信息(自动JSON编码)

    "; - try { - $result = $wechat->callApi( - 'https://api.weixin.qq.com/cgi-bin/user/info/batchget?ACCESS_TOKEN', - [ - 'user_list' => [ - ['openid' => 'o38gpszoJoC9oJYz3UHHf6bEp0Lo'], // 请替换为实际的openid - // ['openid' => 'o38gps3vNdCqaggFfrBRCRikwlWY'], - ] - ], - 'POST' - ); - echo '
    ';
    -        var_export($result);
    -        echo '
    '; - } catch (Exception $e) { - echo "错误: " . $e->getMessage() . PHP_EOL; - } - - // ============================================ - // 测试4: PUT请求 - 更新资源(如果API支持) - // ============================================ - echo "

    测试4: PUT请求 - 更新资源(示例)

    "; - echo "

    注意:此示例仅展示PUT请求用法,实际API可能不支持

    "; - try { - // PUT请求示例(如果API支持) - // $result = $wechat->callApi( - // 'https://api.weixin.qq.com/some/api?ACCESS_TOKEN', - // ['key' => 'value'], - // 'PUT' - // ); - echo '
    PUT请求用法示例(已注释)
    '; - } catch (Exception $e) { - echo "错误: " . $e->getMessage() . PHP_EOL; - } - - // ============================================ - // 测试5: 使用相对路径(需要手动拼接完整URL) - // ============================================ - echo "

    测试5: GET请求 - 使用相对路径(需要完整URL)

    "; - try { - // 注意:callApi需要完整URL,相对路径需要手动拼接 - $baseUrl = 'https://api.weixin.qq.com'; - $result = $wechat->callApi( - $baseUrl . '/cgi-bin/user/get?ACCESS_TOKEN', - [], - 'GET' - ); - echo '
    ';
    -        var_export($result);
    -        echo '
    '; - } catch (Exception $e) { - echo "错误: " . $e->getMessage() . PHP_EOL; - } - - echo "

    === 所有测试完成 ===

    "; - -} catch (Exception $e) { - - // 出错啦,处理下吧 - echo "

    错误: " . $e->getMessage() . "

    "; - echo "
    " . $e->getTraceAsString() . "
    "; - -} - diff --git a/_test/wechat-jssdk-sign.php b/_test/wechat-jssdk-sign.php deleted file mode 100644 index 4033146..0000000 --- a/_test/wechat-jssdk-sign.php +++ /dev/null @@ -1,40 +0,0 @@ -getJsSign('https://a.com/test.php'); - - var_export($result); - -} catch (Exception $e) { - - // 出错啦,处理下吧 - echo $e->getMessage() . PHP_EOL; - -} \ No newline at end of file diff --git a/_test/wechat-menu-get.php b/_test/wechat-menu-get.php deleted file mode 100644 index 6c1c34f..0000000 --- a/_test/wechat-menu-get.php +++ /dev/null @@ -1,40 +0,0 @@ -get(); - - var_export($result); - -} catch (Exception $e) { - - // 出错啦,处理下吧 - echo $e->getMessage() . PHP_EOL; - -} \ No newline at end of file diff --git a/_test/wechat-qrcode-create.php b/_test/wechat-qrcode-create.php deleted file mode 100644 index 949e83b..0000000 --- a/_test/wechat-qrcode-create.php +++ /dev/null @@ -1,44 +0,0 @@ -create('场景内容'); - echo var_export($result, true) . PHP_EOL; - - // 5. 创建二维码链接 - $url = $wechat->url($result['ticket']); - echo var_export($url, true); - - -} catch (Exception $e) { - - // 出错啦,处理下吧 - echo $e->getMessage() . PHP_EOL; - -} \ No newline at end of file diff --git a/_test/wechat-user-get.php b/_test/wechat-user-get.php deleted file mode 100644 index cc43dbc..0000000 --- a/_test/wechat-user-get.php +++ /dev/null @@ -1,47 +0,0 @@ -getUserList(); - - echo '
    ';
    -    var_export($result);
    -
    -    // 5. 批量获取用户资料
    -    foreach (array_chunk($result['data']['openid'], 100) as $item) {
    -        $userList = $wechat->getBatchUserInfo($item);
    -        var_export($userList);
    -    }
    -
    -} catch (Exception $e) {
    -
    -    // 出错啦,处理下吧
    -    echo $e->getMessage() . PHP_EOL;
    -
    -}
    \ No newline at end of file
    diff --git a/_test/work-config.php b/_test/work-config.php
    deleted file mode 100644
    index fdf4e70..0000000
    --- a/_test/work-config.php
    +++ /dev/null
    @@ -1,47 +0,0 @@
    - function ($name, $value, $expired = 360) {
    -//        var_dump(func_get_args());
    -//         return $value;
    -//    },
    -//    'get' => function ($name) {
    -//        var_dump(func_get_args());
    -//        return $value;
    -//    },
    -//    'del' => function ($name) {
    -//        var_dump(func_get_args());
    -//        return true;
    -//    },
    -//    'put' => function ($name) {
    -//        var_dump(func_get_args());
    -//        return $filePath;
    -//    },
    -// ];
    -
    -return [
    -    'appid'      => '', // 企业ID
    -    'appsecret'  => '', // 应用的凭证密钥
    -    'cache_path' => '', // 配置缓存目录
    -];
    \ No newline at end of file
    diff --git a/_test/work-department.php b/_test/work-department.php
    deleted file mode 100644
    index c36878e..0000000
    --- a/_test/work-department.php
    +++ /dev/null
    @@ -1,32 +0,0 @@
    -callGetApi($url);
    -    echo '
    ';
    -    print_r(BasicWeWork::instance($config)->config->get());
    -    print_r($result);
    -    echo '
    '; -} catch (Exception $exception) { - echo $exception->getMessage() . PHP_EOL; -} diff --git a/composer.json b/composer.json index 9bb246f..fa48d6c 100644 --- a/composer.json +++ b/composer.json @@ -1,47 +1,46 @@ { - "type": "library", "name": "zoujingli/wechat-developer", - "homepage": "https://thinkadmin.top", - "description": "WeChat and Alipay Platform Development", + "description": "Multi-platform developer SDK for WeChat and Alipay.", + "type": "library", "license": "MIT", + "homepage": "https://github.com/zoujingli/WeChatDeveloper", + "keywords": [ + "wechat", + "wechat-pay", + "alipay", + "sdk", + "php" + ], "authors": [ { "name": "Anyon", - "email": "zoujingli@qq.com", - "homepage": "https://thinkadmin.top" + "email": "zoujingli@qq.com" } ], - "keywords": [ - "AliPay", - "WeMini", - "WeChat", - "WeChatPay", - "WeChatDeveloper" - ], "require": { - "php": ">=5.4", - "ext-xml": "*", + "php": ">=8.4", "ext-json": "*", - "ext-curl": "*", - "ext-bcmath": "*", - "ext-libxml": "*", "ext-openssl": "*", - "ext-mbstring": "*", - "ext-simplexml": "*" + "ext-simplexml": "*", + "guzzlehttp/guzzle": "^7.0", + "psr/simple-cache": "^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.5" }, "autoload": { - "files": [ - "helper.php" - ], - "classmap": [ - "We.php" - ], "psr-4": { - "WePay\\": "WePay", - "WeChat\\": "WeChat", - "WeMini\\": "WeMini", - "AliPay\\": "AliPay", - "WePayV3\\": "WePayV3" + "We\\": "src/" } - } + }, + "autoload-dev": { + "psr-4": { + "We\\Tests\\": "src/Tests/" + } + }, + "scripts": { + "test": "@php vendor/bin/phpunit -c phpunit.xml" + }, + "minimum-stability": "dev", + "prefer-stable": true } diff --git a/helper.php b/helper.php deleted file mode 100644 index 6a2dbec..0000000 --- a/helper.php +++ /dev/null @@ -1,25 +0,0 @@ - + + + + src/Tests + + + + + src + + + src/Tests + + + diff --git a/readme.md b/readme.md deleted file mode 100644 index 13b96d3..0000000 --- a/readme.md +++ /dev/null @@ -1,978 +0,0 @@ -# WeChatDeveloper for PHP - -[![star](https://gitcode.com/ThinkAdmin/WeChatDeveloper/star/badge.svg)](https://gitcode.com/ThinkAdmin/ThinkAdmin) -[![star](https://gitee.com/zoujingli/WeChatDeveloper/badge/star.svg?theme=gvp)](https://gitee.com/zoujingli/WeChatDeveloper) -[![Latest Stable Version](https://poser.pugx.org/zoujingli/wechat-developer/v/stable)](https://packagist.org/packages/zoujingli/wechat-developer) -[![Total Downloads](https://poser.pugx.org/zoujingli/wechat-developer/downloads)](https://packagist.org/packages/zoujingli/wechat-developer) -[![Monthly Downloads](https://poser.pugx.org/zoujingli/wechat-developer/d/monthly)](https://packagist.org/packages/zoujingli/wechat-developer) -[![Daily Downloads](https://poser.pugx.org/zoujingli/wechat-developer/d/daily)](https://packagist.org/packages/zoujingli/wechat-developer) -[![PHP Version Require](http://poser.pugx.org/zoujingli/wechat-developer/require/php)](https://packagist.org/packages/wechat-developer) -[![License](https://poser.pugx.org/zoujingli/wechat-developer/license)](https://packagist.org/packages/zoujingli/wechat-developer) - -## 🚀 项目简介 - -**WeChatDeveloper** 是一个功能全面、安全可靠的 PHP 微信和支付宝开发 SDK,基于 [wechat-php-sdk](https://github.com/zoujingli/wechat-php-sdk) 重构优化而成。项目经过全面的安全加固和代码质量提升,为开发者提供稳定、安全、易用的微信生态和支付解决方案。 - -## ✨ 核心特性 - -### 🔒 安全可靠 - -- **输入验证**:全面防护 XSS 攻击,所有用户输入都经过严格过滤和验证 -- **文件安全**:文件操作前进行存在性和权限检查,防止恶意利用 -- **加密安全**:使用 SHA-256 替代 MD5,提供更强的安全防护 -- **序列化安全**:添加反序列化数据验证,防止代码执行攻击 - -### 🎯 功能全面 - -- **微信生态**:支持公众号、小程序、企业微信全生态开发 -- **支付功能**:支持微信支付 V2/V3、支付宝支付全场景 -- **多端支持**:覆盖 App、H5、PC、小程序等所有平台 -- **接口完整**:涵盖用户管理、消息推送、素材管理、支付等所有核心功能 - -### ⚡ 性能优化 - -- **自动刷新**:AccessToken 失效自动刷新机制 -- **缓存支持**:支持自定义缓存驱动,可扩展 Redis 等 -- **错误重试**:智能错误重试机制,提高接口调用成功率 -- **类型安全**:修复所有类型声明问题,提升代码质量 -- **自动缓存清理**:自动清理 CURL 临时缓存文件,适配常驻内存框架 -- **通用接口支持**:提供 `callApi()` 万能接口,支持多种 HTTP 方法,适用于所有场景 - -### 🛠 易于使用 - -- **统一入口**:通过`\We::`静态方法统一创建各种功能实例 -- **向后兼容**:完全保持原有 API 和参数,无需修改现有代码 -- **通用接口**:提供标准化的 `callApi()` 方法,支持 GET/POST/PUT/DELETE 等多种 HTTP 方法 -- **文档完善**:提供详细的使用文档和示例代码 -- **社区支持**:活跃的社区和持续的技术支持 - -## 📋 系统要求 - -- **PHP 版本**:最低要求 PHP 5.4,建议 PHP 7.0+ 以获取最佳性能 -- **扩展要求**:curl、json、xml、openssl、mbstring、bcmath -- **权限要求**:缓存目录需要写权限 -- **推荐环境**:PHP 7.4+ / PHP 8.0+ 生产环境 - -## 📦 快速开始 - -### 安装方式 - -#### 方式一:Composer 安装(推荐) - -```bash -# 安装稳定版本 -composer require zoujingli/wechat-developer - -# 安装开发版本 -composer require zoujingli/wechat-developer dev-master - -# 更新到最新版本 -composer update zoujingli/wechat-developer -``` - -#### 方式二:直接下载 - -```bash -# 下载项目到本地 -git clone https://github.com/zoujingli/WeChatDeveloper.git - -# 在项目中引入 -include "WeChatDeveloper/include.php"; -``` - -### 基础使用 - -```php - 'your_wechat_appid', - 'appsecret' => 'your_wechat_appsecret', - 'mch_id' => 'your_merchant_id', // 微信支付需要 - 'mch_key' => 'your_merchant_key', // 微信支付需要 - 'cache_path' => '/path/to/cache' // 可选,缓存目录 -]; - -// 3. 创建实例并调用 -try { - // 微信用户管理 - $user = \We::WeChatUser($config); - $userList = $user->getUserList(); - - // 微信支付 - $pay = \We::WePayOrder($config); - $order = $pay->create($orderData); - - // 支付宝支付 - $alipay = \We::AliPayWeb($config); - $html = $alipay->apply($payData); - - // 使用通用接口(万能接口) - // 所有接口类都提供 callApi() 方法,支持多种 HTTP 方法 - $result = $user->callApi('https://api.weixin.qq.com/cgi-bin/user/get?ACCESS_TOKEN', [], 'GET'); - $result = $pay->callApi('https://api.mch.weixin.qq.com/pay/unifiedorder', $data, 'POST'); - $result = $alipay->callApi('alipay.trade.query', $params, 'GET'); // 支付宝:apiMethod 作为第一参数 - -} catch (Exception $e) { - echo "错误:" . $e->getMessage(); -} -``` - -## 🎯 功能模块 - -### 📱 微信生态支持 - -#### 微信公众号 - -- **用户管理**:用户信息获取、标签管理、分组管理 -- **消息推送**:模板消息、客服消息、群发消息 -- **素材管理**:图片、语音、视频、图文素材上传和管理 -- **菜单管理**:自定义菜单创建、查询、删除 -- **网页授权**:OAuth2.0 网页授权,获取用户信息 -- **二维码**:临时二维码、永久二维码生成 -- **JSSDK**:微信前端 JS-SDK 支持 -- **卡券功能**:微信卡券接口支持 -- **门店管理**:门店 WIFI 管理、摇一摇周边 - -#### 微信小程序 - -- **数据加密**:小程序数据加密解密处理 -- **用户管理**:用户信息获取、登录状态管理 -- **消息推送**:订阅消息、模板消息、动态消息 -- **二维码**:小程序码生成、URL Scheme -- **内容安全**:图片内容检测、文本内容检测 -- **物流助手**:发货信息管理、物流状态查询 -- **直播功能**:小程序直播接口支持 -- **搜索优化**:小程序页面搜索优化 -- **插件管理**:小程序插件申请、管理 -- **OCR 服务**:身份证、银行卡、驾驶证识别 -- **生物认证**:指纹、面部识别支持 - -#### 企业微信 - -- **部门管理**:部门信息获取、创建、更新 -- **用户管理**:企业用户信息管理 -- **消息推送**:企业消息推送功能 - -### 💰 支付功能支持 - -#### 微信支付 - -- **V2 接口**:统一下单、查询、关闭、退款 -- **V3 接口**:新一代支付接口,支持更多功能 -- **支付方式**:JSAPI、APP、H5、Native、小程序支付 -- **订单管理**:订单创建、查询、关闭、退款 -- **账单管理**:对账单下载、交易明细查询 -- **企业付款**:打款到零钱、打款到银行卡 -- **分账功能**:微信分账接口支持 -- **代金券**:代金券创建、发放、核销 -- **红包功能**:微信红包发送和管理 - -#### 支付宝支付 - -- **支付方式**:App 支付、Web 支付、Wap 支付、扫码支付、刷卡支付 -- **订单管理**:订单创建、查询、关闭、退款 -- **转账功能**:单笔转账、批量转账 -- **账单管理**:对账单下载、交易查询 -- **证书支持**:RSA、RSA2 签名,证书模式支持 - -## 💡 使用案例 - -### 📱 微信公众号功能 - -#### 用户管理 - -```php -getUserList(); - -// 批量获取用户信息 -foreach (array_chunk($result['data']['openid'], 100) as $openids) { - $userList = $user->getBatchUserInfo($openids); - foreach ($userList['user_info_list'] as $userInfo) { - echo "用户:" . $userInfo['nickname'] . "\n"; - } -} - -// 设置用户备注 -$user->updateMark('openid', 'VIP用户'); -``` - -#### 二维码生成 - -```php -create('场景内容'); - -// 获取二维码链接 -$url = $qrcode->url($result['ticket']); -echo "二维码链接:" . $url; -``` - -#### 菜单管理 - -```php -get(); - -// 创建自定义菜单 -$menuData = [ - 'button' => [ - [ - 'type' => 'click', - 'name' => '今日歌曲', - 'key' => 'V1001_TODAY_MUSIC' - ], - [ - 'name' => '菜单', - 'sub_button' => [ - [ - 'type' => 'view', - 'name' => '搜索', - 'url' => 'http://www.soso.com/' - ] - ] - ] - ] -]; -$menu->create($menuData); -``` - -### 💰 微信支付功能 - -#### 微信支付 V2 接口 - -```php - '测试商品', - 'out_trade_no' => time(), - 'total_fee' => '1', - 'openid' => 'o38gpszoJoC9oJYz3UHHf6bEp0Lo', - 'trade_type' => 'JSAPI', // JSAPI/NATIVE/APP/MWEB - 'notify_url' => 'https://your-domain.com/notify.php', - 'spbill_create_ip' => '127.0.0.1', -]; - -$result = $pay->create($options); - -// 生成JSAPI支付参数 -$jsApiParams = $pay->createParamsForJsApi($result['prepay_id']); -// 将 $jsApiParams 传给前端发起支付 -``` - -#### 微信支付 V3 接口 - -```php -create('jsapi', [ - 'appid' => $config['appid'], - 'mchid' => $config['mch_id'], - 'description' => '商品描述', - 'out_trade_no' => $order, - 'notify_url' => 'https://your-domain.com/notify.php', - 'payer' => ['openid' => 'o38gps3vNdCqaggFfrBRCRikwlWY'], - 'amount' => ['total' => 2, 'currency' => 'CNY'], -]); - -// H5支付 -$result = $payment->create('h5', [ - 'appid' => $config['appid'], - 'mchid' => $config['mch_id'], - 'description' => '商品描述', - 'out_trade_no' => $order, - 'notify_url' => 'https://your-domain.com/notify.php', - 'amount' => ['total' => 2, 'currency' => 'CNY'], - 'scene_info' => [ - 'h5_info' => ['type' => 'Wap'], - 'payer_client_ip' => '14.23.150.211', - ], -]); - -// 查询订单 -$result = $payment->query($order); - -// 创建退款 -$refundResult = $payment->createRefund([ - 'out_trade_no' => $order, - 'out_refund_no' => strval(time()), - 'amount' => [ - 'refund' => 2, - 'total' => 2, - 'currency' => 'CNY' - ] -]); -``` - -#### 微信红包 - -```php - time(), - 're_openid' => 'o38gps3vNdCqaggFfrBRCRikwlWY', - 'send_name' => '商户名称', - 'act_name' => '活动名称', - 'total_amount' => '100', - 'total_num' => '1', - 'wishing' => '感谢您参加活动!', - 'remark' => '快来抢红包!', - 'client_ip' => '127.0.0.1', -]; - -$result = $redpack->create($options); - -// 查询红包记录 -$result = $redpack->query($options['mch_billno']); -``` - -### 💳 支付宝支付功能 - -#### 网站支付 - -```php -apply([ - 'out_trade_no' => time(), - 'total_amount' => '1', - 'subject' => '支付订单描述', -]); - -// 直接输出HTML表单,用户点击即可跳转支付 -echo $result; -``` - -#### App 支付 - -```php -apply([ - 'out_trade_no' => strval(time()), - 'total_amount' => '1', - 'subject' => '支付宝订单标题', -]); - -// 返回支付参数字符串,传给App端 -echo $result; -``` - -#### 转账功能 - -```php -create([ - 'out_biz_no' => time(), - 'trans_amount' => '10', - 'product_code' => 'TRANS_ACCOUNT_NO_PWD', - 'biz_scene' => 'DIRECT_TRANSFER', - 'payee_info' => [ - 'identity' => 'zoujingli@qq.com', - 'identity_type' => 'ALIPAY_LOGON_ID', - 'name' => '收款人姓名', - ], -]); -``` - -### 📱 微信小程序功能 - -#### 用户登录和数据解密 - -```php -session($code); - -// 解密用户数据 -$userInfo = $mini->userInfo($code, $iv, $encryptedData); - -// 直接解密数据 -$decoded = $mini->decode($iv, $sessionKey, $encryptedData); -``` - -#### 小程序码生成 - -```php -create([ - 'scene' => 'id=123', - 'page' => 'pages/index/index', - 'width' => 430 -]); -``` - -### 🔧 高级功能 - -#### 通用接口(万能接口) - -所有接口类都提供了 `callApi()` 通用方法,支持多种 HTTP 方法(GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS),可以直接传入完整 URL 和参数进行请求,适用于官方新增接口或自定义接口调用。 - -**方法签名:** - -```php -// 微信公众号 -callApi(string $url, array|string $data = [], string $method = 'GET') - -// 微信支付V2 -callApi(string $url, array|string $data = [], string $method = 'POST', bool $isCert = false, string $signType = 'HMAC-SHA256') - -// 微信支付V3 -callApi(string $url, array|string $data = '', string $method = 'POST', bool $verify = false) - -// 支付宝 -callApi(string $apiMethod, array|string $data = [], string $method = 'GET', bool $verify = false) -``` - -**支持的 HTTP 方法:** - -- `GET` - 获取资源(默认:微信公众号、支付宝) -- `POST` - 创建/提交数据(默认:微信支付) -- `PUT` - 更新资源 -- `DELETE` - 删除资源 -- `PATCH` - 部分更新 -- `HEAD` - 获取响应头 -- `OPTIONS` - 获取支持的方法 - -##### 微信公众号通用接口 - -```php -callApi( - 'https://api.weixin.qq.com/cgi-bin/user/info?openid=OPENID&lang=zh_CN&ACCESS_TOKEN', - [], // GET参数(如果URL中已包含参数,这里可以为空) - 'GET' -); - -// POST请求 - 批量获取用户信息(自动JSON编码) -$result = $user->callApi( - 'https://api.weixin.qq.com/cgi-bin/user/info/batchget?ACCESS_TOKEN', - ['user_list' => [['openid' => 'xxx'], ['openid' => 'yyy']]], - 'POST' -); - -// PUT请求 - 更新资源 -$result = $user->callApi($url, $data, 'PUT'); - -// DELETE请求 - 删除资源 -$result = $user->callApi($url, $data, 'DELETE'); - -// PATCH请求 - 部分更新 -$result = $user->callApi($url, $data, 'PATCH'); -``` - -**参数说明:** - -- `$url` (string) - 完整URL或相对路径(支持 ACCESS_TOKEN 占位符,自动替换) -- `$data` (array|string) - 请求参数(GET参数或POST数据,支持数组或字符串) -- `$method` (string) - 请求方法 GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS,默认 GET - -**默认行为:** - -- ✅ 自动处理 ACCESS_TOKEN(URL中包含 ACCESS_TOKEN 时自动替换) -- ✅ POST 请求自动转为 JSON(数组数据) -- ✅ 返回解析后的数组 - -##### 微信支付V2通用接口 - -```php -callApi( - 'https://api.mch.weixin.qq.com/pay/unifiedorder', - [ - 'body' => '测试商品', - 'out_trade_no' => time(), - 'total_fee' => '1', - 'openid' => 'o38gpszoJoC9oJYz3UHHf6bEp0Lo', - 'trade_type' => 'JSAPI', - 'notify_url' => 'https://your-domain.com/notify.php', - 'spbill_create_ip' => '127.0.0.1', - ], - 'POST' -); - -// POST请求 - 使用MD5签名 -$result = $pay->callApi( - 'https://api.mch.weixin.qq.com/pay/unifiedorder', - $data, - 'POST', - false, // 不需要证书 - 'MD5' // 签名类型:MD5 -); - -// POST请求 - 需要证书(如退款) -$result = $pay->callApi( - 'https://api.mch.weixin.qq.com/secapi/pay/refund', - $data, - 'POST', - true, // 需要证书 - 'MD5' // 签名类型 -); - -// GET请求 - 查询订单 -$result = $pay->callApi( - 'https://api.mch.weixin.qq.com/pay/orderquery', - ['out_trade_no' => '123456'], - 'GET' -); - -// PUT/DELETE请求 -$result = $pay->callApi($url, $data, 'PUT', false, 'MD5'); -$result = $pay->callApi($url, $data, 'DELETE', false, 'MD5'); -``` - -**参数说明:** - -- `$url` (string) - 完整URL -- `$data` (array|string) - 请求参数(支持数组或字符串) -- `$method` (string) - 请求方法 GET|POST|PUT|DELETE|PATCH,默认 POST -- `$isCert` (bool) - 是否需要证书,默认 false -- `$signType` (string) - 签名类型 MD5|HMAC-SHA256,默认 HMAC-SHA256 - -**默认行为:** - -- ✅ 自动签名(POST/PUT/PATCH/DELETE 请求) -- ✅ 返回解析后的数组(XML格式) - -##### 微信支付V3通用接口 - -```php -callApi( - '/v3/pay/transactions/jsapi', - [ - 'appid' => $config['appid'], - 'mchid' => $config['mch_id'], - 'description' => '商品描述', - 'out_trade_no' => (string)time(), - 'notify_url' => 'https://your-domain.com/notify.php', - 'payer' => ['openid' => 'o38gpszoJoC9oJYz3UHHf6bEp0Lo'], - 'amount' => ['total' => 2, 'currency' => 'CNY'], - ], - 'POST' -); - -// GET请求 - 查询订单(完整URL或相对路径) -$result = $payment->callApi( - '/v3/pay/transactions/out-trade-no/123456?mchid=' . $config['mch_id'], - '', - 'GET', - true // 验证响应签名 -); - -// PUT请求 - 更新资源 -$result = $payment->callApi('/v3/some/resource/xxx', $data, 'PUT'); - -// DELETE请求 - 删除资源 -$result = $payment->callApi('/v3/some/resource/xxx', '', 'DELETE'); -``` - -**参数说明:** - -- `$url` (string) - 完整URL或相对路径(如 `/v3/pay/transactions/jsapi`) -- `$data` (array|string) - 请求数据(数组会自动转为JSON) -- `$method` (string) - 请求方法 GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS,默认 POST -- `$verify` (bool) - 验证响应签名,默认 false - -**默认行为:** - -- ✅ 数组数据自动转为 JSON -- ✅ 返回解析后的数组 - -##### 支付宝通用接口 - -```php -callApi( - 'alipay.trade.query', // API方法名(必填,第一参数) - ['out_trade_no' => '123456'], - 'GET', - false // 验证响应签名,默认false -); - -// POST请求 - 订单退款 -$result = $alipay->callApi( - 'alipay.trade.refund', // API方法名(第一参数) - [ - 'out_trade_no' => '123456', - 'refund_amount' => '10', - ], - 'POST', - false // 不验证响应签名 -); - -// PUT/DELETE请求 -$result = $alipay->callApi('alipay.some.method', $data, 'PUT', false); -``` - -**参数说明:** - -- `$apiMethod` (string) - API方法名(如:`alipay.trade.query`),必填,第一参数 -- `$data` (array|string) - 请求参数(支持数组或字符串) -- `$method` (string) - 请求方法 GET|POST|PUT|DELETE|PATCH,默认 GET -- `$verify` (bool) - 验证响应签名,默认 false - -**默认行为:** - -- ✅ 自动使用 gateway 作为请求URL -- ✅ 自动签名(默认开启) -- ✅ 返回解析后的数组 - -#### 自定义缓存 - -SDK 支持自定义缓存驱动,可以适配 Redis、Memcached 等缓存系统,特别适用于常驻内存框架(Workerman、Swoole 等)。 - -```php - function ($name, $value, $expired = 360) { - // 自定义缓存设置逻辑 - // $name: 缓存名称 - // $value: 缓存值 - // $expired: 过期时间(秒) - return Redis::setex($name, $expired, serialize($value)); - }, - 'get' => function ($name) { - // 自定义缓存获取逻辑 - // $name: 缓存名称 - // 返回: 缓存值或 null - $data = Redis::get($name); - return $data ? unserialize($data) : null; - }, - 'del' => function ($name) { - // 自定义缓存删除逻辑 - // $name: 缓存名称 - // 返回: boolean - return Redis::del($name); - }, - 'put' => function ($name, $content) { - // 自定义文件缓存逻辑(用于证书等文件) - // $name: 文件名称 - // $content: 文件内容 - // 返回: 文件路径(必须是可读的文件路径) - $file = '/path/to/cache/' . $name; - file_put_contents($file, $content); - return $file; - }, -]; - -// 注意: -// 1. 未配置自定义缓存时,默认使用文件缓存 -// 2. 文件缓存路径可通过配置中的 'cache_path' 参数设置 -// 3. SDK 会自动清理 CURL 临时缓存文件,无需手动处理 -// 4. 在常驻内存框架中,建议使用 Redis 等外部缓存替代文件缓存 -``` - -#### 错误处理 - -```php -getUserList(); -} catch (\WeChat\Exceptions\InvalidResponseException $e) { - // 接口调用异常 - echo "接口错误:" . $e->getMessage(); -} catch (\WeChat\Exceptions\LocalCacheException $e) { - // 缓存异常 - echo "缓存错误:" . $e->getMessage(); -} catch (Exception $e) { - // 其他异常 - echo "系统错误:" . $e->getMessage(); -} -``` - -### 🧩 小程序快速示例 - -#### 订阅消息发送 - -```php -send([ - 'touser' => '用户openid', - 'template_id' => '模板ID', - 'page' => 'pages/index?foo=bar', - 'data' => [ - 'thing1' => ['value' => '订单已支付'], - 'time2' => ['value' => '2024-01-01 12:00'], - ], -]); -``` - -#### 内容安全校验 - -```php -msgSecCheck('留言内容示例'); - -// 图片校验(文件流) -$imgContent = file_get_contents('/path/to/demo.jpg'); -$security->imgSecCheck($imgContent); -``` - -## ⚙️ 配置说明 - -### 基础配置 - -#### 微信公众号配置 - -```php - 'wx60a43dd8161666d4', - 'appsecret' => 'b4e28746f1bd73b5c6684f5e01883c36', - 'token' => 'your_wechat_token', - 'encodingaeskey' => 'your_encodingaeskey', - 'cache_path' => '/path/to/cache', -]; -``` - -#### 微信支付配置 - -```php - 'wx60a43dd8161666d4', - 'mch_id' => '15293xxxxxx', - 'mch_key' => 'your_merchant_key', - 'cache_path' => '/path/to/cache', -]; - -// 微信支付V3配置(推荐) -$payV3Config = [ - 'appid' => 'wx60a43dd8161666d4', - 'mch_id' => '15293xxxxxx', - 'mch_v3_key' => '98b7fxxxxxxxxxxxxxxxxxxxxxxxxxxxx', - 'cert_serial' => '49055D67B2XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', - 'cert_public' => '-----BEGIN CERTIFICATE-----...', - 'cert_private' => '-----BEGIN PRIVATE KEY-----...', - 'cache_path' => '/path/to/cache', - 'cert_package' => [ - 'PUB_KEY_ID_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' => '-----BEGIN CERTIFICATE-----...' - ], -]; -``` - -#### 支付宝配置 - -```php - '2021000122667306', - 'private_key' => 'MIIEowIBAAKCAQEAn...', - 'public_key' => 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...', - 'sign_type' => 'RSA2', - 'notify_url' => 'https://your-domain.com/alipay/notify', - 'return_url' => 'https://your-domain.com/alipay/return', - 'charset' => 'utf-8', - 'debug' => false, // 生产环境设为false -]; - -// 支付宝证书模式配置 -$alipayCertConfig = [ - 'appid' => '2021000122667306', - 'private_key' => 'MIIEowIBAAKCAQEAn...', - 'app_cert_path' => '/path/to/appPublicCert.crt', - 'alipay_root_path' => '/path/to/alipayRootCert.crt', - 'alipay_cert_path' => '/path/to/alipayPublicCert.crt', - 'sign_type' => 'RSA2', - 'notify_url' => 'https://your-domain.com/alipay/notify', - 'return_url' => 'https://your-domain.com/alipay/return', -]; -``` - -## ❓ 常见问题 - -### Q: 如何获取微信支付证书? - -A: 登录微信商户平台,在"账户中心" -> "API 安全" -> "API 证书"中下载证书文件,或使用 API 证书下载工具。 - -### Q: 支付宝沙箱环境如何配置? - -A: 设置 `debug => true` 并使用沙箱环境的 `appid` 和密钥即可。 - -### Q: AccessToken 过期怎么办? - -A: SDK 已内置自动刷新机制,无需手动处理。如需自定义,可设置 `GetAccessTokenCallback` 回调函数。 - -### Q: 如何自定义缓存存储? - -A: 配置 `\WeChat\Contracts\Tools::$cache_callable` 数组,实现自定义的缓存逻辑。 - -### Q: 支持哪些 PHP 版本? - -A: 最低支持 PHP 5.4,建议使用 PHP 7.0+ 以获得最佳性能。 - -### Q: 如何处理支付回调? - -A: 使用 `\WeChat\Receive` 类处理微信支付回调,使用 `\AliPay\Web` 的 `notify()` 方法处理支付宝回调。 - -### Q: 小程序数据解密失败? - -A: 确保 `session_key` 有效且未过期,检查 `iv` 和 `encryptedData` 参数是否正确。 - -### Q: 如何调试接口调用? - -A: 开启错误日志,查看具体的错误信息。SDK 会抛出详细的异常信息帮助定位问题。 - -### Q: 如何在常驻内存框架中使用? - -A: SDK 会自动清理 CURL 缓存文件,无需额外配置。在常驻内存环境中,建议通过自定义缓存驱动(`\WeChat\Contracts\Tools::$cache_callable`)使用 Redis 等外部缓存,避免文件缓存带来的问题。 - -## 📁 文件说明 - -| 文件名 | 类名 | 描述 | 类型 | 加载方法 | -|-------------------|---------------------|----------------|-------|---------------------------| -| **支付宝支付** | | | | | -| App.php | AliPay\App | 支付宝 App 支付 | 支付宝支付 | \We::AliPayApp() | -| Bill.php | AliPay\Bill | 支付宝账单下载 | 支付宝支付 | \We::AliPayBill() | -| Pos.php | AliPay\Pos | 支付宝刷卡支付 | 支付宝支付 | \We::AliPayPos() | -| Scan.php | AliPay\Scan | 支付宝扫码支付 | 支付宝支付 | \We::AliPayScan() | -| Transfer.php | AliPay\Transfer | 支付宝转账 | 支付宝支付 | \We::AliPayTransfer() | -| Wap.php | AliPay\Wap | 支付宝 Wap 支付 | 支付宝支付 | \We::AliPayWap() | -| Web.php | AliPay\Web | 支付宝 Web 支付 | 支付宝支付 | \We::AliPayWeb() | -| **微信公众号** | | | | | -| Card.php | WeChat\Card | 微信卡券接口支持 | 认证服务号 | \We::WeChatCard() | -| Custom.php | WeChat\Custom | 微信客服消息接口支持 | 认证服务号 | \We::WeChatCustom() | -| Draft.php | WeChat\Draft | 微信草稿箱 | 认证服务号 | \We::WeChatDraft() | -| Freepublish.php | WeChat\Freepublish | 微信发布能力 | 认证服务号 | \We::WeChatFreepublish() | -| Media.php | WeChat\Media | 微信媒体素材接口支持 | 认证服务号 | \We::WeChatMedia() | -| Menu.php | WeChat\Menu | 微信菜单管理 | 认证服务号 | \We::WeChatMenu() | -| Oauth.php | WeChat\Oauth | 微信网页授权消息类接口 | 认证服务号 | \We::WeChatOauth() | -| Pay.php | WeChat\Pay | 微信支付类接口 | 认证服务号 | \We::WeChatPay() | -| Product.php | WeChat\Product | 微信商店类接口 | 认证服务号 | \We::WeChatProduct() | -| Qrcode.php | WeChat\Qrcode | 微信二维码接口支持 | 认证服务号 | \We::WeChatQrcode() | -| Receive.php | WeChat\Receive | 微信推送事件消息处理支持 | 认证服务号 | \We::WeChatReceive() | -| Scan.php | WeChat\Scan | 微信扫一扫接口支持 | 认证服务号 | \We::WeChatScan() | -| Script.php | WeChat\Script | 微信前端 JSSDK 支持 | 认证服务号 | \We::WeChatScript() | -| Shake.php | WeChat\Shake | 微信蓝牙设备揺一揺接口 | 认证服务号 | \We::WeChatShake() | -| Tags.php | WeChat\Tags | 微信粉丝标签接口支持 | 认证服务号 | \We::WeChatTags() | -| Template.php | WeChat\Template | 微信模板消息接口支持 | 认证服务号 | \We::WeChatTemplate() | -| User.php | WeChat\User | 微信粉丝管理接口支持 | 认证服务号 | \We::WeChatUser() | -| Wifi.php | WeChat\Wifi | 微信门店 WIFI 管理支持 | 认证服务号 | \We::WeChatWifi() | -| **微信支付** | | | | | -| Bill.php | WePay\Bill | 微信商户账单及评论 | 微信支付 | \We::WePayBill() | -| Coupon.php | WePay\Coupon | 微信商户代金券 | 微信支付 | \We::WePayCoupon() | -| Order.php | WePay\Order | 微信商户订单 | 微信支付 | \We::WePayOrder() | -| Redpack.php | WePay\Redpack | 微信红包支持 | 微信支付 | \We::WePayRedpack() | -| Refund.php | WePay\Refund | 微信商户退款 | 微信支付 | \We::WePayRefund() | -| Transfers.php | WePay\Transfers | 微信商户打款到零钱 | 微信支付 | \We::WePayTransfers() | -| TransfersBank.php | WePay\TransfersBank | 微信商户打款到银行卡 | 微信支付 | \We::WePayTransfersBank() | -| **微信小程序** | | | | | -| Crypt.php | WeMini\Crypt | 微信小程序数据加密处理 | 微信小程序 | \We::WeMiniCrypt() | -| Delivery.php | WeMini\Delivery | 小程序即时配送 | 微信小程序 | \We::WeMiniDelivery() | -| Guide.php | WeMini\Guide | 小程序导购助手 | 微信小程序 | \We::WeMiniGuide() | -| Image.php | WeMini\Image | 小程序图像处理 | 微信小程序 | \We::WeMiniImage() | -| Live.php | WeMini\Live | 小程序直播接口 | 微信小程序 | \We::WeMiniLive() | -| Logistics.php | WeMini\Logistics | 小程序物流助手 | 微信小程序 | \We::WeMiniLogistics() | -| Message.php | WeMini\Message | 小程序动态消息 | 微信小程序 | \We::WeMiniMessage() | -| Newtmpl.php | WeMini\Newtmpl | 小程序订阅消息 | 微信小程序 | \We::WeMiniNewtmpl() | -| Ocr.php | WeMini\Ocr | 小程序 ORC 服务 | 微信小程序 | \We::WeMiniOcr() | -| Operation.php | WeMini\Operation | 小程序运维中心 | 微信小程序 | \We::WeMiniOperation() | -| Plugs.php | WeMini\Plugs | 微信小程序插件管理 | 微信小程序 | \We::WeMiniPlugs() | -| Poi.php | WeMini\Poi | 小程序地址管理 | 微信小程序 | \We::WeMiniPoi() | -| Qrcode.php | WeMini\Qrcode | 微信小程序二维码管理 | 微信小程序 | \We::WeMiniQrcode() | -| Scheme.php | WeMini\Scheme | 小程序 URL-Scheme | 微信小程序 | \We::WeMiniScheme() | -| Search.php | WeMini\Search | 小程序搜索 | 微信小程序 | \We::WeMiniSearch() | -| Security.php | WeMini\Security | 小程序内容安全 | 微信小程序 | \We::WeMiniSecurity() | -| Shipping.php | WeMini\Shipping | 小程序发货信息 | 微信小程序 | \We::WeMiniShipping() | -| Soter.php | WeMini\Soter | 小程序生物认证 | 微信小程序 | \We::WeMiniSoter() | -| Template.php | WeMini\Template | 微信小程序模板消息支持 | 微信小程序 | \We::WeMiniTemplate() | -| Total.php | WeMini\Total | 微信小程序数据接口 | 微信小程序 | \We::WeMiniTotal() | - -## 📚 文档资源 - -### 官方文档 - -- **微信公众平台**:https://mp.weixin.qq.com/wiki -- **微信支付文档**:https://pay.weixin.qq.com/wiki/doc/api/index.html -- **支付宝开放平台**:https://opendocs.alipay.com/ - -### 项目资源 - -- **ThinkAdmin**:https://github.com/zoujingli/ThinkAdmin -- **在线文档**:https://www.kancloud.cn/zoujingli/wechat-developer -- **技术交流群**:QQ 群 513350915 - -### 代码仓库 - -- **GitHub**:https://github.com/zoujingli/WeChatDeveloper(主仓库) -- **Gitee**:https://gitee.com/zoujingli/WeChatDeveloper(国内镜像) -- **GitCode**:https://gitcode.com/ThinkAdmin/WeChatDeveloper(国内镜像) - -## 🛡️ 安全说明 - -本项目经过全面的安全加固,包括: - -- 输入验证和 XSS 防护 -- 文件操作安全检查 -- 加密算法安全升级 -- 序列化数据验证 -- 类型安全修复 - -建议在生产环境中: - -- 定期更新到最新版本 -- 配置 HTTPS 传输 -- 设置适当的文件权限 -- 监控异常访问日志 - -## 📄 版权说明 - -**WeChatDeveloper** 遵循 **MIT** 开源协议发布,并免费提供使用。 - -本项目包含的第三方源码和二进制文件的版权信息将另行标注,请在对应文件查看。 - -版权所有 Copyright © 2014-2025 by ThinkAdmin (https://thinkadmin.top) All rights reserved。 - -## 💝 赞助支持 - -如果这个项目对您有帮助,欢迎赞助支持! - -![赞助](https://thinkadmin.top/static/img/pay.png) diff --git a/src/Client.php b/src/Client.php new file mode 100644 index 0000000..091286e --- /dev/null +++ b/src/Client.php @@ -0,0 +1,180 @@ +cacheKeyPrefix = $g; + $this->cache = $cache ?? new FileCacheStore(self::defaultCacheStoreDirectory()); + $this->authorizers = $authorizers; + $this->http = $http; + } + + /** 默认缓存落盘目录:`sys_get_temp_dir()` + 包级子目录名,供未显式传入 `cache` 时构造 {@see FileCacheStore}。 */ + public static function defaultCacheStoreDirectory(): string + { + return rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR . '/') . DIRECTORY_SEPARATOR . self::DEFAULT_CACHE_STORE_DIR_NAME; + } + + /** + * 按通道标识取实例:`new Client()->get('wechat.platform', new WechatPlatformConfig(...))` + */ + public function get(string $channel, ConfigInterface $config): object + { + $factory = match ($channel) { + 'wechat.platform' => 'wechatPlatform', + 'wechat.wxapp' => 'wechatWxapp', + 'wechat.service' => 'wechatService', + 'wechat.payment' => 'wechatPayment', + 'alipay.platform' => 'alipayPlatform', + 'alipay.payment' => 'alipayPayment', + default => throw new WechatException('不支持的通道标识: ' . $channel), + }; + + return $this->__call($factory, [$config]); + } + + /** + * 魔术工厂:`$client->wechatPlatform($config)` 等价于反射 `new WechatPlatformClient(...)`。 + * + * @param array $arguments + */ + public function __call(string $name, array $arguments): object + { + $config = $arguments[0] ?? null; + if (!$config instanceof ConfigInterface) { + throw new WechatException('通道工厂第一个参数必须为 ConfigInterface 对象'); + } + + return match ($name) { + 'wechatPlatform' => $this->instantiate(WechatPlatformClient::class, [ + $this->ensureConfig($config, WechatPlatformConfig::class, 'wechatPlatform'), + $this->http, + $this->cache, + $this->cacheKeyPrefix, + ]), + 'wechatWxapp' => $this->instantiate(WechatWxappClient::class, [ + $this->ensureConfig($config, WechatWxappConfig::class, 'wechatWxapp'), + $this->http, + $this->cache, + $this->cacheKeyPrefix, + ]), + 'wechatService' => $this->instantiate(WechatServiceClient::class, [ + $this->ensureConfig($config, WechatServiceConfig::class, 'wechatService'), + $this->http, + $this->cache, + $this->authorizers, + $this->cacheKeyPrefix, + ]), + 'wechatPayment' => $this->instantiate(WechatPaymentClient::class, [ + $this->ensureConfig($config, WechatPaymentConfig::class, 'wechatPayment'), + $this->http, + ]), + 'alipayPlatform' => $this->instantiate(AlipayPlatformClient::class, [ + $this->ensureConfig($config, AlipayPlatformConfig::class, 'alipayPlatform'), + $this->http, + ]), + 'alipayPayment' => $this->instantiate(AlipayPaymentClient::class, [ + $this->ensureConfig($config, AlipayPaymentConfig::class, 'alipayPayment'), + $this->http, + ]), + default => throw new WechatException('不支持的通道工厂方法: ' . $name), + }; + } + + /** + * @param class-string $class + * @param array $args + */ + private function instantiate(string $class, array $args): object + { + try { + return (new ReflectionClass($class))->newInstanceArgs($args); + } catch (ReflectionException $e) { + throw new WechatException('通道客户端实例化失败: ' . $e->getMessage(), 0, $e); + } + } + + /** + * @template T of object + * @param class-string $expected + * @return T + */ + private function ensureConfig(ConfigInterface $config, string $expected, string $factory): object + { + if (!$config instanceof $expected) { + throw new WechatException($factory . ' 需要 ' . $this->shortClass($expected)); + } + + return $config; + } + + /** @param class-string $fqcn */ + private function shortClass(string $fqcn): string + { + $pos = strrpos($fqcn, '\\'); + + return $pos === false ? $fqcn : substr($fqcn, $pos + 1); + } +} diff --git a/src/Config/AlipayPaymentConfig.php b/src/Config/AlipayPaymentConfig.php new file mode 100644 index 0000000..2e35016 --- /dev/null +++ b/src/Config/AlipayPaymentConfig.php @@ -0,0 +1,9 @@ +validate(); + } + + public function validate(): void + { + if (trim($this->appid) === '' || trim($this->privateKey) === '') { + throw new WechatException('支付宝 appid 与 private_key 不能为空'); + } + } + + /** + * @param array $data + */ + public static function fromArray(array $data): static + { + return new static( + (string)($data['appid'] ?? $data['app_id'] ?? ''), + (string)($data['private_key'] ?? $data['merchant_private_key'] ?? ''), + (string)($data['alipay_public_key'] ?? ''), + (string)($data['gateway'] ?? 'https://openapi.alipay.com/gateway.do'), + (string)($data['charset'] ?? 'utf-8'), + (string)($data['sign_type'] ?? 'RSA2'), + (string)($data['format'] ?? 'JSON'), + (string)($data['version'] ?? '1.0'), + ); + } +} diff --git a/src/Config/WechatPaymentConfig.php b/src/Config/WechatPaymentConfig.php new file mode 100644 index 0000000..b6d5d5e --- /dev/null +++ b/src/Config/WechatPaymentConfig.php @@ -0,0 +1,56 @@ +validate(); + } + + public function validate(): void + { + foreach ([ + 'appid' => $this->appid, + 'mchId' => $this->mchId, + 'apiV3Key' => $this->apiV3Key, + 'merchantSerial' => $this->merchantSerial, + 'merchantPrivateKey' => $this->merchantPrivateKey, + ] as $name => $value) { + if (trim($value) === '') { + throw new WechatException($name . ' 不能为空'); + } + } + } + + /** + * @param array $data + */ + public static function fromArray(array $data): static + { + return new static( + (string)($data['appid'] ?? ''), + (string)($data['mch_id'] ?? $data['mchid'] ?? ''), + (string)($data['api_v3_key'] ?? $data['mch_v3_key'] ?? ''), + (string)($data['merchant_serial'] ?? $data['cert_serial'] ?? ''), + (string)($data['merchant_private_key'] ?? $data['cert_private'] ?? ''), + (string)($data['platform_certificate'] ?? $data['cert_public'] ?? ''), + (string)($data['platform_public_key'] ?? ''), + (string)($data['platform_serial'] ?? ''), + ); + } +} diff --git a/src/Config/WechatPlatformConfig.php b/src/Config/WechatPlatformConfig.php new file mode 100644 index 0000000..85f9c29 --- /dev/null +++ b/src/Config/WechatPlatformConfig.php @@ -0,0 +1,51 @@ +validate(); + } + + /** + * @param array $data + */ + public static function fromArray(array $data): static + { + return new static( + (string)($data['appid'] ?? ''), + (string)($data['appsecret'] ?? $data['app_secret'] ?? ''), + (string)($data['token'] ?? ''), + (string)($data['encodingaeskey'] ?? $data['encoding_aes_key'] ?? ''), + (string)($data['storage_scope'] ?? $data['storageScope'] ?? ''), + ); + } + + public function validate(): void + { + foreach ([ + 'appid' => $this->appid, + 'appSecret' => $this->appSecret, + ] as $name => $value) { + if (trim($value) === '') { + throw new WechatException($name . ' 不能为空'); + } + } + } +} diff --git a/src/Config/WechatServiceConfig.php b/src/Config/WechatServiceConfig.php new file mode 100644 index 0000000..601a840 --- /dev/null +++ b/src/Config/WechatServiceConfig.php @@ -0,0 +1,50 @@ +validate(); + } + + public function validate(): void + { + foreach ([ + 'componentAppid' => $this->componentAppid, + 'componentAppSecret' => $this->componentAppSecret, + 'componentToken' => $this->componentToken, + 'componentEncodingAesKey' => $this->componentEncodingAesKey, + ] as $name => $value) { + if (trim($value) === '') { + throw new WechatException($name . ' 不能为空'); + } + } + } + + /** + * @param array $data + */ + public static function fromArray(array $data): static + { + return new static( + (string)($data['component_appid'] ?? ''), + (string)($data['component_appsecret'] ?? $data['component_app_secret'] ?? ''), + (string)($data['component_token'] ?? ''), + (string)($data['component_encodingaeskey'] ?? $data['component_encoding_aes_key'] ?? ''), + (string)($data['storage_scope'] ?? $data['storageScope'] ?? ''), + ); + } +} diff --git a/src/Config/WechatWxappConfig.php b/src/Config/WechatWxappConfig.php new file mode 100644 index 0000000..ed8fe7c --- /dev/null +++ b/src/Config/WechatWxappConfig.php @@ -0,0 +1,39 @@ +validate(); + } + + /** + * @param array $data + */ + public static function fromArray(array $data): static + { + return new static( + (string)($data['appid'] ?? ''), + (string)($data['appsecret'] ?? $data['app_secret'] ?? ''), + (string)($data['storage_scope'] ?? $data['storageScope'] ?? ''), + ); + } + + public function validate(): void + { + if (trim($this->appid) === '' || trim($this->appSecret) === '') { + throw new WechatException('小程序 appid 与 appSecret 不能为空'); + } + } +} diff --git a/src/Contract/ConfigInterface.php b/src/Contract/ConfigInterface.php new file mode 100644 index 0000000..ad3098f --- /dev/null +++ b/src/Contract/ConfigInterface.php @@ -0,0 +1,21 @@ + $data + */ + public static function fromArray(array $data): static; + + /** + * 校验配置字段完整性、密钥格式等通道级约束;失败时抛出 SDK 异常。 + */ + public function validate(): void; +} diff --git a/src/Contract/StoreCacheInterface.php b/src/Contract/StoreCacheInterface.php new file mode 100644 index 0000000..bb8cb3b --- /dev/null +++ b/src/Contract/StoreCacheInterface.php @@ -0,0 +1,35 @@ + $payload + */ + public function saveAuthorizerToken(string $authorizerAppid, array $payload): void; +} diff --git a/src/Exception/ApiException.php b/src/Exception/ApiException.php new file mode 100644 index 0000000..558f4e1 --- /dev/null +++ b/src/Exception/ApiException.php @@ -0,0 +1,7 @@ + $context + */ + public function __construct( + string $message, + int $code = 0, + ?Throwable $previous = null, + private readonly array $context = [] + ) { + parent::__construct($message, $code, $previous); + } + + /** + * @return array + */ + public function context(): array + { + return $this->context; + } +} diff --git a/src/Platform/Alipay/PaymentClient.php b/src/Platform/Alipay/PaymentClient.php new file mode 100644 index 0000000..1f070c9 --- /dev/null +++ b/src/Platform/Alipay/PaymentClient.php @@ -0,0 +1,87 @@ + $bizContent + * @param array $extra + * @return array + */ + public function refund(array $bizContent, array $extra = []): array + { + return $this->request('alipay.trade.refund', $bizContent, $extra); + } + + /** + * @param array $bizContent + */ + public function page(array $bizContent, array $extra = []): string + { + $params = [ + 'app_id' => $this->config->appid, + 'method' => 'alipay.trade.page.pay', + 'format' => $this->config->format, + 'charset' => $this->config->charset, + 'sign_type' => $this->config->signType, + 'timestamp' => date('Y-m-d H:i:s'), + 'version' => $this->config->version, + 'biz_content' => json_encode($bizContent, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: '{}', + ]; + foreach ($extra as $key => $value) { + $params[(string)$key] = is_scalar($value) ? (string)$value : json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + } + $params['sign'] = $this->sign($params); + + return $this->config->gateway . '?' . http_build_query($params); + } + + /** + * @param array $params + * @param array $options + * @return array + */ + public function call(string $uriOrPath, array $params = [], string $httpMethod = 'POST', array $options = []): array + { + $apiMethod = trim($uriOrPath); + if ($apiMethod === 'page') { + return ['url' => $this->page($params, $options)]; + } + if ($apiMethod === 'refund') { + return $this->refund($params, $options); + } + + return $this->request($apiMethod, $params, $options); + } + + /** + * @param array $params + * @param array $options + * @return array + */ + public function post(string $uriOrPath, array $params = [], array $options = []): array + { + return $this->call($uriOrPath, $params, 'POST', $options); + } + + /** + * @param array $params + * @param array $options + * @return array + */ + public function get(string $uriOrPath, array $params = [], array $options = []): array + { + return $this->call($uriOrPath, $params, 'GET', $options); + } +} diff --git a/src/Platform/Alipay/PlatformClient.php b/src/Platform/Alipay/PlatformClient.php new file mode 100644 index 0000000..4ea474c --- /dev/null +++ b/src/Platform/Alipay/PlatformClient.php @@ -0,0 +1,159 @@ +http = $http ?? new GuzzleClient(['timeout' => 20.0]); + } + + /** @param array $bizContent @param array $extra @return array */ + public function request(string $apiMethod, array $bizContent = [], array $extra = []): array + { + $params = [ + 'app_id' => $this->config->appid, + 'method' => $apiMethod, + 'format' => $this->config->format, + 'charset' => $this->config->charset, + 'sign_type' => $this->config->signType, + 'timestamp' => date('Y-m-d H:i:s'), + 'version' => $this->config->version, + 'biz_content' => json_encode($bizContent, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: '{}', + ]; + foreach ($extra as $key => $value) { + $params[(string)$key] = is_scalar($value) ? (string)$value : json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + } + $params['sign'] = $this->sign($params); + $response = $this->http->request('POST', $this->config->gateway, [ + 'form_params' => $params, + 'headers' => ['Accept' => 'application/json'], + ]); + $payload = json_decode((string)$response->getBody(), true); + if (!is_array($payload)) { + throw new WechatException('支付宝网关响应格式无效'); + } + $node = str_replace('.', '_', $apiMethod) . '_response'; + $data = is_array($payload[$node] ?? null) ? $payload[$node] : $payload; + if (($data['code'] ?? '10000') !== '10000') { + throw new WechatException((string)($data['sub_msg'] ?? $data['msg'] ?? '支付宝接口调用失败')); + } + + return $data; + } + + public function auth(string $redirectUri, string $scope = 'auth_user', string $state = ''): string + { + return 'https://openauth.alipay.com/oauth2/publicAppAuthorize.htm?' . http_build_query([ + 'app_id' => $this->config->appid, + 'scope' => $scope, + 'redirect_uri' => $redirectUri, + 'state' => $state, + ]); + } + + /** @return array */ + public function decrypt(string $encryptedData, string $sessionKey, string $iv): array + { + $plain = openssl_decrypt( + base64_decode($encryptedData, true) ?: '', + 'AES-128-CBC', + base64_decode($sessionKey, true) ?: '', + OPENSSL_RAW_DATA, + base64_decode($iv, true) ?: '' + ); + if (!is_string($plain) || $plain === '') { + throw new WechatException('支付宝数据解密失败'); + } + $data = json_decode($plain, true); + if (!is_array($data)) { + throw new WechatException('支付宝解密结果无效'); + } + + return $data; + } + + /** + * @param array $params + * @param array $options + * @return array + */ + public function call(string $uriOrPath, array $params = [], string $httpMethod = 'POST', array $options = []): array + { + $apiMethod = trim($uriOrPath); + if ($apiMethod === 'auth') { + return ['url' => $this->auth( + (string)($params['redirect_uri'] ?? ''), + (string)($params['scope'] ?? 'auth_user'), + (string)($params['state'] ?? ''), + )]; + } + if ($apiMethod === 'decrypt') { + return $this->decrypt( + (string)($params['encrypted_data'] ?? ''), + (string)($params['session_key'] ?? ''), + (string)($params['iv'] ?? ''), + ); + } + + return $this->request($apiMethod, $params, $options); + } + + /** + * @param array $params + * @param array $options + * @return array + */ + public function post(string $uriOrPath, array $params = [], array $options = []): array + { + return $this->call($uriOrPath, $params, 'POST', $options); + } + + /** + * @param array $params + * @param array $options + * @return array + */ + public function get(string $uriOrPath, array $params = [], array $options = []): array + { + return $this->call($uriOrPath, $params, 'GET', $options); + } + + /** @param array $params */ + protected function sign(array $params): string + { + ksort($params); + $pairs = []; + foreach ($params as $key => $value) { + if ($key === 'sign' || $value === null || $value === '') { + continue; + } + $pairs[] = $key . '=' . $value; + } + $data = implode('&', $pairs); + $privateKey = str_contains($this->config->privateKey, 'BEGIN') ? $this->config->privateKey : "-----BEGIN PRIVATE KEY-----\n" . chunk_split($this->config->privateKey, 64, "\n") . "-----END PRIVATE KEY-----"; + $resource = openssl_pkey_get_private($privateKey); + if ($resource === false) { + throw new WechatException('支付宝私钥无效'); + } + $algo = strtoupper($this->config->signType) === 'RSA2' ? OPENSSL_ALGO_SHA256 : OPENSSL_ALGO_SHA1; + $ok = openssl_sign($data, $signature, $resource, $algo); + if ($ok !== true) { + throw new WechatException('支付宝签名失败'); + } + + return base64_encode($signature); + } +} diff --git a/src/Platform/Wechat/PaymentClient.php b/src/Platform/Wechat/PaymentClient.php new file mode 100644 index 0000000..b5df91b --- /dev/null +++ b/src/Platform/Wechat/PaymentClient.php @@ -0,0 +1,142 @@ +http = new JsonClient($http ?? new \GuzzleHttp\Client(['base_uri' => 'https://api.mch.weixin.qq.com/', 'timeout' => 20.0])); + } + + /** @param array $payload @return array */ + public function request(string $method, string $uri, array $payload = [], array $query = []): array + { + $uri = '/' . ltrim($uri, '/'); + $body = $payload === [] ? '' : (json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: '{}'); + $nonce = bin2hex(random_bytes(16)); + $timestamp = (string)time(); + $authorization = $this->authorization($method, $uri . ($query === [] ? '' : '?' . http_build_query($query)), $timestamp, $nonce, $body); + + return $this->http->request($method, ltrim($uri, '/'), $query, [ + 'body' => $body, + 'headers' => [ + 'Accept' => 'application/json', + 'Content-Type' => 'application/json', + 'Authorization' => $authorization, + 'Wechatpay-Serial' => $this->config->merchantSerial, + ], + ]); + } + + /** @param array $payload @return array */ + public function refund(array $payload): array + { + return $this->request('POST', 'v3/refund/domestic/refunds', $payload); + } + + /** + * @param array $headers + * @param array $body + * @return array + */ + public function decryptNotification(array $headers, array $body): array + { + $this->assertNotificationSignature($headers, json_encode($body, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: '{}'); + /** @var array{ciphertext:string,nonce:string,associated_data?:string} $resource */ + $resource = $body['resource'] ?? []; + + return PayCrypto::decryptResource($this->config->apiV3Key, $resource); + } + + /** + * @param array $params + * @param array $options + * @return array + */ + public function call(string $uriOrPath, array $params = [], string $httpMethod = 'POST', array $options = []): array + { + $uri = ltrim($uriOrPath, '/'); + $method = strtoupper($httpMethod === '' ? 'POST' : $httpMethod); + if ($uri === 'refund') { + return $this->refund($params); + } + if ($uri === 'decrypt_notification') { + return $this->decryptNotification( + is_array($options['headers'] ?? null) ? $options['headers'] : [], + is_array($options['body'] ?? null) ? $options['body'] : $params, + ); + } + + return $this->request( + $method, + $uri, + $method === 'GET' ? (is_array($options['payload'] ?? null) ? $options['payload'] : []) : $params, + $method === 'GET' ? $params : (is_array($options['query'] ?? null) ? $options['query'] : []), + ); + } + + /** + * @param array $params + * @param array $options + * @return array + */ + public function post(string $uriOrPath, array $params = [], array $options = []): array + { + return $this->call($uriOrPath, $params, 'POST', $options); + } + + /** + * @param array $params + * @param array $options + * @return array + */ + public function get(string $uriOrPath, array $params = [], array $options = []): array + { + return $this->call($uriOrPath, $params, 'GET', $options); + } + + private function authorization(string $method, string $uri, string $timestamp, string $nonce, string $body): string + { + $message = strtoupper($method) . "\n{$uri}\n{$timestamp}\n{$nonce}\n{$body}\n"; + $signature = Signature::payV3Sign($this->config->merchantPrivateKey, $message); + $schema = 'WECHATPAY2-SHA256-RSA2048'; + $fields = [ + 'mchid' => $this->config->mchId, + 'nonce_str' => $nonce, + 'timestamp' => $timestamp, + 'serial_no' => $this->config->merchantSerial, + 'signature' => $signature, + ]; + + return $schema . ' ' . implode(',', array_map(static fn (string $key, string $value): string => $key . '="' . $value . '"', array_keys($fields), $fields)); + } + + private function assertNotificationSignature(array $headers, string $body): void + { + $timestamp = (string)($headers['Wechatpay-Timestamp'] ?? $headers['wechatpay-timestamp'] ?? ''); + $nonce = (string)($headers['Wechatpay-Nonce'] ?? $headers['wechatpay-nonce'] ?? ''); + $signature = (string)($headers['Wechatpay-Signature'] ?? $headers['wechatpay-signature'] ?? ''); + $message = "{$timestamp}\n{$nonce}\n{$body}\n"; + $publicKey = $this->config->platformPublicKey !== '' ? $this->config->platformPublicKey : $this->config->platformCertificate; + if ($publicKey === '') { + throw new SignatureException('微信支付平台公钥或证书不能为空'); + } + if (!Signature::verifyPayV3($publicKey, $message, $signature)) { + throw new SignatureException('微信支付回调验签失败'); + } + } +} diff --git a/src/Platform/Wechat/PlatformClient.php b/src/Platform/Wechat/PlatformClient.php new file mode 100644 index 0000000..8b0b230 --- /dev/null +++ b/src/Platform/Wechat/PlatformClient.php @@ -0,0 +1,179 @@ +http = new JsonClient($http ?? new \GuzzleHttp\Client(['base_uri' => self::API, 'timeout' => 20.0])); + } + + public function accessToken(bool $refresh = false): string + { + $key = $this->cacheKey(TokenCacheKey::wechatOfficialAccessToken($this->config->appid, $this->config->storageScope)); + if (!$refresh && is_string($token = $this->cache->get($key, '')) && $token !== '') { + return $token; + } + + return $this->cache->lock('lock:' . $key, 30, function () use ($key, $refresh): string { + if (!$refresh && is_string($token = $this->cache->get($key, '')) && $token !== '') { + return $token; + } + $data = $this->http->request('GET', 'cgi-bin/token', [ + 'grant_type' => 'client_credential', + 'appid' => $this->config->appid, + 'secret' => $this->config->appSecret, + ]); + $token = (string)($data['access_token'] ?? ''); + $this->cache->set($key, $token, max(1, (int)($data['expires_in'] ?? 7200) - 300)); + + return $token; + }); + } + + /** + * @param array $query + * @param array $options + * @return array + */ + public function request(string $method, string $uri, array $query = [], array $options = [], bool $withToken = true): array + { + if ($withToken) { + $query['access_token'] = $query['access_token'] ?? $this->accessToken(); + } + + return $this->http->request($method, ltrim($uri, '/'), $query, $options); + } + + /** @param array $menu @return array */ + public function createMenu(array $menu): array + { + return $this->request('POST', 'cgi-bin/menu/create', options: ['json' => $menu]); + } + + /** @return array */ + public function userList(string $nextOpenid = ''): array + { + return $this->request('GET', 'cgi-bin/user/get', ['next_openid' => $nextOpenid]); + } + + /** + * @param array $openids + * @return array + */ + public function batchUserInfo(array $openids, string $lang = 'zh_CN'): array + { + return $this->request('POST', 'cgi-bin/user/info/batchget', options: [ + 'json' => [ + 'user_list' => array_map(static fn (string $openid): array => ['openid' => $openid, 'lang' => $lang], $openids), + ], + ]); + } + + public function messageCrypto(): MessageCrypto + { + return new MessageCrypto($this->config->token, $this->config->encodingAesKey, $this->config->appid); + } + + /** + * 统一调用入口:除 token 基础接口外,默认按 URI + 参数发起请求。 + * + * @param array $params + * @param array $options + * @return array + */ + public function call(string $uriOrPath, array $params = [], string $httpMethod = 'POST', array $options = []): array + { + $method = strtoupper($httpMethod === '' ? 'POST' : $httpMethod); + $uri = ltrim($uriOrPath, '/'); + if ($uri === 'decrypt_message') { + return $this->messageCrypto()->decryptMessage( + (string)($params['body'] ?? ''), + (string)($params['msg_signature'] ?? ''), + (string)($params['timestamp'] ?? ''), + (string)($params['nonce'] ?? ''), + ); + } + if ($uri === 'encrypt_message') { + return [ + 'xml' => $this->messageCrypto()->encryptMessage( + (string)($params['body'] ?? ''), + (string)($params['timestamp'] ?? time()), + (string)($params['nonce'] ?? ''), + ), + ]; + } + + return $this->request( + $method, + $uri, + $method === 'GET' ? $params : (is_array($options['query'] ?? null) ? $options['query'] : []), + $this->buildOptions($method, $params, $options), + (bool)($options['with_token'] ?? true), + ); + } + + /** + * @param array $params + * @param array $options + * @return array + */ + public function post(string $uriOrPath, array $params = [], array $options = []): array + { + return $this->call($uriOrPath, $params, 'POST', $options); + } + + /** + * @param array $params + * @param array $options + * @return array + */ + public function get(string $uriOrPath, array $params = [], array $options = []): array + { + return $this->call($uriOrPath, $params, 'GET', $options); + } + + /** + * @param array $params + * @param array $options + * @return array + */ + private function buildOptions(string $method, array $params, array $options): array + { + if ($method === 'GET' || isset($options['json']) || isset($options['body']) || isset($options['form_params']) || isset($options['multipart'])) { + return $options; + } + $options['json'] = $params; + + return $options; + } + + private function cacheKey(string $logicalKey): string + { + return CacheKey::compose($this->cacheKeyPrefix, self::TOKEN_PLATFORM_CHANNEL, $logicalKey); + } +} diff --git a/src/Platform/Wechat/ServiceClient.php b/src/Platform/Wechat/ServiceClient.php new file mode 100644 index 0000000..95276f8 --- /dev/null +++ b/src/Platform/Wechat/ServiceClient.php @@ -0,0 +1,249 @@ +http = new JsonClient($http ?? new \GuzzleHttp\Client(['base_uri' => 'https://api.weixin.qq.com/', 'timeout' => 20.0])); + $this->authorizers = $authorizers; + } + + private readonly ?StoreTokenInterface $authorizers; + + public function componentAccessToken(string $componentVerifyTicket, bool $refresh = false): string + { + $key = $this->cacheKey(TokenCacheKey::wechatOpenComponentAccessToken($this->config->componentAppid, $this->config->storageScope)); + if (!$refresh && is_string($token = $this->cache->get($key, '')) && $token !== '') { + return $token; + } + + return $this->cache->lock('lock:' . $key, 30, function () use ($key, $componentVerifyTicket, $refresh): string { + if (!$refresh && is_string($token = $this->cache->get($key, '')) && $token !== '') { + return $token; + } + $data = $this->http->request('POST', 'cgi-bin/component/api_component_token', options: [ + 'json' => [ + 'component_appid' => $this->config->componentAppid, + 'component_appsecret' => $this->config->componentAppSecret, + 'component_verify_ticket' => $componentVerifyTicket, + ], + ]); + $token = (string)$data['component_access_token']; + $this->cache->set($key, $token, max(1, (int)($data['expires_in'] ?? 7200) - 300)); + + return $token; + }); + } + + /** @return array */ + public function request(string $method, string $uri, array $query = [], array $options = []): array + { + return $this->http->request($method, ltrim($uri, '/'), $query, $options); + } + + /** @return array */ + public function createPreAuthCode(string $componentAccessToken): array + { + return $this->http->request('POST', 'cgi-bin/component/api_create_preauthcode', ['component_access_token' => $componentAccessToken], [ + 'json' => ['component_appid' => $this->config->componentAppid], + ]); + } + + public function authorizationUrl(string $preAuthCode, string $redirectUri, int $authType = 3, string $state = ''): string + { + return 'https://mp.weixin.qq.com/cgi-bin/componentloginpage?' . http_build_query([ + 'component_appid' => $this->config->componentAppid, + 'pre_auth_code' => $preAuthCode, + 'redirect_uri' => $redirectUri, + 'auth_type' => $authType, + 'biz_appid' => '', + 'state' => $state, + ]); + } + + /** @return array */ + public function queryAuth(string $componentAccessToken, string $authorizationCode): array + { + return $this->http->request('POST', 'cgi-bin/component/api_query_auth', ['component_access_token' => $componentAccessToken], [ + 'json' => [ + 'component_appid' => $this->config->componentAppid, + 'authorization_code' => $authorizationCode, + ], + ]); + } + + /** @return array */ + public function authorizerInfo(string $componentAccessToken, string $authorizerAppid): array + { + return $this->http->request('POST', 'cgi-bin/component/api_get_authorizer_info', ['component_access_token' => $componentAccessToken], [ + 'json' => [ + 'component_appid' => $this->config->componentAppid, + 'authorizer_appid' => $authorizerAppid, + ], + ]); + } + + /** @return array */ + public function requestAsAuthorizer(string $method, string $uri, string $authorizerAppid, string $componentAccessToken, array $query = [], array $options = []): array + { + $query['access_token'] = $this->authorizerAccessToken($componentAccessToken, $authorizerAppid); + + return $this->http->request($method, ltrim($uri, '/'), $query, $options); + } + + public function messageCrypto(): MessageCrypto + { + return new MessageCrypto($this->config->componentToken, $this->config->componentEncodingAesKey, $this->config->componentAppid); + } + + /** + * 统一调用入口:除 token 基础接口外,其他能力默认按 URI + 参数执行。 + * + * @param array $params + * @param array $options + * @return array + */ + public function call(string $uriOrPath, array $params = [], string $httpMethod = 'POST', array $options = []): array + { + $method = strtoupper($httpMethod === '' ? 'POST' : $httpMethod); + $uri = ltrim($uriOrPath, '/'); + if ($uri === 'decrypt_message') { + return $this->messageCrypto()->decryptMessage( + (string)($params['body'] ?? ''), + (string)($params['msg_signature'] ?? ''), + (string)($params['timestamp'] ?? ''), + (string)($params['nonce'] ?? ''), + ); + } + if ($uri === 'encrypt_message') { + return [ + 'xml' => $this->messageCrypto()->encryptMessage( + (string)($params['body'] ?? ''), + (string)($params['timestamp'] ?? time()), + (string)($params['nonce'] ?? ''), + ), + ]; + } + if (isset($options['authorizer_appid'], $options['component_access_token'])) { + return $this->requestAsAuthorizer( + $method, + $uri, + (string)$options['authorizer_appid'], + (string)$options['component_access_token'], + is_array($options['query'] ?? null) ? $options['query'] : [], + $this->buildOptions($method, $params, $options), + ); + } + + return $this->request( + $method, + $uri, + $method === 'GET' ? $params : (is_array($options['query'] ?? null) ? $options['query'] : []), + $this->buildOptions($method, $params, $options), + ); + } + + /** + * @param array $params + * @param array $options + * @return array + */ + public function post(string $uriOrPath, array $params = [], array $options = []): array + { + return $this->call($uriOrPath, $params, 'POST', $options); + } + + /** + * @param array $params + * @param array $options + * @return array + */ + public function get(string $uriOrPath, array $params = [], array $options = []): array + { + return $this->call($uriOrPath, $params, 'GET', $options); + } + + private function authorizerAccessToken(string $componentAccessToken, string $authorizerAppid): string + { + if (!$this->authorizers) { + throw new WechatException('未配置授权账号 Token 仓库'); + } + $key = $this->cacheKey(TokenCacheKey::wechatOpenAuthorizerAccessToken( + $this->config->componentAppid, + $authorizerAppid, + $this->config->storageScope, + )); + if (is_string($token = $this->cache->get($key, '')) && $token !== '') { + return $token; + } + + return $this->cache->lock('lock:' . $key, 30, function () use ($key, $componentAccessToken, $authorizerAppid): string { + if (is_string($token = $this->cache->get($key, '')) && $token !== '') { + return $token; + } + $data = $this->refreshAuthorizerToken($componentAccessToken, $authorizerAppid, $this->authorizers->refreshToken($authorizerAppid)); + $this->authorizers->saveAuthorizerToken($authorizerAppid, $data); + $token = (string)$data['authorizer_access_token']; + $this->cache->set($key, $token, max(1, (int)($data['expires_in'] ?? 7200) - 300)); + + return $token; + }); + } + + /** @return array */ + private function refreshAuthorizerToken(string $componentAccessToken, string $authorizerAppid, string $refreshToken): array + { + return $this->http->request('POST', 'cgi-bin/component/api_authorizer_token', ['component_access_token' => $componentAccessToken], [ + 'json' => [ + 'component_appid' => $this->config->componentAppid, + 'authorizer_appid' => $authorizerAppid, + 'authorizer_refresh_token' => $refreshToken, + ], + ]); + } + + /** + * @param array $params + * @param array $options + * @return array + */ + private function buildOptions(string $method, array $params, array $options): array + { + if ($method === 'GET' || isset($options['json']) || isset($options['body']) || isset($options['form_params']) || isset($options['multipart'])) { + return $options; + } + $options['json'] = $params; + + return $options; + } + + private function cacheKey(string $logicalKey): string + { + return CacheKey::compose($this->cacheKeyPrefix, self::TOKEN_PLATFORM_CHANNEL, $logicalKey); + } +} diff --git a/src/Platform/Wechat/WxappClient.php b/src/Platform/Wechat/WxappClient.php new file mode 100644 index 0000000..54d2e12 --- /dev/null +++ b/src/Platform/Wechat/WxappClient.php @@ -0,0 +1,123 @@ +http = new JsonClient($http ?? new \GuzzleHttp\Client(['base_uri' => 'https://api.weixin.qq.com/', 'timeout' => 20.0])); + } + + public function accessToken(bool $refresh = false): string + { + $key = $this->cacheKey(TokenCacheKey::wechatMiniAccessToken($this->config->appid, $this->config->storageScope)); + if (!$refresh && is_string($token = $this->cache->get($key, '')) && $token !== '') { + return $token; + } + + return $this->cache->lock('lock:' . $key, 30, function () use ($key, $refresh): string { + if (!$refresh && is_string($token = $this->cache->get($key, '')) && $token !== '') { + return $token; + } + $data = $this->http->request('GET', 'cgi-bin/token', [ + 'grant_type' => 'client_credential', + 'appid' => $this->config->appid, + 'secret' => $this->config->appSecret, + ]); + $token = (string)$data['access_token']; + $this->cache->set($key, $token, max(1, (int)($data['expires_in'] ?? 7200) - 300)); + + return $token; + }); + } + + /** + * @return array + */ + public function request(string $method, string $uri, array $query = [], array $options = [], bool $withToken = true): array + { + if ($withToken) { + $query['access_token'] = $query['access_token'] ?? $this->accessToken(); + } + + return $this->http->request($method, ltrim($uri, '/'), $query, $options); + } + + /** + * @param array $params + * @param array $options + * @return array + */ + public function call(string $uriOrPath, array $params = [], string $httpMethod = 'POST', array $options = []): array + { + $method = strtoupper($httpMethod === '' ? 'POST' : $httpMethod); + + return $this->request( + $method, + ltrim($uriOrPath, '/'), + $method === 'GET' ? $params : (is_array($options['query'] ?? null) ? $options['query'] : []), + $this->buildOptions($method, $params, $options), + (bool)($options['with_token'] ?? true), + ); + } + + /** + * @param array $params + * @param array $options + * @return array + */ + public function post(string $uriOrPath, array $params = [], array $options = []): array + { + return $this->call($uriOrPath, $params, 'POST', $options); + } + + /** + * @param array $params + * @param array $options + * @return array + */ + public function get(string $uriOrPath, array $params = [], array $options = []): array + { + return $this->call($uriOrPath, $params, 'GET', $options); + } + + /** + * @param array $params + * @param array $options + * @return array + */ + private function buildOptions(string $method, array $params, array $options): array + { + if ($method === 'GET' || isset($options['json']) || isset($options['body']) || isset($options['form_params'])) { + return $options; + } + $options['json'] = $params; + + return $options; + } + + private function cacheKey(string $logicalKey): string + { + return CacheKey::compose($this->cacheKeyPrefix, self::TOKEN_PLATFORM_CHANNEL, $logicalKey); + } +} diff --git a/src/Support/CacheKey.php b/src/Support/CacheKey.php new file mode 100644 index 0000000..ec60731 --- /dev/null +++ b/src/Support/CacheKey.php @@ -0,0 +1,46 @@ +directory === '') { + throw new WechatException('FileCacheStore 目录不能为空'); + } + if (!is_dir($this->directory) && @mkdir($this->directory, 0775, true) !== true) { + throw new WechatException('FileCacheStore 无法创建目录: ' . $this->directory); + } + if (!is_writable($this->directory)) { + throw new WechatException('FileCacheStore 目录不可写: ' . $this->directory); + } + } + + public function get(string $key, mixed $default = null): mixed + { + $path = $this->pathFor($key); + if (!is_file($path)) { + return $default; + } + $handle = @fopen($path, 'rb'); + if ($handle === false) { + return $default; + } + + try { + if (!flock($handle, LOCK_SH)) { + return $default; + } + $raw = stream_get_contents($handle); + flock($handle, LOCK_UN); + } finally { + fclose($handle); + } + + if (!is_string($raw) || $raw === '') { + return $default; + } + + /** @var array{expires_at?:int,value?:mixed}|null $payload */ + $payload = json_decode($raw, true); + if (!is_array($payload) || !array_key_exists('expires_at', $payload) || !array_key_exists('value', $payload)) { + return $default; + } + if ((int)$payload['expires_at'] < time()) { + $this->del($key); + + return $default; + } + + return $payload['value']; + } + + public function set(string $key, mixed $value, int $ttl): void + { + $path = $this->pathFor($key); + $dir = dirname($path); + if (!is_dir($dir) && @mkdir($dir, 0775, true) !== true) { + throw new WechatException('FileCacheStore 无法创建子目录: ' . $dir); + } + + $body = json_encode( + ['expires_at' => time() + max(1, $ttl), 'value' => $value], + JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES, + ); + $tmp = $path . '.' . bin2hex(random_bytes(4)) . '.tmp'; + if (@file_put_contents($tmp, $body, LOCK_EX) === false) { + @unlink($tmp); + throw new WechatException('FileCacheStore 写入失败: ' . $tmp); + } + if (!@rename($tmp, $path)) { + @unlink($path); + if (!@rename($tmp, $path)) { + @unlink($tmp); + throw new WechatException('FileCacheStore 提交失败: ' . $path); + } + } + } + + public function del(string $key): void + { + $path = $this->pathFor($key); + if (is_file($path)) { + @unlink($path); + } + } + + public function lock(string $key, int $ttl, callable $callback): mixed + { + $path = $this->pathFor('lock:' . $key) . '.lock'; + $dir = dirname($path); + if (!is_dir($dir) && @mkdir($dir, 0775, true) !== true) { + throw new WechatException('FileCacheStore 无法创建锁目录: ' . $dir); + } + + $handle = @fopen($path, 'c'); + if ($handle === false) { + throw new WechatException('FileCacheStore 无法创建锁文件: ' . $path); + } + + try { + if (!flock($handle, LOCK_EX)) { + throw new WechatException('FileCacheStore 获取锁失败: ' . $key); + } + + return $callback(); + } finally { + flock($handle, LOCK_UN); + fclose($handle); + } + } + + private function pathFor(string $key): string + { + $hash = hash('sha256', $key); + + return $this->directory . DIRECTORY_SEPARATOR . substr($hash, 0, 2) . DIRECTORY_SEPARATOR . $hash . '.json'; + } +} diff --git a/src/Support/JsonClient.php b/src/Support/JsonClient.php new file mode 100644 index 0000000..2b2301c --- /dev/null +++ b/src/Support/JsonClient.php @@ -0,0 +1,52 @@ + 20.0]), + ) {} + + /** + * @param array $query + * @param array $options + * @return array + */ + public function request(string $method, string $uri, array $query = [], array $options = []): array + { + $options['query'] = array_merge($query, $options['query'] ?? []); + $response = $this->send($method, $uri, $options); + $body = (string)$response->getBody(); + $data = $body === '' ? [] : json_decode($body, true); + if (!is_array($data)) { + throw new ApiException('微信接口响应不是有效 JSON', (int)$response->getStatusCode(), null, ['body' => $body]); + } + $errcode = (int)($data['errcode'] ?? 0); + if ($errcode !== 0) { + throw new ApiException((string)($data['errmsg'] ?? '微信接口请求失败'), $errcode, null, $data); + } + + return $data; + } + + /** + * @param array $options + */ + public function send(string $method, string $uri, array $options = []): ResponseInterface + { + try { + return $this->http->request($method, $uri, $options); + } catch (GuzzleException $exception) { + throw new ApiException($exception->getMessage(), (int)$exception->getCode(), $exception); + } + } +} diff --git a/src/Support/MessageCrypto.php b/src/Support/MessageCrypto.php new file mode 100644 index 0000000..435ed0c --- /dev/null +++ b/src/Support/MessageCrypto.php @@ -0,0 +1,80 @@ +aesKey = $key; + } + + /** + * @return array + */ + public function decryptMessage(string $xml, string $msgSignature, string $timestamp, string $nonce): array + { + $payload = Xml::decode($xml); + $encrypt = (string)($payload['Encrypt'] ?? ''); + if ($encrypt === '') { + throw new WechatException('微信加密消息缺少 Encrypt 字段'); + } + + Signature::assertSha1($msgSignature, [$this->token, $timestamp, $nonce, $encrypt]); + + $plain = openssl_decrypt(base64_decode($encrypt, true) ?: '', 'AES-256-CBC', $this->aesKey, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, substr($this->aesKey, 0, 16)); + if (!is_string($plain) || $plain === '') { + throw new WechatException('微信加密消息解密失败'); + } + + $plain = $this->removePadding($plain); + $length = unpack('N', substr($plain, 16, 4))[1] ?? 0; + $message = substr($plain, 20, (int)$length); + $appid = substr($plain, 20 + (int)$length); + if (!hash_equals($this->appid, $appid)) { + throw new SignatureException('微信加密消息 AppID 不匹配'); + } + + return Xml::decode($message); + } + + public function encryptMessage(string $xml, string $timestamp, string $nonce): string + { + $random = random_bytes(16); + $payload = $random . pack('N', strlen($xml)) . $xml . $this->appid; + $payload .= str_repeat(chr($pad = 32 - strlen($payload) % 32), $pad); + $encrypted = base64_encode(openssl_encrypt($payload, 'AES-256-CBC', $this->aesKey, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, substr($this->aesKey, 0, 16)) ?: ''); + $signature = Signature::sha1([$this->token, $timestamp, $nonce, $encrypted]); + + return Xml::encode([ + 'Encrypt' => $encrypted, + 'MsgSignature' => $signature, + 'TimeStamp' => $timestamp, + 'Nonce' => $nonce, + ]); + } + + private function removePadding(string $data): string + { + $pad = ord(substr($data, -1)); + if ($pad < 1 || $pad > 32) { + $pad = 0; + } + + return substr($data, 0, strlen($data) - $pad); + } +} diff --git a/src/Support/NullCacheStore.php b/src/Support/NullCacheStore.php new file mode 100644 index 0000000..7fd5b86 --- /dev/null +++ b/src/Support/NullCacheStore.php @@ -0,0 +1,27 @@ + + */ + public static function decryptResource(string $apiV3Key, array $resource): array + { + $ciphertext = base64_decode($resource['ciphertext'], true); + if ($ciphertext === false || strlen($ciphertext) <= 16) { + throw new WechatException('微信支付回调密文无效'); + } + + $tag = substr($ciphertext, -16); + $ciphertext = substr($ciphertext, 0, -16); + $plain = openssl_decrypt( + $ciphertext, + 'aes-256-gcm', + $apiV3Key, + OPENSSL_RAW_DATA, + $resource['nonce'], + $tag, + (string)($resource['associated_data'] ?? '') + ); + if (!is_string($plain) || $plain === '') { + throw new WechatException('微信支付回调解密失败'); + } + + $data = json_decode($plain, true); + if (!is_array($data)) { + throw new WechatException('微信支付回调明文 JSON 无效'); + } + + return $data; + } +} diff --git a/src/Support/PsrSimpleCacheStore.php b/src/Support/PsrSimpleCacheStore.php new file mode 100644 index 0000000..2308626 --- /dev/null +++ b/src/Support/PsrSimpleCacheStore.php @@ -0,0 +1,47 @@ +cache->get($key, $default); + } + + public function set(string $key, mixed $value, int $ttl): void + { + $this->cache->set($key, $value, max(1, $ttl)); + } + + public function del(string $key): void + { + $this->cache->delete($key); + } + + public function lock(string $key, int $ttl, callable $callback): mixed + { + if (!is_callable($this->locker)) { + throw new WechatException('PsrSimpleCacheStore 未配置锁能力'); + } + + return ($this->locker)($key, max(1, $ttl), $callback); + } +} diff --git a/src/Support/Signature.php b/src/Support/Signature.php new file mode 100644 index 0000000..6291f79 --- /dev/null +++ b/src/Support/Signature.php @@ -0,0 +1,56 @@ + $items + */ + public static function sha1(array $items): string + { + sort($items, SORT_STRING); + + return sha1(implode('', $items)); + } + + /** + * @param array $items + */ + public static function assertSha1(string $expected, array $items): void + { + $actual = self::sha1($items); + if (!hash_equals($actual, $expected)) { + throw new SignatureException('微信回调签名验证失败', 0, null, ['expected' => $expected, 'actual' => $actual]); + } + } + + public static function payV3Sign(string $privateKey, string $message): string + { + $key = openssl_pkey_get_private($privateKey); + if ($key === false) { + throw new SignatureException('微信支付商户私钥无效'); + } + if (!openssl_sign($message, $signature, $key, OPENSSL_ALGO_SHA256)) { + throw new SignatureException('微信支付签名生成失败'); + } + + return base64_encode($signature); + } + + public static function verifyPayV3(string $publicKey, string $message, string $signature): bool + { + $key = openssl_pkey_get_public($publicKey); + if ($key === false) { + throw new SignatureException('微信支付平台公钥或证书无效'); + } + + return openssl_verify($message, base64_decode($signature, true) ?: '', $key, OPENSSL_ALGO_SHA256) === 1; + } +} diff --git a/src/Support/TokenCacheKey.php b/src/Support/TokenCacheKey.php new file mode 100644 index 0000000..e37f4ef --- /dev/null +++ b/src/Support/TokenCacheKey.php @@ -0,0 +1,57 @@ + + */ + public static function decode(string $xml): array + { + $xml = trim($xml); + if ($xml === '') { + return []; + } + + $previous = libxml_use_internal_errors(true); + $element = simplexml_load_string($xml, SimpleXMLElement::class, LIBXML_NOCDATA); + libxml_use_internal_errors($previous); + if (!$element instanceof SimpleXMLElement) { + throw new WechatException('微信 XML 格式无效'); + } + + return self::normalize($element); + } + + /** + * @param array $data + */ + public static function encode(array $data): string + { + $content = ''; + foreach ($data as $key => $value) { + if (is_array($value)) { + $value = json_encode($value, JSON_UNESCAPED_UNICODE) ?: ''; + } + $value = (string)$value; + $content .= sprintf('<%1$s>', $key, $value); + } + + return $content . ''; + } + + /** + * @return array + */ + private static function normalize(SimpleXMLElement $element): array + { + $result = []; + foreach ($element->children() as $key => $value) { + $children = $value->children(); + $result[$key] = $children->count() > 0 ? self::normalize($value) : (string)$value; + } + + return $result; + } +} diff --git a/src/Tests/AccessTokenCacheTest.php b/src/Tests/AccessTokenCacheTest.php new file mode 100644 index 0000000..9122192 --- /dev/null +++ b/src/Tests/AccessTokenCacheTest.php @@ -0,0 +1,130 @@ +set($key, 'cached-token', 3600); + $http = new FakeHttpClient(['access_token' => 'remote-token', 'expires_in' => 7200]); + + $token = (new Client(cache: $cache, http: $http)) + ->wechatPlatform(new WechatPlatformConfig('wx_app', 'secret')) + ->accessToken(); + + $this->assertSame('cached-token', $token); + $this->assertSame(0, $http->requests); + $this->assertSame(0, $cache->lockCalls); + } + + public function testAccessTokenRefreshUsesLockAndWritesCache(): void + { + $cache = new ArrayCacheStore(); + $http = new FakeHttpClient(['access_token' => 'remote-token', 'expires_in' => 7200]); + $platform = (new Client(cache: $cache, http: $http)) + ->wechatPlatform(new WechatPlatformConfig('wx_app', 'secret')); + + $this->assertSame('remote-token', $platform->accessToken()); + $this->assertSame(1, $http->requests); + $this->assertSame(1, $cache->lockCalls); + $this->assertSame('remote-token', $platform->accessToken()); + $this->assertSame(1, $http->requests); + } +} + +final class ArrayCacheStore implements StoreCacheInterface +{ + /** @var array */ + private array $values = []; + + public int $lockCalls = 0; + + public function get(string $key, mixed $default = null): mixed + { + return $this->values[$key] ?? $default; + } + + public function set(string $key, mixed $value, int $ttl): void + { + $this->values[$key] = $value; + } + + public function del(string $key): void + { + unset($this->values[$key]); + } + + public function lock(string $key, int $ttl, callable $callback): mixed + { + ++$this->lockCalls; + + return $callback(); + } +} + +final class FakeHttpClient implements ClientInterface +{ + public int $requests = 0; + + /** + * @param array $payload + */ + public function __construct(private readonly array $payload) {} + + public function send(RequestInterface $request, array $options = []): ResponseInterface + { + return $this->response(); + } + + public function sendAsync(RequestInterface $request, array $options = []): PromiseInterface + { + return Create::rejectionFor(new \RuntimeException('sendAsync is not used in this test')); + } + + public function request(string $method, $uri = '', array $options = []): ResponseInterface + { + ++$this->requests; + + return $this->response(); + } + + public function requestAsync(string $method, $uri = '', array $options = []): PromiseInterface + { + return Create::rejectionFor(new \RuntimeException('requestAsync is not used in this test')); + } + + public function getConfig(?string $option = null): mixed + { + return null; + } + + private function response(): ResponseInterface + { + return new Response(200, [], json_encode($this->payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: '{}'); + } +} diff --git a/src/Tests/CacheKeyTest.php b/src/Tests/CacheKeyTest.php new file mode 100644 index 0000000..19ce736 --- /dev/null +++ b/src/Tests/CacheKeyTest.php @@ -0,0 +1,49 @@ +assertSame('myapp:wechat.platform:' . $logical, $full); + } + + public function testComposeTrimsColonNoiseOnSegments(): void + { + $logical = 'wechat:app:z:official:access_token'; + $this->assertSame('ns:wechat.service:' . $logical, CacheKey::compose('::ns::', ':::wechat.service::', $logical)); + } + + public function testComposeThrowsWhenPrefixEmpty(): void + { + $this->expectException(WechatException::class); + $this->expectExceptionMessage('通用前缀'); + CacheKey::compose('', 'wechat.wxapp', 'wechat:app:x:mini:access_token'); + } + + public function testComposeThrowsWhenChannelEmpty(): void + { + $this->expectException(WechatException::class); + $this->expectExceptionMessage('通道段'); + CacheKey::compose('app', '', 'wechat:app:x:mini:access_token'); + } + + public function testComposeThrowsWhenLogicalEmpty(): void + { + $this->expectException(WechatException::class); + $this->expectExceptionMessage('逻辑段'); + CacheKey::compose('app', 'wechat.platform', ' '); + } +} diff --git a/src/Tests/CacheStoreTest.php b/src/Tests/CacheStoreTest.php new file mode 100644 index 0000000..b54973b --- /dev/null +++ b/src/Tests/CacheStoreTest.php @@ -0,0 +1,162 @@ +tempDir(); + $store = new FileCacheStore($dir); + + $this->assertSame('missing', $store->get('k1', 'missing')); + $store->set('k1', 'value', 3600); + $this->assertSame('value', $store->get('k1')); + $store->del('k1'); + $this->assertSame('missing', $store->get('k1', 'missing')); + + $store->set('k2', 'expired', -10); + sleep(2); + $this->assertSame('missing', $store->get('k2', 'missing')); + + $this->removeDir($dir); + } + + public function testFileCacheStoreLockExecutesCallback(): void + { + $dir = $this->tempDir(); + $store = new FileCacheStore($dir); + + $result = $store->lock('lock:k1', 10, static fn (): string => 'locked'); + + $this->assertSame('locked', $result); + $this->removeDir($dir); + } + + public function testNullCacheStoreLockExecutesCallback(): void + { + $store = new NullCacheStore(); + + $this->assertSame('ok', $store->lock('k', 10, static fn (): string => 'ok')); + } + + public function testPsrSimpleCacheStoreThrowsWhenLockerMissing(): void + { + $store = new PsrSimpleCacheStore(new ArraySimpleCache()); + + $this->expectException(WechatException::class); + $this->expectExceptionMessage('锁能力'); + + $store->lock('k', 10, static fn (): string => 'never'); + } + + public function testPsrSimpleCacheStoreUsesInjectedLocker(): void + { + $calls = []; + $store = new PsrSimpleCacheStore(new ArraySimpleCache(), static function (string $key, int $ttl, callable $callback) use (&$calls): mixed { + $calls[] = [$key, $ttl]; + + return $callback(); + }); + + $this->assertSame('ok', $store->lock('k', 10, static fn (): string => 'ok')); + $this->assertSame([['k', 10]], $calls); + } + + private function tempDir(): string + { + return sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'wechatdev_cache_' . bin2hex(random_bytes(8)); + } + + private function removeDir(string $dir): void + { + if (!is_dir($dir)) { + return; + } + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS), + \RecursiveIteratorIterator::CHILD_FIRST, + ); + foreach ($iterator as $file) { + $path = $file->getPathname(); + $file->isDir() ? @rmdir($path) : @unlink($path); + } + @rmdir($dir); + } +} + +final class ArraySimpleCache implements CacheInterface +{ + /** @var array */ + private array $values = []; + + public function get(string $key, mixed $default = null): mixed + { + return $this->values[$key] ?? $default; + } + + public function set(string $key, mixed $value, null|int|DateInterval $ttl = null): bool + { + $this->values[$key] = $value; + + return true; + } + + public function delete(string $key): bool + { + unset($this->values[$key]); + + return true; + } + + public function clear(): bool + { + $this->values = []; + + return true; + } + + public function getMultiple(iterable $keys, mixed $default = null): iterable + { + foreach ($keys as $key) { + yield $key => $this->get((string)$key, $default); + } + } + + public function setMultiple(iterable $values, null|int|DateInterval $ttl = null): bool + { + foreach ($values as $key => $value) { + $this->set((string)$key, $value, $ttl); + } + + return true; + } + + public function deleteMultiple(iterable $keys): bool + { + foreach ($keys as $key) { + $this->delete((string)$key); + } + + return true; + } + + public function has(string $key): bool + { + return array_key_exists($key, $this->values); + } +} diff --git a/src/Tests/ClientTest.php b/src/Tests/ClientTest.php new file mode 100644 index 0000000..f693db7 --- /dev/null +++ b/src/Tests/ClientTest.php @@ -0,0 +1,98 @@ +expectException(WechatException::class); + $this->expectExceptionMessage('cacheKeyPrefix'); + new Client(cacheKeyPrefix: ' '); + } + + public function testDefaultCacheStoreDirectoryUnderSysTemp(): void + { + $dir = Client::defaultCacheStoreDirectory(); + $this->assertStringStartsWith(rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR . '/'), $dir); + $this->assertStringEndsWith(Client::DEFAULT_CACHE_STORE_DIR_NAME, $dir); + } + + public function testGetThrowsWhenChannelUnsupported(): void + { + $client = new Client(); + + $this->expectException(WechatException::class); + $this->expectExceptionMessage('不支持的通道标识'); + $client->get('unknown.channel', new WechatPlatformConfig('wx_x', 'sec')); + } + + public function testGetThrowsWhenConfigMismatch(): void + { + $client = new Client(); + + $this->expectException(WechatException::class); + $this->expectExceptionMessage('WechatPlatformConfig'); + $client->get('wechat.platform', new WechatServiceConfig('app', 'sec', 'token', 'encoding')); + } + + public function testWechatPlatformFactoryReturnsTypedClient(): void + { + $client = new Client(); + $wechat = $client->wechatPlatform(new WechatPlatformConfig('wx_appid', 'app_secret')); + + $this->assertInstanceOf(WechatPlatformClient::class, $wechat); + } + + public function testServiceClientAuthorizationUrlStillAvailable(): void + { + $client = new Client(); + $service = $client->wechatService(new WechatServiceConfig( + 'wx_component', + 'component_secret', + 'component_token', + 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG' + )); + $result = $service->authorizationUrl('preauthcode', 'https://example.com/callback', 3, 'STATE_TEST'); + + $this->assertStringContainsString('componentloginpage', $result); + $this->assertStringContainsString('pre_auth_code=preauthcode', $result); + } + + public function testAlipayPlatformCallReturnsAuthorizationUrl(): void + { + $client = new Client(); + $alipay = $client->alipayPlatform(new AlipayPlatformConfig('202605010001', str_repeat('a', 64))); + $result = $alipay->get('auth', [ + 'redirect_uri' => 'https://example.com/alipay/callback', + 'scope' => 'auth_user', + 'state' => 'S2', + ]); + + $this->assertArrayHasKey('url', $result); + $this->assertStringContainsString('state=S2', (string)$result['url']); + } + + public function testGetCanReturnSpecificChannelClient(): void + { + $client = new Client(); + $channelClient = $client->get('alipay.platform', new AlipayPlatformConfig('202605010001', str_repeat('a', 64))); + + $this->assertInstanceOf(AlipayPlatformClient::class, $channelClient); + $this->assertNotInstanceOf(WechatServiceClient::class, $channelClient); + } +} diff --git a/src/Tests/ConfigInterfaceTest.php b/src/Tests/ConfigInterfaceTest.php new file mode 100644 index 0000000..e3d4abc --- /dev/null +++ b/src/Tests/ConfigInterfaceTest.php @@ -0,0 +1,60 @@ +assertInstanceOf(ConfigInterface::class, $config); + } + } + + public function testConfigValidateRequiredFieldsOnConstruct(): void + { + $this->expectException(WechatException::class); + $this->expectExceptionMessage('不能为空'); + + new WechatPlatformConfig('', 'secret'); + } + + public function testConfigValidateRequiredFieldsFromArray(): void + { + $this->expectException(WechatException::class); + $this->expectExceptionMessage('不能为空'); + + WechatPaymentConfig::fromArray(['appid' => 'wx_app']); + } + + public function testAlipayPaymentFromArrayReturnsChildClass(): void + { + $config = AlipayPaymentConfig::fromArray([ + 'appid' => 'ali_pay', + 'private_key' => 'private-key', + ]); + + $this->assertInstanceOf(AlipayPaymentConfig::class, $config); + } +} diff --git a/src/Tests/MessageCryptoTest.php b/src/Tests/MessageCryptoTest.php new file mode 100644 index 0000000..271ef2f --- /dev/null +++ b/src/Tests/MessageCryptoTest.php @@ -0,0 +1,33 @@ +'; + $timestamp = '1777600000'; + $nonce = 'nonce'; + + $encrypted = $crypto->encryptMessage($xml, $timestamp, $nonce); + preg_match('/<\/Encrypt>/', $encrypted, $matches); + $this->assertNotEmpty($matches[1] ?? ''); + $signature = Signature::sha1(['token', $timestamp, $nonce, $matches[1]]); + + $plain = $crypto->decryptMessage($encrypted, $signature, $timestamp, $nonce); + + $this->assertSame('text', $plain['MsgType']); + $this->assertSame('hello', $plain['Content']); + } +} diff --git a/src/Tests/PayCryptoTest.php b/src/Tests/PayCryptoTest.php new file mode 100644 index 0000000..0ea344c --- /dev/null +++ b/src/Tests/PayCryptoTest.php @@ -0,0 +1,31 @@ + base64_encode($cipher . $tag), + 'nonce' => $nonce, + 'associated_data' => $aad, + ]); + + $this->assertSame('T202605010001', $data['out_trade_no']); + $this->assertSame('SUCCESS', $data['trade_state']); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..40963a2 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,27 @@ +