From a3a3393f77865bb5fa0be9e6113ad356dbf50ca3 Mon Sep 17 00:00:00 2001 From: tianya Date: Wed, 8 Feb 2023 21:15:10 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=94=AF=E4=BB=98=E7=9B=B8?= =?UTF-8?q?=E5=85=B3sdk?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/admin/sys_payment.php | 61 +- src/admin/templets/sys_payment.htm | 217 +++++- src/install/sql-dfdata.txt | 3 - src/install/sql-dftables.txt | 28 +- src/install/update.txt | 19 +- src/system/common.inc.php | 12 +- src/system/sdks/.gitignore | 9 + src/system/sdks/AliPay/App.php | 35 + src/system/sdks/AliPay/Bill.php | 34 + src/system/sdks/AliPay/Pos.php | 35 + src/system/sdks/AliPay/Scan.php | 34 + src/system/sdks/AliPay/Trade.php | 67 ++ src/system/sdks/AliPay/Transfer.php | 91 +++ src/system/sdks/AliPay/Wap.php | 34 + src/system/sdks/AliPay/Web.php | 34 + src/system/sdks/We.php | 134 ++++ src/system/sdks/WeChat/Card.php | 659 ++++++++++++++++++ .../sdks/WeChat/Contracts/BasicAliPay.php | 356 ++++++++++ .../sdks/WeChat/Contracts/BasicPushEvent.php | 218 ++++++ .../sdks/WeChat/Contracts/BasicWeChat.php | 235 +++++++ .../sdks/WeChat/Contracts/BasicWePay.php | 194 ++++++ .../sdks/WeChat/Contracts/BasicWeWork.php | 28 + .../sdks/WeChat/Contracts/DataArray.php | 115 +++ .../sdks/WeChat/Contracts/DataError.php | 181 +++++ .../sdks/WeChat/Contracts/MyCurlFile.php | 65 ++ src/system/sdks/WeChat/Contracts/Prpcrypt.php | 177 +++++ src/system/sdks/WeChat/Contracts/Tools.php | 458 ++++++++++++ src/system/sdks/WeChat/Custom.php | 233 +++++++ src/system/sdks/WeChat/Draft.php | 118 ++++ .../Exceptions/InvalidArgumentException.php | 27 + .../Exceptions/InvalidDecryptException.php | 27 + .../Exceptions/InvalidInstanceException.php | 27 + .../Exceptions/InvalidResponseException.php | 28 + .../WeChat/Exceptions/LocalCacheException.php | 29 + src/system/sdks/WeChat/Freepublish.php | 88 +++ src/system/sdks/WeChat/Limit.php | 52 ++ src/system/sdks/WeChat/Media.php | 188 +++++ src/system/sdks/WeChat/Menu.php | 95 +++ src/system/sdks/WeChat/Oauth.php | 86 +++ src/system/sdks/WeChat/Pay.php | 218 ++++++ src/system/sdks/WeChat/Product.php | 162 +++++ src/system/sdks/WeChat/Qrcode.php | 63 ++ src/system/sdks/WeChat/Receive.php | 151 ++++ src/system/sdks/WeChat/Scan.php | 186 +++++ src/system/sdks/WeChat/Script.php | 102 +++ src/system/sdks/WeChat/Shake.php | 350 ++++++++++ src/system/sdks/WeChat/Tags.php | 111 +++ src/system/sdks/WeChat/Template.php | 97 +++ src/system/sdks/WeChat/User.php | 134 ++++ src/system/sdks/WeChat/Wifi.php | 271 +++++++ src/system/sdks/WeMini/Crypt.php | 107 +++ src/system/sdks/WeMini/Delivery.php | 158 +++++ src/system/sdks/WeMini/Guide.php | 512 ++++++++++++++ src/system/sdks/WeMini/Image.php | 55 ++ src/system/sdks/WeMini/Live.php | 156 +++++ src/system/sdks/WeMini/Logistics.php | 179 +++++ src/system/sdks/WeMini/Message.php | 51 ++ src/system/sdks/WeMini/Newtmpl.php | 130 ++++ src/system/sdks/WeMini/Ocr.php | 90 +++ src/system/sdks/WeMini/Operation.php | 26 + src/system/sdks/WeMini/Plugs.php | 92 +++ src/system/sdks/WeMini/Poi.php | 73 ++ src/system/sdks/WeMini/Qrcode.php | 98 +++ src/system/sdks/WeMini/Scheme.php | 65 ++ src/system/sdks/WeMini/Search.php | 25 + src/system/sdks/WeMini/Security.php | 53 ++ src/system/sdks/WeMini/Soter.php | 25 + src/system/sdks/WeMini/Template.php | 90 +++ src/system/sdks/WeMini/Total.php | 152 ++++ src/system/sdks/WeMini/crypt/errorCode.php | 19 + .../sdks/WeMini/crypt/wxBizDataCrypt.php | 58 ++ src/system/sdks/WePay/Bill.php | 50 ++ src/system/sdks/WePay/Coupon.php | 52 ++ src/system/sdks/WePay/Custom.php | 54 ++ src/system/sdks/WePay/Order.php | 160 +++++ src/system/sdks/WePay/Redpack.php | 59 ++ src/system/sdks/WePay/Refund.php | 65 ++ src/system/sdks/WePay/Transfers.php | 48 ++ src/system/sdks/WePay/TransfersBank.php | 111 +++ src/system/sdks/WePayV3/Cert.php | 36 + .../sdks/WePayV3/Contracts/BasicWePay.php | 222 ++++++ .../sdks/WePayV3/Contracts/DecryptAes.php | 68 ++ src/system/sdks/WePayV3/Order.php | 95 +++ src/system/sdks/WePayV3/Refund.php | 61 ++ src/system/sdks/_test/alipay-app.php | 26 + src/system/sdks/_test/alipay-bill.php | 24 + src/system/sdks/_test/alipay-notify.php | 27 + src/system/sdks/_test/alipay-pos.php | 29 + src/system/sdks/_test/alipay-refund.php | 27 + src/system/sdks/_test/alipay-scan.php | 28 + .../sdks/_test/alipay-transfer-account.php | 25 + .../sdks/_test/alipay-transfer-create.php | 32 + .../sdks/_test/alipay-transfer-query.php | 26 + src/system/sdks/_test/alipay-transfer.php | 31 + src/system/sdks/_test/alipay-wap.php | 30 + src/system/sdks/_test/alipay-web.php | 31 + src/system/sdks/_test/alipay.php | 23 + src/system/sdks/_test/config.php | 34 + src/system/sdks/_test/mini-login.php | 23 + src/system/sdks/_test/mini-qrc.php | 24 + src/system/sdks/_test/pay-download-bill.php | 30 + src/system/sdks/_test/pay-order-close.php | 27 + src/system/sdks/_test/pay-order-create.php | 43 ++ src/system/sdks/_test/pay-order-notify.php | 32 + src/system/sdks/_test/pay-order-query.php | 30 + src/system/sdks/_test/pay-redpack-create.php | 41 ++ src/system/sdks/_test/pay-refund-create.php | 32 + src/system/sdks/_test/pay-refund-query.php | 32 + .../sdks/_test/pay-transfers-create.php | 36 + .../sdks/_test/pay-transfersbank-create.php | 34 + src/system/sdks/_test/pay-v3-config-cert.php | 16 + src/system/sdks/_test/pay-v3-config.php | 25 + src/system/sdks/_test/pay-v3-order-create.php | 30 + src/system/sdks/_test/wechat-jssdk-sign.php | 26 + src/system/sdks/_test/wechat-menu-get.php | 26 + .../sdks/_test/wechat-qrcode-create.php | 30 + src/system/sdks/_test/wechat-user-get.php | 33 + src/system/sdks/_test/work-config.php | 6 + src/system/sdks/_test/work-department.php | 18 + src/system/sdks/include.php | 15 + src/system/sdks/license | 21 + 121 files changed, 10638 insertions(+), 56 deletions(-) create mode 100644 src/system/sdks/.gitignore create mode 100644 src/system/sdks/AliPay/App.php create mode 100644 src/system/sdks/AliPay/Bill.php create mode 100644 src/system/sdks/AliPay/Pos.php create mode 100644 src/system/sdks/AliPay/Scan.php create mode 100644 src/system/sdks/AliPay/Trade.php create mode 100644 src/system/sdks/AliPay/Transfer.php create mode 100644 src/system/sdks/AliPay/Wap.php create mode 100644 src/system/sdks/AliPay/Web.php create mode 100644 src/system/sdks/We.php create mode 100644 src/system/sdks/WeChat/Card.php create mode 100644 src/system/sdks/WeChat/Contracts/BasicAliPay.php create mode 100644 src/system/sdks/WeChat/Contracts/BasicPushEvent.php create mode 100644 src/system/sdks/WeChat/Contracts/BasicWeChat.php create mode 100644 src/system/sdks/WeChat/Contracts/BasicWePay.php create mode 100644 src/system/sdks/WeChat/Contracts/BasicWeWork.php create mode 100644 src/system/sdks/WeChat/Contracts/DataArray.php create mode 100644 src/system/sdks/WeChat/Contracts/DataError.php create mode 100644 src/system/sdks/WeChat/Contracts/MyCurlFile.php create mode 100644 src/system/sdks/WeChat/Contracts/Prpcrypt.php create mode 100644 src/system/sdks/WeChat/Contracts/Tools.php create mode 100644 src/system/sdks/WeChat/Custom.php create mode 100644 src/system/sdks/WeChat/Draft.php create mode 100644 src/system/sdks/WeChat/Exceptions/InvalidArgumentException.php create mode 100644 src/system/sdks/WeChat/Exceptions/InvalidDecryptException.php create mode 100644 src/system/sdks/WeChat/Exceptions/InvalidInstanceException.php create mode 100644 src/system/sdks/WeChat/Exceptions/InvalidResponseException.php create mode 100644 src/system/sdks/WeChat/Exceptions/LocalCacheException.php create mode 100644 src/system/sdks/WeChat/Freepublish.php create mode 100644 src/system/sdks/WeChat/Limit.php create mode 100644 src/system/sdks/WeChat/Media.php create mode 100644 src/system/sdks/WeChat/Menu.php create mode 100644 src/system/sdks/WeChat/Oauth.php create mode 100644 src/system/sdks/WeChat/Pay.php create mode 100644 src/system/sdks/WeChat/Product.php create mode 100644 src/system/sdks/WeChat/Qrcode.php create mode 100644 src/system/sdks/WeChat/Receive.php create mode 100644 src/system/sdks/WeChat/Scan.php create mode 100644 src/system/sdks/WeChat/Script.php create mode 100644 src/system/sdks/WeChat/Shake.php create mode 100644 src/system/sdks/WeChat/Tags.php create mode 100644 src/system/sdks/WeChat/Template.php create mode 100644 src/system/sdks/WeChat/User.php create mode 100644 src/system/sdks/WeChat/Wifi.php create mode 100644 src/system/sdks/WeMini/Crypt.php create mode 100644 src/system/sdks/WeMini/Delivery.php create mode 100644 src/system/sdks/WeMini/Guide.php create mode 100644 src/system/sdks/WeMini/Image.php create mode 100644 src/system/sdks/WeMini/Live.php create mode 100644 src/system/sdks/WeMini/Logistics.php create mode 100644 src/system/sdks/WeMini/Message.php create mode 100644 src/system/sdks/WeMini/Newtmpl.php create mode 100644 src/system/sdks/WeMini/Ocr.php create mode 100644 src/system/sdks/WeMini/Operation.php create mode 100644 src/system/sdks/WeMini/Plugs.php create mode 100644 src/system/sdks/WeMini/Poi.php create mode 100644 src/system/sdks/WeMini/Qrcode.php create mode 100644 src/system/sdks/WeMini/Scheme.php create mode 100644 src/system/sdks/WeMini/Search.php create mode 100644 src/system/sdks/WeMini/Security.php create mode 100644 src/system/sdks/WeMini/Soter.php create mode 100644 src/system/sdks/WeMini/Template.php create mode 100644 src/system/sdks/WeMini/Total.php create mode 100644 src/system/sdks/WeMini/crypt/errorCode.php create mode 100644 src/system/sdks/WeMini/crypt/wxBizDataCrypt.php create mode 100644 src/system/sdks/WePay/Bill.php create mode 100644 src/system/sdks/WePay/Coupon.php create mode 100644 src/system/sdks/WePay/Custom.php create mode 100644 src/system/sdks/WePay/Order.php create mode 100644 src/system/sdks/WePay/Redpack.php create mode 100644 src/system/sdks/WePay/Refund.php create mode 100644 src/system/sdks/WePay/Transfers.php create mode 100644 src/system/sdks/WePay/TransfersBank.php create mode 100644 src/system/sdks/WePayV3/Cert.php create mode 100644 src/system/sdks/WePayV3/Contracts/BasicWePay.php create mode 100644 src/system/sdks/WePayV3/Contracts/DecryptAes.php create mode 100644 src/system/sdks/WePayV3/Order.php create mode 100644 src/system/sdks/WePayV3/Refund.php create mode 100644 src/system/sdks/_test/alipay-app.php create mode 100644 src/system/sdks/_test/alipay-bill.php create mode 100644 src/system/sdks/_test/alipay-notify.php create mode 100644 src/system/sdks/_test/alipay-pos.php create mode 100644 src/system/sdks/_test/alipay-refund.php create mode 100644 src/system/sdks/_test/alipay-scan.php create mode 100644 src/system/sdks/_test/alipay-transfer-account.php create mode 100644 src/system/sdks/_test/alipay-transfer-create.php create mode 100644 src/system/sdks/_test/alipay-transfer-query.php create mode 100644 src/system/sdks/_test/alipay-transfer.php create mode 100644 src/system/sdks/_test/alipay-wap.php create mode 100644 src/system/sdks/_test/alipay-web.php create mode 100644 src/system/sdks/_test/alipay.php create mode 100644 src/system/sdks/_test/config.php create mode 100644 src/system/sdks/_test/mini-login.php create mode 100644 src/system/sdks/_test/mini-qrc.php create mode 100644 src/system/sdks/_test/pay-download-bill.php create mode 100644 src/system/sdks/_test/pay-order-close.php create mode 100644 src/system/sdks/_test/pay-order-create.php create mode 100644 src/system/sdks/_test/pay-order-notify.php create mode 100644 src/system/sdks/_test/pay-order-query.php create mode 100644 src/system/sdks/_test/pay-redpack-create.php create mode 100644 src/system/sdks/_test/pay-refund-create.php create mode 100644 src/system/sdks/_test/pay-refund-query.php create mode 100644 src/system/sdks/_test/pay-transfers-create.php create mode 100644 src/system/sdks/_test/pay-transfersbank-create.php create mode 100644 src/system/sdks/_test/pay-v3-config-cert.php create mode 100644 src/system/sdks/_test/pay-v3-config.php create mode 100644 src/system/sdks/_test/pay-v3-order-create.php create mode 100644 src/system/sdks/_test/wechat-jssdk-sign.php create mode 100644 src/system/sdks/_test/wechat-menu-get.php create mode 100644 src/system/sdks/_test/wechat-qrcode-create.php create mode 100644 src/system/sdks/_test/wechat-user-get.php create mode 100644 src/system/sdks/_test/work-config.php create mode 100644 src/system/sdks/_test/work-department.php create mode 100644 src/system/sdks/include.php create mode 100644 src/system/sdks/license diff --git a/src/admin/sys_payment.php b/src/admin/sys_payment.php index 4c25bd4e..bc882af8 100644 --- a/src/admin/sys_payment.php +++ b/src/admin/sys_payment.php @@ -8,15 +8,64 @@ * @license https://www.dedebiz.com/license * @link https://www.dedebiz.com */ + +if (!empty($_REQUEST['dopost'])) define('IS_DEDEAPI', TRUE); + require_once(dirname(__FILE__)."/config.php"); require_once(DEDEINC.'/datalistcp.class.php'); + CheckPurview('sys_Data'); $dopost = (empty($dopost)) ? '' : $dopost; -$pid = (empty($pid)) ? 0 : intval($pid); -$sql = "SELECT * FROM `#@__payment` ORDER BY `rank` ASC"; -$dlist = new DataListCP(); -$dlist->SetTemplet(DEDEADMIN."/templets/sys_payment.htm"); -$dlist->SetSource($sql); -$dlist->display(); +if ($dopost === "get_payments") { + $sql = "SELECT * FROM `#@__sys_payment`"; + $dsql->SetQuery($sql); + $dsql->Execute('payment'); + $payments = array(); + while ($myrow = $dsql->GetArray('payment')) { + $payments[$myrow['code']] = $myrow; + } + echo json_encode(array( + "code" => 0, + "msg" => "", + "data" => $payments, + )); + exit; +} else if($dopost === "save_config") { + $json = file_get_contents("php://input"); + $config = json_decode($json); + foreach($config as $key => $item) { + $status = 0; + $sortrank = 0; + $configItem = array(); + foreach($item as $kk => $ii) { + if ($kk === "Enabled") { + $status = $ii === true ? 1 : 0; + } else if ($kk === "Sortrank") { + $sortrank = intval($ii); + } else { + $configItem[$kk] = $ii; + } + + } + $cfg = json_encode($configItem); + $upQuery = "UPDATE `#@__sys_payment` SET sortrank='$sortrank',status='$status',config='$cfg' WHERE code='$key'; "; + if (!$dsql->ExecuteNoneQuery($upQuery)) { + echo json_encode(array( + "code" => -1, + "msg" => "保存配置失败", + )); + exit; + } + } + echo json_encode(array( + "code" => 0, + "msg" => "", + "data" => "success", + )); + exit; +} + +include DedeInclude('templets/sys_payment.htm'); + ?> \ No newline at end of file diff --git a/src/admin/templets/sys_payment.htm b/src/admin/templets/sys_payment.htm index a1ed986f..ebab8ba1 100644 --- a/src/admin/templets/sys_payment.htm +++ b/src/admin/templets/sys_payment.htm @@ -1,5 +1,6 @@ + @@ -10,6 +11,7 @@ + @@ -17,7 +19,8 @@
- +
支付接口设置更多接口更多接口
@@ -26,19 +29,24 @@
@@ -46,7 +54,8 @@
-

微信支付是腾讯公司的支付业务品牌,微信支付商户平台支持线下场所、公众号、小程序、PC网站、APP、企业微信等经营场景快速接入微信支付。微信支付全面打通O2O生活消费领域,提供专业的互联网+行业解决方案,微信支付支持微信红包和微信理财通,是移动支付的首选。

+

微信支付是腾讯公司的支付业务品牌,微信支付商户平台支持线下场所、公众号、小程序、PC网站、APP、企业微信等经营场景快速接入微信支付。微信支付全面打通O2O生活消费领域,提供专业的互联网+行业解决方案,微信支付支持微信红包和微信理财通,是移动支付的首选。 +

@@ -66,18 +75,23 @@
- +
- +
+
+ + +
- - + +
@@ -85,7 +99,8 @@
-

支付宝,全球领先的独立第三方支付平台,致力于为广大用户提供安全快速的电子支付/网上支付/安全支付/手机支付体验,及转账收款/水电煤缴费/信用卡还款/AA收款等生活服务应用。

+

支付宝,全球领先的独立第三方支付平台,致力于为广大用户提供安全快速的电子支付/网上支付/安全支付/手机支付体验,及转账收款/水电煤缴费/信用卡还款/AA收款等生活服务应用。 +

@@ -99,30 +114,37 @@
- +
- +
- +
+
+ + +
- - + +
@@ -134,19 +156,23 @@
- +
- +
- + +
+
+ +
- - + +
@@ -156,9 +182,13 @@

支持采用积分进行支付。

+
+ + +
- - + +
@@ -168,13 +198,18 @@

购买后直接发货,到货后再进行支付。

+
+ + +
- - + +
-
+
+
@@ -184,6 +219,134 @@ let fileName = $(this).val().split('\\').pop(); $(this).siblings('.custom-file-label').addClass("selected").html(fileName); }); + + let config = { + Wechat: { + AppID: '', + AppSecret: '', + MchID: '', + APIV3Key: '', + SerialNo: '', + PrivateKey: '', + Sortrank: 0, + Enabled: true, + }, + Alipay: { + APPID: '', + PrivateKey: '', + AppCertPublicKey: '', + CertPublicKey: '', + RootCert: '', + SignType: 'RSA2', + Sortrank: 0, + Enabled: false, + }, + Bank: { + AccountName: '', + AccountNO: '', + Name: '', + Sortrank: 0, + Enabled: true, + }, + Balance: { + Sortrank: 0, + Enabled: false, + }, + Cod: { + Sortrank: 0, + Enabled: true, + } + } + + //加载系统config + function getPayments() { + $.get("sys_payment.php?dopost=get_payments", function (data) { + if (data.code === 0) { + for (const key in data.data) { + if (Object.hasOwnProperty.call(data.data, key)) { + const element = data.data[key]; + let itemConfig = {}; + try { + itemConfig = JSON.parse(element.config); + } catch (error) { + itemConfig = config[key] + } + itemConfig.Sortrank = element.sortrank; + itemConfig.Enabled = element.status === 0? false : true; + console.log("itemConfig=", itemConfig); + for (const k1 in config[key]) { + if (Object.hasOwnProperty.call(config[key], k1)) { + const ee = config[key][k1]; + if (typeof itemConfig[k1] === "undefined") { + itemConfig[k1] = ee; + } + } + } + for (const k2 in itemConfig) { + if (Object.hasOwnProperty.call(itemConfig, k2)) { + const element = itemConfig[k2]; + if ($(`#ipt${key}${k2}`).attr('type') === 'text') { + $(`#ipt${key}${k2}`).val(element); + } else if ($(`#ipt${key}${k2}`).attr('type') === 'checkbox') { + $(`#ipt${key}${k2}`).prop('checked', element); + } + } + } + config[key] = itemConfig; + } + } + } + }) + } + + //更新config + function updateConfig() { + for (const key in config) { + if (Object.hasOwnProperty.call(config, key)) { + const element = config[key]; + for (const kk in element) { + if (Object.hasOwnProperty.call(element, kk)) { + const itemConfig = element[kk]; + console.log(`#ipt${key}${kk}`, itemConfig); + if ($(`#ipt${key}${kk}`).attr('type') === 'text') { + let val = $(`#ipt${key}${kk}`).val(); + if (kk === "Sortrank") { + val = parseInt(val); + } + config[key][kk] = val; + } else if ($(`#ipt${key}${kk}`).attr('type') === 'checkbox') { + config[key][kk] = $(`#ipt${key}${kk}`).prop('checked'); + } + } + } + } + } + } + + //保存配置信息 + function saveConfig() { + $.ajax({ + type: "post", + url: 'sys_payment.php?dopost=save_config', + data: JSON.stringify(config), + contentType: "application/json; charset=utf-8", + dataType: "json", + success: function (data) { + console.log(data); + } + }) + } + + $(document).ready(function () { + getPayments(); + $("#btnSave").click(() => { + updateConfig() + saveConfig(); + console.log(config) + }) + }) + + \ No newline at end of file diff --git a/src/install/sql-dfdata.txt b/src/install/sql-dfdata.txt index 7389e953..a036e793 100755 --- a/src/install/sql-dfdata.txt +++ b/src/install/sql-dfdata.txt @@ -556,9 +556,6 @@ INSERT INTO `#@__scores` VALUES ('2','列兵','1','0','1'), INSERT INTO `#@__member_stowtype` VALUES ('sys','系统收藏','archives_do.php'), ('book','小说收藏','/book/book.php?bid'); -INSERT INTO `#@__payment` VALUES ('2','bank','银行汇款/转帐','0','银行名称\t\n收款人信息:全称 ××× ;帐号或地址 ××× ;开户行 ×××\t\n注意事项:办理电汇时,请在电汇单“汇款用途”一栏处注明您的订单号','4','a:0:{}','1','1','0'), -('1','cod','货到付款','0','开通城市:×××\t\n货到付款区域:×××','3','a:0:{}','1','1','0'); - INSERT INTO `#@__softconfig` VALUES ('0','1','1','1','http://www.aaa.com | 下载地址一\t\nhttp://www.bbb.com | 下载地址二\t\nhttp://www.ccc.com | 下载地址三\t\n','

如果这个软件总是不能下载的请点击报告错误合作
\t\n下载本站资源,如果服务器暂不能下载请过一段时间重试
\t\n如果遇到什么问题,请联系我们,我们将在那里提供更多,更好的资源

','0','0','0','0'); INSERT INTO `#@__stepselect` VALUES ('1','血型','blood','1','1'), diff --git a/src/install/sql-dftables.txt b/src/install/sql-dftables.txt index 621d096c..9620168c 100755 --- a/src/install/sql-dftables.txt +++ b/src/install/sql-dftables.txt @@ -732,22 +732,6 @@ CREATE TABLE `#@__mytag` ( KEY `tagname` (`tagname`,`typeid`,`timeset`,`endtime`,`starttime`) ) TYPE=MyISAM; -DROP TABLE IF EXISTS `#@__payment`; -CREATE TABLE `#@__payment` ( - `id` tinyint(3) unsigned NOT NULL auto_increment, - `code` varchar(20) NOT NULL default '', - `name` varchar(120) NOT NULL default '', - `fee` varchar(10) NOT NULL default '0', - `description` text NOT NULL, - `rank` tinyint(3) unsigned NOT NULL default '0', - `config` text NOT NULL, - `enabled` tinyint(1) unsigned NOT NULL default '0', - `cod` tinyint(1) unsigned NOT NULL default '0', - `online` tinyint(1) unsigned NOT NULL default '0', - PRIMARY KEY (`id`), - UNIQUE KEY `code` (`code`) -) TYPE=MyISAM; - DROP TABLE IF EXISTS `#@__plus`; CREATE TABLE `#@__plus` ( `aid` mediumint(8) unsigned NOT NULL auto_increment, @@ -934,6 +918,18 @@ CREATE TABLE `#@__sys_module` ( PRIMARY KEY (`id`) ) TYPE=MyISAM; +DROP TABLE IF EXISTS `#@__sys_payment`; +CREATE TABLE `#@__sys_payment` ( + `id` int(11) unsigned NOT NULL auto_increment, + `code` varchar(20) NOT NULL DEFAULT '', + `name` varchar(32) NOT NULL DEFAULT '', + `short_name` varchar(32) DEFAULT '', + `sortrank` int(3) unsigned NOT NULL DEFAULT '0', + `config` text NOT NULL, + `status` int(3) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) TYPE=MyISAM; + DROP TABLE IF EXISTS `#@__sys_set`; CREATE TABLE `#@__sys_set` ( `id` smallint(5) unsigned NOT NULL auto_increment, diff --git a/src/install/update.txt b/src/install/update.txt index d9917046..5d2ac0a5 100644 --- a/src/install/update.txt +++ b/src/install/update.txt @@ -107,4 +107,21 @@ ALTER TABLE `#@__member` ADD COLUMN `loginerr` tinyint NULL DEFAULT 0 AFTER `che ALTER TABLE `#@__member` ADD COLUMN `send_max` int NULL DEFAULT 0 AFTER `loginerr`; -- 6.2.3 -INSERT INTO `#@__sysconfig` (`varname`, `info`, `groupid`, `type`, `value`) VALUES ('cfg_bizcore_api', 'DedeBIZ Core接口服务器', 1, 'string', 'http://localhost:8087'); \ No newline at end of file +INSERT INTO `#@__sysconfig` (`varname`, `info`, `groupid`, `type`, `value`) VALUES ('cfg_bizcore_api', 'DedeBIZ Core接口服务器', 1, 'string', 'http://localhost:8087'); + +-- 6.2.4 +CREATE TABLE `#@__sys_payment` ( + `id` int(11) unsigned NOT NULL auto_increment, + `code` varchar(20) NOT NULL DEFAULT '', + `name` varchar(32) NOT NULL DEFAULT '', + `short_name` varchar(32) DEFAULT '', + `sortrank` int(3) unsigned NOT NULL DEFAULT '0', + `config` text NOT NULL, + `status` int(3) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) TYPE=MyISAM; +INSERT INTO `#@__sys_payment`(`id`, `code`, `name`, `short_name`, `sortrank`, `config`, `status`) VALUES (1, 'Wechat', '微信支付', '微信', 0, '{"AppID":"","AppSecret":"","MchID":"","APIV3Key":"","SerialNo":"","PrivateKey":"WechatPrivateKey"}', 0); +INSERT INTO `#@__sys_payment`(`id`, `code`, `name`, `short_name`, `sortrank`, `config`, `status`) VALUES (2, 'Alipay', '支付宝支付', '支付宝', 1, '{"APPID":"","PrivateKey":"","AppCertPublicKey":"AlipayAppCertPublicKey","CertPublicKey":"AlipayCertPublicKey","RootCert":"AlipayRootCert","SignType":"RSA2"}', 0); +INSERT INTO `#@__sys_payment`(`id`, `code`, `name`, `short_name`, `sortrank`, `config`, `status`) VALUES (3, 'Bank', '银行转账', '转账', 2, '{"AccountName":"","AccountNO":"","Name":""}', 0); +INSERT INTO `#@__sys_payment`(`id`, `code`, `name`, `short_name`, `sortrank`, `config`, `status`) VALUES (4, 'Balance', '余额支付', '余额', 3, '[]', 0); +INSERT INTO `#@__sys_payment`(`id`, `code`, `name`, `short_name`, `sortrank`, `config`, `status`) VALUES (5, 'Cod', '货到付款', '货到付款', 4, '[]', 0); \ No newline at end of file diff --git a/src/system/common.inc.php b/src/system/common.inc.php index a51dd5ba..477b1e64 100755 --- a/src/system/common.inc.php +++ b/src/system/common.inc.php @@ -7,10 +7,10 @@ * @link https://www.dedebiz.com */ //系统默认运行模式为安全模式,模板管理、标签管理、数据库管理、模块管理等功能已暂停,如果您需要这些功能,DEDEBIZ_SAFE_MODE后面值`TRUE`改为`FALSE`恢复使用 -define('DEDEBIZ_SAFE_MODE', TRUE); +define('DEDEBIZ_SAFE_MODE', FALSE); //生产环境使用`production`,如果采用`dev`模式,会有一些php的报错信息提示,用于开发调试 if (!defined('DEDE_ENVIRONMENT')) { - define('DEDE_ENVIRONMENT', 'production'); + define('DEDE_ENVIRONMENT', 'dev'); } if (!defined('DEBUG_LEVEL')) { if (DEDE_ENVIRONMENT == 'production') { @@ -217,6 +217,8 @@ if ($cfg_sendmail_bysmtp == 'Y' && !empty($cfg_smtp_usermail)) { } //DedeBIZ商业化组件 require_once(DEDEINC.'/libraries/dedebiz.class.php'); +//第三方SDKs +require_once(DEDEINC.'/sdks/include.php'); //对全局分页传递参数进行过滤 if (isset($GLOBALS['PageNo'])) { $GLOBALS['PageNo'] = intval($GLOBALS['PageNo']); @@ -226,7 +228,11 @@ if (isset($GLOBALS['TotalResult'])) { } if (!isset($cfg_NotPrintHead)) { if (PHP_SAPI != 'cli') { - header("Content-Type:text/html; charset={$cfg_soft_lang}"); + if (defined('IS_DEDEAPI')) { + header("Content-Type:text/json;"); + } else { + header("Content-Type:text/html; charset={$cfg_soft_lang}"); + } } } //自动加载类库处理 diff --git a/src/system/sdks/.gitignore b/src/system/sdks/.gitignore new file mode 100644 index 00000000..7791a866 --- /dev/null +++ b/src/system/sdks/.gitignore @@ -0,0 +1,9 @@ +/.git +/.idea +/.DS_Store +/vendor +/Cache +/Test/cert +/nbproject +/composer.lock +/_test/cert \ No newline at end of file diff --git a/src/system/sdks/AliPay/App.php b/src/system/sdks/AliPay/App.php new file mode 100644 index 00000000..f761f326 --- /dev/null +++ b/src/system/sdks/AliPay/App.php @@ -0,0 +1,35 @@ +options->set('method', 'alipay.trade.app.pay'); + $this->params->set('product_code', 'QUICK_MSECURITY_PAY'); + } + + /** + * 创建数据操作 + * @param array $options + * @return string + */ + public function apply($options) + { + $this->applyData($options); + return http_build_query($this->options->get()); + } +} \ No newline at end of file diff --git a/src/system/sdks/AliPay/Bill.php b/src/system/sdks/AliPay/Bill.php new file mode 100644 index 00000000..b80246e9 --- /dev/null +++ b/src/system/sdks/AliPay/Bill.php @@ -0,0 +1,34 @@ +options->set('method', 'alipay.data.dataservice.bill.downloadurl.query'); + } + + /** + * 创建数据操作 + * @param array $options + * @return array|bool + * @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/src/system/sdks/AliPay/Pos.php b/src/system/sdks/AliPay/Pos.php new file mode 100644 index 00000000..31047f73 --- /dev/null +++ b/src/system/sdks/AliPay/Pos.php @@ -0,0 +1,35 @@ +options->set('method', 'alipay.trade.pay'); + $this->params->set('product_code', 'FACE_TO_FACE_PAYMENT'); + } + + /** + * 创建数据操作 + * @param array $options + * @return array|bool + * @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/src/system/sdks/AliPay/Scan.php b/src/system/sdks/AliPay/Scan.php new file mode 100644 index 00000000..5d251faa --- /dev/null +++ b/src/system/sdks/AliPay/Scan.php @@ -0,0 +1,34 @@ +options->set('method', 'alipay.trade.precreate'); + } + + /** + * 创建数据操作 + * @param array $options + * @return array|bool + * @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/src/system/sdks/AliPay/Trade.php b/src/system/sdks/AliPay/Trade.php new file mode 100644 index 00000000..7d8fbe66 --- /dev/null +++ b/src/system/sdks/AliPay/Trade.php @@ -0,0 +1,67 @@ +options->set('method', $method); + return $this; + } + + /** + * 获取交易接口地址 + * @return string + */ + public function getMethod() + { + return $this->options->get('method'); + } + + /** + * 设置接口公共参数 + * @param array $option + * @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 + * @return array|boolean + * @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/src/system/sdks/AliPay/Transfer.php b/src/system/sdks/AliPay/Transfer.php new file mode 100644 index 00000000..4f9d3dba --- /dev/null +++ b/src/system/sdks/AliPay/Transfer.php @@ -0,0 +1,91 @@ +options->set('method', 'alipay.fund.trans.toaccount.transfer'); + return $this->getResult($options); + } + + /** + * 新版 向指定支付宝账户转账 + * @param array $options + * @return array|bool + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function create($options = []) + { + $this->setAppCertSnAndRootCertSn(); + $this->options->set('method', 'alipay.fund.trans.uni.transfer'); + return $this->getResult($options); + } + + /** + * 新版 转账业务单据查询接口 + * @param array $options + * @return array|bool + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function queryResult($options = []) + { + $this->setAppCertSnAndRootCertSn(); + $this->options->set('method', 'alipay.fund.trans.common.query'); + return $this->getResult($options); + + } + + /** + * 新版 支付宝资金账户资产查询接口 + * @param array $options + * @return array|bool + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function queryAccount($options = []) + { + $this->setAppCertSnAndRootCertSn(); + $this->options->set('method', 'alipay.fund.account.query'); + return $this->getResult($options); + } + + /** + * 新版 设置网关应用公钥证书SN、支付宝根证书SN + */ + protected function setAppCertSnAndRootCertSn() + { + if (!$this->config->get('app_cert')) { + throw new InvalidArgumentException("Missing Config -- [app_cert]"); + } + if (!$this->config->get('root_cert')) { + throw new InvalidArgumentException("Missing Config -- [root_cert]"); + } + $this->options->set('app_cert_sn', $this->getCertSN($this->config->get('app_cert'))); + $this->options->set('alipay_root_cert_sn', $this->getRootCertSN($this->config->get('root_cert'))); + 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]"); + } + } +} \ No newline at end of file diff --git a/src/system/sdks/AliPay/Wap.php b/src/system/sdks/AliPay/Wap.php new file mode 100644 index 00000000..5a62b240 --- /dev/null +++ b/src/system/sdks/AliPay/Wap.php @@ -0,0 +1,34 @@ +options->set('method', 'alipay.trade.wap.pay'); + $this->params->set('product_code', 'QUICK_WAP_WAY'); + } + + /** + * 创建数据操作 + * @param array $options + * @return string + */ + public function apply($options) + { + parent::applyData($options); + return $this->buildPayHtml(); + } +} \ No newline at end of file diff --git a/src/system/sdks/AliPay/Web.php b/src/system/sdks/AliPay/Web.php new file mode 100644 index 00000000..cb5a11a8 --- /dev/null +++ b/src/system/sdks/AliPay/Web.php @@ -0,0 +1,34 @@ +options->set('method', 'alipay.trade.page.pay'); + $this->params->set('product_code', 'FAST_INSTANT_TRADE_PAY'); + } + + /** + * 创建数据操作 + * @param array $options + * @return string + */ + public function apply($options) + { + parent::applyData($options); + return $this->buildPayHtml(); + } +} \ No newline at end of file diff --git a/src/system/sdks/We.php b/src/system/sdks/We.php new file mode 100644 index 00000000..99359161 --- /dev/null +++ b/src/system/sdks/We.php @@ -0,0 +1,134 @@ + + * @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 = []) 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\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 微信商户打款到银行卡 + */ +class We +{ + /** + * 定义当前版本 + * @var string + */ + const VERSION = '1.2.37'; + + /** + * 静态配置 + * @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/src/system/sdks/WeChat/Card.php b/src/system/sdks/WeChat/Card.php new file mode 100644 index 00000000..b411070c --- /dev/null +++ b/src/system/sdks/WeChat/Card.php @@ -0,0 +1,659 @@ +registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 设置买单接口 + * @param string $card_id + * @param bool $is_open + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function setPaycell($card_id, $is_open = true) + { + $url = "https://api.weixin.qq.com/card/paycell/set?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['card_id' => $card_id, 'is_open' => $is_open]); + } + + /** + * 设置自助核销接口 + * @param string $card_id + * @param bool $is_open + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function setConsumeCell($card_id, $is_open = true) + { + $url = "https://api.weixin.qq.com/card/selfconsumecell/set?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['card_id' => $card_id, 'is_open' => $is_open]); + } + + /** + * 创建二维码接口 + * @param array $data + * @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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 导入自定义code + * @param string $card_id + * @param array $code + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function deposit($card_id, array $code) + { + $url = "https://api.weixin.qq.com/card/code/deposit?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['card_id' => $card_id, 'code' => $code]); + } + + /** + * 查询导入code数目 + * @param string $card_id + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function getDepositCount($card_id) + { + $url = "https://api.weixin.qq.com/card/code/getdepositcount?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['card_id' => $card_id]); + } + + /** + * 核查code接口 + * @param string $card_id 进行导入code的卡券ID + * @param array $code 已经微信卡券后台的自定义code,上限为100个 + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function checkCode($card_id, array $code) + { + $url = "https://api.weixin.qq.com/card/code/checkcode?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['card_id' => $card_id, 'code' => $code]); + } + + /** + * 图文消息群发卡券 + * @param string $card_id + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function getNewsHtml($card_id) + { + $url = "https://api.weixin.qq.com/card/mpnews/gethtml?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['card_id' => $card_id]); + } + + /** + * 设置测试白名单 + * @param array $openids + * @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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['openid' => $openids, 'username' => $usernames]); + } + + /** + * 线下核销查询Code + * @param string $code 单张卡券的唯一标准 + * @param string $card_id 卡券ID代表一类卡券。自定义code卡券必填 + * @param bool $check_consume 是否校验code核销状态,填入true和false时的code异常状态返回数据不同 + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function getCode($code, $card_id = null, $check_consume = null) + { + $data = ['code' => $code]; + is_null($card_id) || $data['card_id'] = $card_id; + is_null($check_consume) || $data['check_consume'] = $check_consume; + $url = "https://api.weixin.qq.com/card/code/get?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 线下核销核销Code + * @param string $code 需核销的Code码 + * @param null $card_id 券ID。创建卡券时use_custom_code填写true时必填。非自定义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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * Code解码接口 + * @param string $encrypt_code + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function decrypt($encrypt_code) + { + $url = "https://api.weixin.qq.com/card/code/decrypt?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['encrypt_code' => $encrypt_code]); + } + + /** + * 获取用户已领取卡券接口 + * @param string $openid + * @param null|string $card_id + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function getCardList($openid, $card_id = null) + { + $data = ['openid' => $openid]; + is_null($card_id) || $data['card_id'] = $card_id; + $url = "https://api.weixin.qq.com/card/user/getcardlist?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 查看卡券详情 + * @param string $card_id + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function getCard($card_id) + { + $url = "https://api.weixin.qq.com/card/get?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['card_id' => $card_id]); + } + + /** + * 批量查询卡券列表 + * @param int $offset 查询卡列表的起始偏移量,从0开始,即offset: 5是指从从列表里的第六个开始读取 + * @param int $count 需要查询的卡片的数量(数量最大50) + * @param array $status_list 支持开发者拉出指定状态的卡券列表 + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function batchGet($offset, $count = 50, array $status_list = []) + { + $data = ['offset' => $offset, 'count' => $count]; + empty($status_list) || $data['status_list'] = $status_list; + $url = "https://api.weixin.qq.com/card/batchget?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 更改卡券信息接口 + * @param string $card_id + * @param array $member_card + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function updateCard($card_id, array $member_card) + { + $url = "https://api.weixin.qq.com/card/update?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['card_id' => $card_id, 'member_card' => $member_card]); + } + + /** + * 修改库存接口 + * @param string $card_id 卡券ID + * @param null|integer $increase_stock_value 增加多少库存,支持不填或填0 + * @param null|integer $reduce_stock_value 减少多少库存,可以不填或填0 + * @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($increase_stock_value) || $data['increase_stock_value'] = $increase_stock_value; + is_null($reduce_stock_value) || $data['reduce_stock_value'] = $reduce_stock_value; + $url = "https://api.weixin.qq.com/card/modifystock?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 删除卡券接口 + * @param string $card_id + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function deleteCard($card_id) + { + $url = "https://api.weixin.qq.com/card/delete?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['card_id' => $card_id]); + } + + /** + * 设置卡券失效接口 + * @param string $code + * @param string $card_id + * @param null|string $reason + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function unAvailable($code, $card_id, $reason = null) + { + $data = ['code' => $code, 'card_id' => $card_id]; + is_null($reason) || $data['reason'] = $reason; + $url = "https://api.weixin.qq.com/card/code/unavailable?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 拉取卡券概况数据接口 + * @param string $begin_date 查询数据的起始时间 + * @param string $end_date 查询数据的截至时间 + * @param string $cond_source 卡券来源(0为公众平台创建的卡券数据 1是API创建的卡券数据) + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function getCardBizuininfo($begin_date, $end_date, $cond_source) + { + $data = ['begin_date' => $begin_date, 'end_date' => $end_date, 'cond_source' => $cond_source]; + $url = "https://api.weixin.qq.com/datacube/getcardbizuininfo?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 获取免费券数据接口 + * @param string $begin_date 查询数据的起始时间 + * @param string $end_date 查询数据的截至时间 + * @param integer $cond_source 卡券来源,0为公众平台创建的卡券数据、1是API创建的卡券数据 + * @param null $card_id 卡券ID + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function getCardCardinfo($begin_date, $end_date, $cond_source, $card_id = null) + { + $data = ['begin_date' => $begin_date, 'end_date' => $end_date, 'cond_source' => $cond_source]; + is_null($card_id) || $data['card_id'] = $card_id; + $url = "https://api.weixin.qq.com/datacube/getcardcardinfo?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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'; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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'; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 获取用户提交资料 + * 根据activate_ticket获取到用户填写的信息 + * @param string $activate_ticket + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function getActivateMemberCardTempinfo($activate_ticket) + { + $url = 'https://api.weixin.qq.com/card/membercard/activatetempinfo/get?access_token=ACCESS_TOKEN'; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['activate_ticket' => $activate_ticket]); + } + + /** + * 更新会员信息 + * @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'; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 拉取会员卡概况数据接口 + * @param string $begin_date 查询数据的起始时间 + * @param string $end_date 查询数据的截至时间 + * @param string $cond_source 卡券来源(0为公众平台创建的卡券数据 1是API创建的卡券数据) + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function getCardMemberCardinfo($begin_date, $end_date, $cond_source) + { + $data = ['begin_date' => $begin_date, 'end_date' => $end_date, 'cond_source' => $cond_source]; + $url = "https://api.weixin.qq.com/datacube/getcardmembercardinfo?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 拉取单张会员卡数据接口 + * @param string $begin_date 查询数据的起始时间 + * @param string $end_date 查询数据的截至时间 + * @param string $card_id 卡券id + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function getCardMemberCardDetail($begin_date, $end_date, $card_id) + { + $data = ['begin_date' => $begin_date, 'end_date' => $end_date, 'card_id' => $card_id]; + $url = "https://api.weixin.qq.com/datacube/getcardmembercarddetail?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 拉取会员信息(积分查询)接口 + * @param string $card_id 查询会员卡的cardid + * @param string $code 所查询用户领取到的code值 + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function getCardMemberCard($card_id, $code) + { + $data = ['card_id' => $card_id, 'code' => $code]; + $url = "https://api.weixin.qq.com/card/membercard/userinfo/get?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 删除支付后投放卡券规则 + * @param integer $rule_id 支付即会员的规则名称 + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function delPayGiftCard($rule_id) + { + $url = "https://api.weixin.qq.com/card/paygiftcard/add?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['rule_id' => $rule_id]); + } + + /** + * 查询支付后投放卡券规则详情 + * @param integer $rule_id 要查询规则id + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function getPayGiftCard($rule_id) + { + $url = "https://api.weixin.qq.com/card/paygiftcard/getbyid?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['rule_id' => $rule_id]); + } + + /** + * 批量查询支付后投放卡券规则 + * @param integer $offset 起始偏移量 + * @param integer $count 查询的数量 + * @param bool $effective 是否仅查询生效的规则 + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function batchGetPayGiftCard($offset = 0, $count = 10, $effective = true) + { + $data = ['type' => 'RULE_TYPE_PAY_MEMBER_CARD', 'offset' => $offset, 'count' => $count, 'effective' => $effective]; + $url = "https://api.weixin.qq.com/card/paygiftcard/batchget?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpGetForJson($url); + } + + /** + * 对优惠券批价 + * @param string $card_id 需要来配置库存的card_id + * @param integer $quantity 本次需要兑换的库存数目 + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function getPayprice($card_id, $quantity) + { + $url = "POST https://api.weixin.qq.com/card/pay/getpayprice?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['card_id' => $card_id, '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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpGetForJson($url); + } + + /** + * 确认兑换库存接口 + * @param string $card_id 需要来兑换库存的card_id + * @param integer $quantity 本次需要兑换的库存数目 + * @param string $order_id 仅可以使用上面得到的订单号,保证批价有效性 + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function payConfirm($card_id, $quantity, $order_id) + { + $data = ['card_id' => $card_id, 'quantity' => $quantity, 'order_id' => $order_id]; + $url = "https://api.weixin.qq.com/card/pay/confirm?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 充值券点接口 + * @param integer $coin_count + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function payRecharge($coin_count) + { + $url = "https://api.weixin.qq.com/card/pay/recharge?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['coin_count' => $coin_count]); + } + + /** + * 查询订单详情接口 + * @param string $order_id + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function payGetOrder($order_id) + { + $url = "https://api.weixin.qq.com/card/pay/getorder?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['order_id' => $order_id]); + } + + /** + * 查询券点流水详情接口 + * @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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + +} \ No newline at end of file diff --git a/src/system/sdks/WeChat/Contracts/BasicAliPay.php b/src/system/sdks/WeChat/Contracts/BasicAliPay.php new file mode 100644 index 00000000..57960c21 --- /dev/null +++ b/src/system/sdks/WeChat/Contracts/BasicAliPay.php @@ -0,0 +1,356 @@ +params = new DataArray([]); + $this->config = new DataArray($options); + if (empty($options['appid'])) { + throw new InvalidArgumentException("Missing Config -- [appid]"); + } + if (empty($options['public_key'])) { + throw new InvalidArgumentException("Missing Config -- [public_key]"); + } + if (empty($options['private_key'])) { + throw new InvalidArgumentException("Missing Config -- [private_key]"); + } + if (!empty($options['debug'])) { + $this->gateway = 'https://openapi.alipaydev.com/gateway.do?charset=utf-8'; + } + $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']); + } + } + + /** + * 静态创建对象 + * @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 $out_trade_no + * @return array|boolean + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function query($out_trade_no = '') + { + $this->options->set('method', 'alipay.trade.query'); + return $this->getResult(['out_trade_no' => $out_trade_no]); + } + + /** + * 支付宝订单退款操作 + * @param array|string $options 退款参数或退款商户订单号 + * @param null $refund_amount 退款金额 + * @return array|boolean + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function refund($options, $refund_amount = null) + { + if (!is_array($options)) $options = ['out_trade_no' => $options, 'refund_amount' => $refund_amount]; + $this->options->set('method', 'alipay.trade.refund'); + return $this->getResult($options); + } + + /** + * 关闭支付宝进行中的订单 + * @param array|string $options + * @return array|boolean + * @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); + $content = wordwrap($this->config->get('public_key'), 64, "\n", true); + $res = "-----BEGIN PUBLIC KEY-----\n{$content}\n-----END PUBLIC KEY-----"; + if (openssl_verify($string, base64_decode($data['sign']), $res, OPENSSL_ALGO_SHA256) !== 1) { + throw new InvalidResponseException('Data signature verification failed.', 0, $data); + } + return $data; + } + + /** + * 验证接口返回的数据签名 + * @param array $data 通知数据 + * @param null|string $sign 数据签名 + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + */ + protected function verify($data, $sign) + { + $content = wordwrap($this->config->get('public_key'), 64, "\n", true); + $res = "-----BEGIN PUBLIC KEY-----\n{$content}\n-----END PUBLIC KEY-----"; + if ($this->options->get('sign_type') === 'RSA2') { + if (openssl_verify(json_encode($data, 256), base64_decode($sign), $res, OPENSSL_ALGO_SHA256) !== 1) { + throw new InvalidResponseException('Data signature verification failed.'); + } + } else { + if (openssl_verify(json_encode($data, 256), base64_decode($sign), $res, OPENSSL_ALGO_SHA1) !== 1) { + throw new InvalidResponseException('Data signature verification failed.'); + } + } + return $data; + } + + /** + * 获取数据签名 + * @return string + */ + protected function getSign() + { + $content = wordwrap($this->trimCert($this->config->get('private_key')), 64, "\n", true); + $string = "-----BEGIN RSA PRIVATE KEY-----\n{$content}\n-----END RSA PRIVATE KEY-----"; + if ($this->options->get('sign_type') === 'RSA2') { + openssl_sign($this->getSignContent($this->options->get(), true), $sign, $string, OPENSSL_ALGO_SHA256); + } else { + openssl_sign($this->getSignContent($this->options->get(), true), $sign, $string, OPENSSL_ALGO_SHA1); + } + return base64_encode($sign); + } + + /** + * 去除证书前后内容及空白 + * @param string $sign + * @return string + */ + protected function trimCert($sign) + { + // if (file_exists($sign)) $sign = file_get_contents($sign); + return preg_replace(['/\s+/', '/\-{5}.*?\-{5}/'], '', $sign); + } + + /** + * 数据签名处理 + * @param array $data 需要进行签名数据 + * @param boolean $needSignType 是否需要sign_type字段 + * @return string + */ + private function getSignContent(array $data, $needSignType = false) + { + list($attrs,) = [[], ksort($data)]; + 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); + } + + /** + * 数据包生成及数据签名 + * @param array $options + */ + protected function applyData($options) + { + $this->options->set('biz_content', json_encode($this->params->merge($options), 256)); + $this->options->set('sign', $this->getSign()); + } + + /** + * 请求接口并验证访问数据 + * @param array $options + * @return array|boolean + * @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']); + } + + /** + * 生成支付HTML代码 + * @return string + */ + protected function buildPayHtml() + { + $html = "
"; + foreach ($this->options->get() as $key => $value) { + $value = str_replace("'", ''', $value); + $html .= ""; + } + $html .= "
"; + return "{$html}"; + } + + /** + * 新版 从证书中提取序列号 + * @param string $sign + * @return string + */ + public function getCertSN($sign) + { + // if (file_exists($sign)) $sign = file_get_contents($sign); + $ssl = openssl_x509_parse($sign); + return md5($this->_arr2str(array_reverse($ssl['issuer'])) . $ssl['serialNumber']); + } + + /** + * 新版 提取根证书序列号 + * @param string $sign + * @return string|null + */ + public function getRootCertSN($sign) + { + $sn = null; + // if (file_exists($sign)) $sign = file_get_contents($sign); + $array = explode("-----END CERTIFICATE-----", $sign); + for ($i = 0; $i < count($array) - 1; $i++) { + $ssl[$i] = openssl_x509_parse($array[$i] . "-----END CERTIFICATE-----"); + if (strpos($ssl[$i]['serialNumber'], '0x') === 0) { + $ssl[$i]['serialNumber'] = $this->_hex2dec($ssl[$i]['serialNumber']); + } + if ($ssl[$i]['signatureTypeLN'] == "sha1WithRSAEncryption" || $ssl[$i]['signatureTypeLN'] == "sha256WithRSAEncryption") { + if ($sn == null) { + $sn = md5($this->_arr2str(array_reverse($ssl[$i]['issuer'])) . $ssl[$i]['serialNumber']); + } else { + $sn = $sn . "_" . md5($this->_arr2str(array_reverse($ssl[$i]['issuer'])) . $ssl[$i]['serialNumber']); + } + } + } + return $sn; + } + + /** + * 新版 数组转字符串 + * @param array $array + * @return string + */ + private function _arr2str($array) + { + $string = []; + if ($array && is_array($array)) { + foreach ($array as $key => $value) { + $string[] = $key . '=' . $value; + } + } + return implode(',', $string); + } + + + /** + * 新版 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; + } + + /** + * 应用数据操作 + * @param array $options + * @return mixed + */ + abstract public function apply($options); + +} \ No newline at end of file diff --git a/src/system/sdks/WeChat/Contracts/BasicPushEvent.php b/src/system/sdks/WeChat/Contracts/BasicPushEvent.php new file mode 100644 index 00000000..b02e6fdc --- /dev/null +++ b/src/system/sdks/WeChat/Contracts/BasicPushEvent.php @@ -0,0 +1,218 @@ +config = new DataArray($options); + $this->input = new DataArray($_REQUEST); + $this->appid = $this->config->get('appid'); + // 推送消息处理 + if ($_SERVER['REQUEST_METHOD'] == "POST") { + $this->postxml = file_get_contents("php://input"); + $this->encryptType = $this->input->get('encrypt_type'); + if ($this->isEncrypt()) { + if (empty($options['encodingaeskey'])) { + throw new InvalidArgumentException("Missing Config -- [encodingaeskey]"); + } + if (!class_exists('Prpcrypt', false)) { + require __DIR__ . '/Prpcrypt.php'; + } + $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()) { + @ob_clean(); + exit($this->input->get('echostr')); + } else { + throw new InvalidResponseException('Invalid interface request.', '0'); + } + } + + /** + * 消息是否需要加密 + * @return boolean + */ + public function isEncrypt() + { + return $this->encryptType === 'aes'; + } + + /** + * 回复消息 + * @param array $data 消息内容 + * @param boolean $return 是否返回XML内容 + * @param boolean $isEncrypt 是否加密内容 + * @return string + * @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) { + if (!class_exists('Prpcrypt', false)) { + require __DIR__ . '/Prpcrypt.php'; + } + $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 string $str + * @return bool + */ + private function checkSignature($str = '') + { + $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, $str]; + sort($tmpArr, SORT_STRING); + return sha1(implode($tmpArr)) === $signature; + } + + /** + * 获取公众号推送对象 + * @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/src/system/sdks/WeChat/Contracts/BasicWeChat.php b/src/system/sdks/WeChat/Contracts/BasicWeChat.php new file mode 100644 index 00000000..6a056d62 --- /dev/null +++ b/src/system/sdks/WeChat/Contracts/BasicWeChat.php @@ -0,0 +1,235 @@ +GetAccessTokenCallback = $options['GetAccessTokenCallback']; + } + if (!empty($options['cache_path'])) { + Tools::$cache_path = $options['cache_path']; + } + $this->config = new DataArray($options); + } + + /** + * 静态创建对象 + * @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); + } + + /** + * 获取访问 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); + } + + /** + * 清理删除 AccessToken + * @return bool + */ + public function delAccessToken() + { + $this->access_token = ''; + return Tools::delCache($this->config->get('appid') . '_access_token'); + } + + /** + * 以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()); + } + } + + /** + * 以POST获取接口数据并转为数组 + * @param string $url 接口地址 + * @param array $data 请求数据 + * @param bool $buildToJson + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + protected function httpPostForJson($url, array $data, $buildToJson = true) + { + try { + $options = []; + if ($buildToJson) $options['headers'] = ['Content-Type: application/json']; + return Tools::json2arr(Tools::post($url, $buildToJson ? 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()); + } + } + + /** + * 注册当前请求接口 + * @param string $url 接口地址 + * @param string $method 当前接口方法 + * @param array $arguments 请求参数 + * @return string + * @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); + } + + /** + * 接口通用POST请求方法 + * @param string $url 接口URL + * @param array $data POST提交接口参数 + * @param bool $isBuildJson + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function callPostApi($url, array $data, $isBuildJson = true) + { + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data, $isBuildJson); + } + + /** + * 接口通用GET请求方法 + * @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); + } + +} \ No newline at end of file diff --git a/src/system/sdks/WeChat/Contracts/BasicWePay.php b/src/system/sdks/WeChat/Contracts/BasicWePay.php new file mode 100644 index 00000000..3f5ae4c3 --- /dev/null +++ b/src/system/sdks/WeChat/Contracts/BasicWePay.php @@ -0,0 +1,194 @@ +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); + } + + /** + * 获取微信支付通知 + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + */ + public function getNotify() + { + $data = Tools::xml2arr(file_get_contents('php://input')); + if (isset($data['sign']) && $this->getPaySign($data) === $data['sign']) { + return $data; + } + throw new InvalidResponseException('Invalid Notify.', '0'); + } + + /** + * 获取微信支付通知回复内容 + * @return string + */ + public function getNotifySuccessReply() + { + return Tools::arr2xml(['return_code' => 'SUCCESS', 'return_msg' => 'OK']); + } + + /** + * 生成支付签名 + * @param array $data 参与签名的数据 + * @param string $signType 参与签名的类型 + * @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'))); + } + + /** + * 转换短链接 + * @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]); + } + + /** + * 数组直接转xml数据输出 + * @param array $data + * @param bool $isReturn + * @return string + */ + public function toXml(array $data, $isReturn = false) + { + $xml = Tools::arr2xml($data); + if ($isReturn) { + return $xml; + } + echo $xml; + } + + /** + * 以 Post 请求接口 + * @param string $url 请求 + * @param array $data 接口参数 + * @param bool $isCert 是否需要使用双向证书 + * @param string $signType 数据签名类型 MD5|SHA256 + * @param bool $needSignType 是否需要传签名类型参数 + * @param bool $needNonceStr + * @return array + * @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; + } +} diff --git a/src/system/sdks/WeChat/Contracts/BasicWeWork.php b/src/system/sdks/WeChat/Contracts/BasicWeWork.php new file mode 100644 index 00000000..3c4e6717 --- /dev/null +++ b/src/system/sdks/WeChat/Contracts/BasicWeWork.php @@ -0,0 +1,28 @@ +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/src/system/sdks/WeChat/Contracts/DataArray.php b/src/system/sdks/WeChat/Contracts/DataArray.php new file mode 100644 index 00000000..ea83b1b0 --- /dev/null +++ b/src/system/sdks/WeChat/Contracts/DataArray.php @@ -0,0 +1,115 @@ +config = $options; + } + + /** + * 设置配置项值 + * @param string $offset + * @param string|array|null|integer $value + */ + public function set($offset, $value) + { + $this->offsetSet($offset, $value); + } + + /** + * 获取配置项参数 + * @param string|null $offset + * @return array|string|null + */ + public function get($offset = null) + { + return $this->offsetGet($offset); + } + + /** + * 合并数据到对象 + * @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); + } + + /** + * 设置配置项值 + * @param string $offset + * @param string|array|null|integer $value + */ + #[\ReturnTypeWillChange] + public function offsetSet($offset, $value) + { + if (is_null($offset)) { + $this->config[] = $value; + } else { + $this->config[$offset] = $value; + } + } + + /** + * 判断配置Key是否存在 + * @param string $offset + * @return bool + */ + #[\ReturnTypeWillChange] + public function offsetExists($offset) + { + return isset($this->config[$offset]); + } + + /** + * 清理配置项 + * @param string|null $offset + */ + #[\ReturnTypeWillChange] + public function offsetUnset($offset = null) + { + if (is_null($offset)) { + $this->config = []; + } else { + unset($this->config[$offset]); + } + } + + /** + * 获取配置项参数 + * @param string|null $offset + * @return array|string|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset = null) + { + if (is_null($offset)) { + return $this->config; + } + return isset($this->config[$offset]) ? $this->config[$offset] : null; + } +} \ No newline at end of file diff --git a/src/system/sdks/WeChat/Contracts/DataError.php b/src/system/sdks/WeChat/Contracts/DataError.php new file mode 100644 index 00000000..354e70b8 --- /dev/null +++ b/src/system/sdks/WeChat/Contracts/DataError.php @@ -0,0 +1,181 @@ + '系统繁忙,此时请开发者稍候再试', + 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/src/system/sdks/WeChat/Contracts/MyCurlFile.php b/src/system/sdks/WeChat/Contracts/MyCurlFile.php new file mode 100644 index 00000000..6380fdae --- /dev/null +++ b/src/system/sdks/WeChat/Contracts/MyCurlFile.php @@ -0,0 +1,65 @@ + $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/src/system/sdks/WeChat/Contracts/Prpcrypt.php b/src/system/sdks/WeChat/Contracts/Prpcrypt.php new file mode 100644 index 00000000..51d42cba --- /dev/null +++ b/src/system/sdks/WeChat/Contracts/Prpcrypt.php @@ -0,0 +1,177 @@ + PKCS7Encoder::$blockSize) { + $pad = 0; + } + return substr($text, 0, strlen($text) - $pad); + } + +} + +/** + * 公众号消息 - 加解密 + * Class Prpcrypt + */ +class Prpcrypt +{ + + public $key; + + /** + * Prpcrypt constructor. + * @param $key + */ + function __construct($key) + { + $this->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]; + } + } + + /** + * 对密文进行解密 + * @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]; + } + } + + /** + * 随机生成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; + } + +} + +/** + * 仅用作类内部使用 + * 不用于官方API接口的errCode码 + * Class ErrorCode + */ +class ErrorCode +{ + + public static $OK = 0; + public static $ParseXmlError = 40002; + public static $IllegalAesKey = 40004; + public static $IllegalBuffer = 40008; + public static $EncryptAESError = 40006; + public static $DecryptAESError = 40007; + public static $EncodeBase64Error = 40009; + public static $DecodeBase64Error = 40010; + public static $GenReturnXmlError = 40011; + public static $ValidateAppidError = 40005; + public static $ComputeSignatureError = 40003; + public static $ValidateSignatureError = 40001; + public static $errCode = [ + '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; + } + +} diff --git a/src/system/sdks/WeChat/Contracts/Tools.php b/src/system/sdks/WeChat/Contracts/Tools.php new file mode 100644 index 00000000..772b5b29 --- /dev/null +++ b/src/system/sdks/WeChat/Contracts/Tools.php @@ -0,0 +1,458 @@ + null, // 写入缓存 + 'get' => null, // 获取缓存 + 'del' => null, // 删除缓存 + 'put' => null, // 写入文件 + ]; + + /** + * 网络缓存 + * @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; + } + + + /** + * 根据文件后缀获取文件类型 + * @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; + } + + /** + * 创建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; + } + + /** + * 数组转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 + */ + 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 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 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反解析处理 + * @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原形转换为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 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); + } + + /** + * 以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); + } + + /** + * 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数据设置 + if (strtolower($method) === 'post') { + curl_setopt($curl, CURLOPT_POST, true); + 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, 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); + list($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; + } + + /** + * 写入文件 + * @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; + } + + /** + * 缓存配置与存储 + * @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; + } + + /** + * 获取缓存内容 + * @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 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 + * @return string + */ + private static function _getCacheName($name) + { + if (empty(self::$cache_path)) { + self::$cache_path = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . 'Cache' . DIRECTORY_SEPARATOR; + } + self::$cache_path = rtrim(self::$cache_path, '/\\') . DIRECTORY_SEPARATOR; + file_exists(self::$cache_path) || mkdir(self::$cache_path, 0755, true); + return self::$cache_path . $name; + } +} diff --git a/src/system/sdks/WeChat/Custom.php b/src/system/sdks/WeChat/Custom.php new file mode 100644 index 00000000..9c9145cb --- /dev/null +++ b/src/system/sdks/WeChat/Custom.php @@ -0,0 +1,233 @@ + $kf_account, 'nickname' => $nickname]; + $url = "https://api.weixin.qq.com/customservice/kfaccount/add?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 修改客服帐号 + * @param string $kf_account 客服账号 + * @param string $nickname 客服昵称 + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function updateAccount($kf_account, $nickname) + { + $data = ['kf_account' => $kf_account, 'nickname' => $nickname]; + $url = "https://api.weixin.qq.com/customservice/kfaccount/update?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 删除客服帐号 + * @param string $kf_account 客服账号 + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function deleteAccount($kf_account) + { + $data = ['kf_account' => $kf_account]; + $url = "https://api.weixin.qq.com/customservice/kfaccount/del?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 邀请绑定客服帐号 + * @param string $kf_account 完整客服帐号,格式为:帐号前缀@公众号微信号 + * @param string $invite_wx 接收绑定邀请的客服微信号 + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function inviteWorker($kf_account, $invite_wx) + { + $url = 'https://api.weixin.qq.com/customservice/kfaccount/inviteworker?access_token=ACCESS_TOKEN'; + return $this->callPostApi($url, ['kf_account' => $kf_account, '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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpGetForJson($url); + } + + /** + * 设置客服帐号的头像 + * @param string $kf_account 客户账号 + * @param string $image 头像文件位置 + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function uploadHeadimg($kf_account, $image) + { + $url = "http://api.weixin.qq.com/customservice/kfaccount/uploadheadimg?access_token=ACCESS_TOKEN&kf_account={$kf_account}"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['media' => Tools::createCurlFile($image)]); + } + + /** + * 客服接口-发消息 + * @param array $data + * @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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 删除群发【订阅号与服务号认证后均可用】 + * @param integer $msg_id 发送出去的消息ID + * @param null|integer $article_idx 要删除的文章在图文消息中的位置,第一篇编号为1,该字段不填或填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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 查询群发消息发送状态【订阅号与服务号认证后均可用】 + * @param integer $msg_id 群发消息后返回的消息id + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function massGet($msg_id) + { + $url = "https://api.weixin.qq.com/cgi-bin/message/mass/get?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['msg_id' => $msg_id]); + } + + /** + * 获取群发速度 + * @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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, []); + } + + /** + * 设置群发速度 + * @param integer $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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['speed' => $speed]); + } + + +} \ No newline at end of file diff --git a/src/system/sdks/WeChat/Draft.php b/src/system/sdks/WeChat/Draft.php new file mode 100644 index 00000000..9ff31e58 --- /dev/null +++ b/src/system/sdks/WeChat/Draft.php @@ -0,0 +1,118 @@ +registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['articles' => $articles]); + } + + /** + * 获取草稿 + * @param string $media_id + * @param string $outType 返回处理函数 + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function get($media_id, $outType = null) + { + $url = "https://api.weixin.qq.com/cgi-bin/draft/get?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['media_id' => $media_id]); + } + + + /** + * 删除草稿 + * @param string $media_id + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function delete($media_id) + { + $url = "https://api.weixin.qq.com/cgi-bin/draft/delete?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['media_id' => $media_id]); + } + + /** + * 新增图文素材 + * @param array $data 文件名称 + * @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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 修改草稿 + * @param string $media_id 要修改的图文消息的id + * @param int $index 要更新的文章在图文消息中的位置(多图文消息时,此字段才有意义),第一篇为0 + * @param $articles + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function update($media_id, $index, $articles) + { + $data = ['media_id' => $media_id, 'index' => $index, 'articles' => $articles]; + $url = "https://api.weixin.qq.com/cgi-bin/draft/update?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpGetForJson($url); + } + + /** + * 获取草稿列表 + * @param int $offset 从全部素材的该偏移位置开始返回,0表示从第一个素材返回 + * @param int $count 返回素材的数量,取值在1到20之间 + * @param int $no_content 1 表示不返回 content 字段,0 表示正常返回,默认为 0 + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function batchGet($offset = 0, $count = 20, $no_content = 0) + { + $url = "https://api.weixin.qq.com/cgi-bin/draft/batchget?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['no_content' => $no_content, 'offset' => $offset, 'count' => $count]); + } + +} diff --git a/src/system/sdks/WeChat/Exceptions/InvalidArgumentException.php b/src/system/sdks/WeChat/Exceptions/InvalidArgumentException.php new file mode 100644 index 00000000..1e45e5f5 --- /dev/null +++ b/src/system/sdks/WeChat/Exceptions/InvalidArgumentException.php @@ -0,0 +1,27 @@ +raw = $raw; + } +} \ No newline at end of file diff --git a/src/system/sdks/WeChat/Exceptions/InvalidDecryptException.php b/src/system/sdks/WeChat/Exceptions/InvalidDecryptException.php new file mode 100644 index 00000000..056d0aa7 --- /dev/null +++ b/src/system/sdks/WeChat/Exceptions/InvalidDecryptException.php @@ -0,0 +1,27 @@ +raw = $raw; + } +} \ No newline at end of file diff --git a/src/system/sdks/WeChat/Exceptions/InvalidInstanceException.php b/src/system/sdks/WeChat/Exceptions/InvalidInstanceException.php new file mode 100644 index 00000000..538d9dc3 --- /dev/null +++ b/src/system/sdks/WeChat/Exceptions/InvalidInstanceException.php @@ -0,0 +1,27 @@ +raw = $raw; + } +} \ No newline at end of file diff --git a/src/system/sdks/WeChat/Exceptions/InvalidResponseException.php b/src/system/sdks/WeChat/Exceptions/InvalidResponseException.php new file mode 100644 index 00000000..8f1a6b98 --- /dev/null +++ b/src/system/sdks/WeChat/Exceptions/InvalidResponseException.php @@ -0,0 +1,28 @@ +raw = $raw; + } + +} \ No newline at end of file diff --git a/src/system/sdks/WeChat/Exceptions/LocalCacheException.php b/src/system/sdks/WeChat/Exceptions/LocalCacheException.php new file mode 100644 index 00000000..e294cded --- /dev/null +++ b/src/system/sdks/WeChat/Exceptions/LocalCacheException.php @@ -0,0 +1,29 @@ +raw = $raw; + } + +} \ No newline at end of file diff --git a/src/system/sdks/WeChat/Freepublish.php b/src/system/sdks/WeChat/Freepublish.php new file mode 100644 index 00000000..03dee9e2 --- /dev/null +++ b/src/system/sdks/WeChat/Freepublish.php @@ -0,0 +1,88 @@ +registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['media_id' => $media_id]); + } + + /** + * 发布状态轮询接口 + * @param mixed $publish_id + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function get($publish_id) + { + $url = "https://api.weixin.qq.com/cgi-bin/freepublish/get?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['publish_id' => $publish_id]); + } + + /** + * 删除发布 + * 发布成功之后,随时可以通过该接口删除。此操作不可逆,请谨慎操作。 + * @param mixed $article_id 成功发布时返回的 article_id + * @param int $index 要删除的文章在图文消息中的位置,第一篇编号为1,该字段不填或填0会删除全部文章 + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function delete($article_id, $index = 0) + { + $url = "https://api.weixin.qq.com/cgi-bin/freepublish/delete?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['article_id' => $article_id, 'index' => $index]); + } + + /** + * 通过 article_id 获取已发布文章 + * @param mixed $article_id 要获取的草稿的article_id + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function getArticle($article_id) + { + $url = "https://api.weixin.qq.com/cgi-bin/freepublish/getarticle?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['article_id' => $article_id]); + } + + /** + * 获取成功发布列表 + * @param int $offset 从全部素材的该偏移位置开始返回,0表示从第一个素材返回 + * @param int $count 返回素材的数量,取值在1到20之间 + * @param int $no_content 1 表示不返回 content 字段,0 表示正常返回,默认为 0 + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function batchGet($offset = 0, $count = 20, $no_content = 0) + { + $url = "https://api.weixin.qq.com/cgi-bin/freepublish/batchget?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['no_content' => $no_content, 'offset' => $offset, 'count' => $count]); + } +} \ No newline at end of file diff --git a/src/system/sdks/WeChat/Limit.php b/src/system/sdks/WeChat/Limit.php new file mode 100644 index 00000000..bc164694 --- /dev/null +++ b/src/system/sdks/WeChat/Limit.php @@ -0,0 +1,52 @@ +callPostApi($url, ['appid' => $this->config->get('appid')]); + } + + /** + * 网络检测 + * @param string $action 执行的检测动作 + * @param string $operator 指定平台从某个运营商进行检测 + * @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'; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpGetForJson($url); + } +} \ No newline at end of file diff --git a/src/system/sdks/WeChat/Media.php b/src/system/sdks/WeChat/Media.php new file mode 100644 index 00000000..155d264b --- /dev/null +++ b/src/system/sdks/WeChat/Media.php @@ -0,0 +1,188 @@ +registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['media' => Tools::createCurlFile($filename)], false); + } + + /** + * 获取临时素材 + * @param string $media_id + * @param string $outType 返回处理函数 + * @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()); + $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 文件名称 + * @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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 更新图文素材 + * @param string $media_id 要修改的图文消息的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) + { + $data = ['media_id' => $media_id, 'index' => $index, 'articles' => $news]; + $url = "https://api.weixin.qq.com/cgi-bin/material/update_news?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 上传图文消息内的图片获取URL + * @param mixed $filename + * @return array + * @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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['media' => Tools::createCurlFile($filename)], false); + } + + /** + * 新增其他类型永久素材 + * @param mixed $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}"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['media' => Tools::createCurlFile($filename), 'description' => Tools::arr2json($description)], false); + } + + /** + * 获取永久素材 + * @param string $media_id + * @param null|string $outType 输出类型 + * @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()); + $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 $media_id + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function delMaterial($media_id) + { + $url = "https://api.weixin.qq.com/cgi-bin/material/del_material?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['media_id' => $media_id]); + } + + /** + * 获取素材总数 + * @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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpGetForJson($url); + } + + /** + * 获取素材列表 + * @param string $type + * @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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['type' => $type, 'offset' => $offset, 'count' => $count]); + } +} \ No newline at end of file diff --git a/src/system/sdks/WeChat/Menu.php b/src/system/sdks/WeChat/Menu.php new file mode 100644 index 00000000..a86ae03e --- /dev/null +++ b/src/system/sdks/WeChat/Menu.php @@ -0,0 +1,95 @@ +registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpGetForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpGetForJson($url); + } + + /** + * 自定义菜单创建 + * @param array $data + * @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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 创建个性化菜单 + * @param array $data + * @return array + * @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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 删除个性化菜单 + * @param string $menuid + * @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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['menuid' => $menuid]); + } + + /** + * 测试个性化菜单匹配结果 + * @param string $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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['user_id' => $openid]); + } +} \ No newline at end of file diff --git a/src/system/sdks/WeChat/Oauth.php b/src/system/sdks/WeChat/Oauth.php new file mode 100644 index 00000000..31f4ad7c --- /dev/null +++ b/src/system/sdks/WeChat/Oauth.php @@ -0,0 +1,86 @@ +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 获取 AccessToken 和 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); + } + + /** + * 刷新AccessToken并续期 + * @param string $refresh_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 $access_token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同 + * @param string $openid 用户的唯一标识 + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function checkOauthAccessToken($access_token, $openid) + { + $url = "https://api.weixin.qq.com/sns/auth?access_token={$access_token}&openid={$openid}"; + return $this->httpGetForJson($url); + } + + /** + * 拉取用户信息(需scope为 snsapi_userinfo) + * @param string $access_token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同 + * @param string $openid 用户的唯一标识 + * @param string $lang 返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语 + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function getUserInfo($access_token, $openid, $lang = 'zh_CN') + { + $url = "https://api.weixin.qq.com/sns/userinfo?access_token={$access_token}&openid={$openid}&lang={$lang}"; + return $this->httpGetForJson($url); + } +} diff --git a/src/system/sdks/WeChat/Pay.php b/src/system/sdks/WeChat/Pay.php new file mode 100644 index 00000000..5f7340b4 --- /dev/null +++ b/src/system/sdks/WeChat/Pay.php @@ -0,0 +1,218 @@ +config->get())->create($options); + } + + /** + * 刷卡支付 + * @param array $options + * @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 统一下单预支付码 + * @return array + */ + public function createParamsForJsApi($prepay_id) + { + return Order::instance($this->config->get())->jsapiParams($prepay_id); + } + + /** + * 获取APP支付参数 + * @param string $prepay_id 统一下单预支付码 + * @return array + */ + public function createParamsForApp($prepay_id) + { + return Order::instance($this->config->get())->appParams($prepay_id); + } + + /** + * 获取支付规则二维码 + * @param string $product_id 商户定义的商品id 或者订单号 + * @return string + */ + public function createParamsForRuleQrc($product_id) + { + return Order::instance($this->config->get())->qrcParams($product_id); + } + + /** + * 查询订单 + * @param array $options + * @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 + * @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 + * @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 + * @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 + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function queryAuthCode($authCode) + { + return Order::instance($this->config->get())->queryAuthCode($authCode); + } + + /** + * 下载对账单 + * @param array $options 静音参数 + * @param null|string $outType 输出类型 + * @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 + * @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 + * @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 商户调用企业付款API时使用的商户订单号 + * @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 + * @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/src/system/sdks/WeChat/Product.php b/src/system/sdks/WeChat/Product.php new file mode 100644 index 00000000..ec4e3738 --- /dev/null +++ b/src/system/sdks/WeChat/Product.php @@ -0,0 +1,162 @@ + $keystandard, 'keystr' => $keystr, 'status' => $status]; + $url = "https://api.weixin.qq.com/scan/product/modstatus?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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 = []) + { + $data = ['openid' => $openids, 'username' => $usernames]; + $url = "https://api.weixin.qq.com/scan/testwhitelist/set?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 获取商品二维码 + * @param string $keystandard 商品编码标准 + * @param string $keystr 商品编码内容 + * @param integer $qrcode_size 二维码的尺寸(整型),数值代表边长像素数,不填写默认值为100 + * @param array $extinfo 由商户自定义传入,建议仅使用大小写字母、数字及-_().*这6个常用字符 + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function getQrcode($keystandard, $keystr, $qrcode_size, $extinfo = []) + { + $data = ['keystandard' => $keystandard, 'keystr' => $keystr, 'qrcode_size' => $qrcode_size]; + empty($extinfo) || $data['extinfo'] = $extinfo; + $url = "https://api.weixin.qq.com/scan/product/getqrcode?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 查询商品信息 + * @param string $keystandard 商品编码标准 + * @param string $keystr 商品编码内容 + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function getProduct($keystandard, $keystr) + { + $data = ['keystandard' => $keystandard, 'keystr' => $keystr]; + empty($extinfo) || $data['extinfo'] = $extinfo; + $url = "https://api.weixin.qq.com/scan/product/get?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 批量查询商品信息 + * @param integer $offset 批量查询的起始位置,从0开始,包含该起始位置 + * @param integer $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 = '') + { + $data = ['offset' => $offset, 'limit' => $limit]; + is_null($status) || $data['status'] = $status; + empty($keystr) || $data['keystr'] = $keystr; + $url = "https://api.weixin.qq.com/scan/product/get?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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) + { + $data = ['keystandard' => $keystandard, 'keystr' => $keystr, 'extinfo' => $extinfo]; + $url = "https://api.weixin.qq.com/scan/scanticket/check?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + +} \ No newline at end of file diff --git a/src/system/sdks/WeChat/Qrcode.php b/src/system/sdks/WeChat/Qrcode.php new file mode 100644 index 00000000..61ceb2f6 --- /dev/null +++ b/src/system/sdks/WeChat/Qrcode.php @@ -0,0 +1,63 @@ + ['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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 通过ticket换取二维码 + * @param string $ticket 获取的二维码ticket,凭借此ticket可以在有效时间内换取二维码。 + * @return string + */ + 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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['action' => 'long2short', 'long_url' => $longUrl]); + } +} \ No newline at end of file diff --git a/src/system/sdks/WeChat/Receive.php b/src/system/sdks/WeChat/Receive.php new file mode 100644 index 00000000..372a14a2 --- /dev/null +++ b/src/system/sdks/WeChat/Receive.php @@ -0,0 +1,151 @@ +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 + * @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 图片媒体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 语音媒体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 视频媒体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 音乐图片缩略图的媒体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/src/system/sdks/WeChat/Scan.php b/src/system/sdks/WeChat/Scan.php new file mode 100644 index 00000000..52ec7eab --- /dev/null +++ b/src/system/sdks/WeChat/Scan.php @@ -0,0 +1,186 @@ +registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpGetForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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') + { + $data = ['keystandard' => $keystandard, 'keystr' => $keystr, 'status' => $status]; + $url = "https://api.weixin.qq.com/scan/product/modstatus?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 设置测试人员白名单 + * @param array $openids 测试人员的openid列表 + * @param array $usernames 测试人员的微信号列表 + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function setTestWhiteList($openids = [], $usernames = []) + { + $data = ['openid' => $openids, 'username' => $usernames]; + $url = "https://api.weixin.qq.com/scan/product/modstatus?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 获取商品二维码 + * @param string $keystandard + * @param string $keystr + * @param null|string $extinfo + * @param integer $qrcode_size + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function getQrc($keystandard, $keystr, $extinfo = null, $qrcode_size = 64) + { + $data = ['keystandard' => $keystandard, 'keystr' => $keystr, 'qrcode_size' => $qrcode_size]; + is_null($extinfo) || $data['extinfo'] = $extinfo; + $url = "https://api.weixin.qq.com/scan/product/getqrcode?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['keystandard' => $keystandard, 'keystr' => $keystr]); + } + + /** + * 批量查询商品信息 + * @param integer $offset 批量查询的起始位置,从0开始,包含该起始位置。 + * @param integer $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) + { + $data = ['offset' => $offset, 'limit' => $limit]; + is_null($status) || $data['status'] = $status; + is_null($keystr) || $data['keystr'] = $keystr; + $url = "https://api.weixin.qq.com/scan/product/getlist?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['keystandard' => $keystandard, 'keystr' => $keystr, 'extinfo' => $extinfo]); + } + +} \ No newline at end of file diff --git a/src/system/sdks/WeChat/Script.php b/src/system/sdks/WeChat/Script.php new file mode 100644 index 00000000..eafab05c --- /dev/null +++ b/src/system/sdks/WeChat/Script.php @@ -0,0 +1,102 @@ +config->get('appid'); + $cache_name = "{$appid}_ticket_{$type}"; + Tools::delCache($cache_name); + } + + /** + * 获取JSAPI_TICKET接口 + * @param string $type TICKET类型(wx_card|jsapi) + * @param string $appid 强制指定有效APPID + * @return string + * @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; + } + + /** + * 获取JsApi使用签名 + * @param string $url 网页的URL + * @param string $appid 用于多个appid时使用(可空) + * @param string $ticket 强制指定ticket + * @param array $jsApiList 需初始化的 jsApiList + * @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, + ]; + } + + /** + * 数据生成签名 + * @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/src/system/sdks/WeChat/Shake.php b/src/system/sdks/WeChat/Shake.php new file mode 100644 index 00000000..a9a648c9 --- /dev/null +++ b/src/system/sdks/WeChat/Shake.php @@ -0,0 +1,350 @@ +registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpGetForJson($url); + } + + /** + * 申请设备ID + * @param string $quantity 申请的设备ID的数量,单次新增设备超过500个,需走人工审核流程 + * @param string $apply_reason 申请理由,不超过100个汉字或200个英文字母 + * @param null|string $comment 备注,不超过15个汉字或30个英文字母 + * @param null|string $poi_id 设备关联的门店ID,关联门店后,在门店1KM的范围内有优先摇出信息的机会。 + * @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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 查询设备ID申请审核状态 + * @param integer $applyId 批次ID,申请设备ID时所返回的批次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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 删除页面 + * @param integer $page_id 指定页面的id + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function deletePage($page_id) + { + $url = "https://api.weixin.qq.com/shakearound/page/delete?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['page_id' => $page_id]); + } + + /** + * 上传图片素材 + * @param string $filename 图片名字 + * @param string $type Icon:摇一摇页面展示的icon图;License:申请开通摇一摇周边功能时需上传的资质文件;若不传type,则默认type=icon + * @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}"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['media' => Tools::createCurlFile($filename)]); + } + + /** + * 配置设备与页面的关联关系 + * @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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 批量查询设备统计数据接口 + * @param integer $date 指定查询日期时间戳,单位为秒 + * @param integer $page_index 指定查询的结果页序号;返回结果按摇周边人数降序排序,每50条记录为一页 + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function totalDeviceList($date, $page_index = 1) + { + $url = "https://api.weixin.qq.com/shakearound/statistics/devicelist?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['date' => $date, 'page_index' => $page_index]); + } + + /** + * 以页面为维度的数据统计接口 + * @param integer $page_id 指定页面的设备ID + * @param integer $begin_date 起始日期时间戳,最长时间跨度为30天,单位为秒 + * @param integer $end_date 结束日期时间戳,最长时间跨度为30天,单位为秒 + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function totalPage($page_id, $begin_date, $end_date) + { + $url = "https://api.weixin.qq.com/shakearound/statistics/page?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['page_id' => $page_id, 'begin_date' => $begin_date, 'end_date' => $end_date]); + } + + /** + * 编辑分组信息 + * @param integer $group_id 分组唯一标识,全局唯一 + * @param string $group_name 分组名称,不超过100汉字或200个英文字母 + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function updateGroup($group_id, $group_name) + { + $url = "https://api.weixin.qq.com/shakearound/device/group/update?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['group_id' => $group_id, 'group_name' => $group_name]); + } + + /** + * 删除分组 + * @param integer $group_id 分组唯一标识,全局唯一 + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function deleteGroup($group_id) + { + $url = "https://api.weixin.qq.com/shakearound/device/group/delete?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['group_id' => $group_id]); + } + + /** + * 查询分组列表 + * @param integer $begin 分组列表的起始索引值 + * @param integer $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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['begin' => $begin, 'count' => $count]); + } + + + /** + * 查询分组详情 + * @param integer $group_id 分组唯一标识,全局唯一 + * @param integer $begin 分组里设备的起始索引值 + * @param integer $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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + +} \ No newline at end of file diff --git a/src/system/sdks/WeChat/Tags.php b/src/system/sdks/WeChat/Tags.php new file mode 100644 index 00000000..0fa302da --- /dev/null +++ b/src/system/sdks/WeChat/Tags.php @@ -0,0 +1,111 @@ +registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpGetForJson($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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['tag' => ['name' => $name]]); + } + + /** + * 更新粉丝标签 + * @param integer $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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['tag' => ['name' => $name, 'id' => $id]]); + } + + /** + * 删除粉丝标签 + * @param int $tagId + * @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'; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['tag' => ['id' => $tagId]]); + } + + /** + * 批量为用户打标签 + * @param array $openids + * @param integer $tagId + * @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'; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['openid_list' => $openids, 'tagid' => $tagId]); + } + + /** + * 批量为用户取消标签 + * @param array $openids + * @param integer $tagId + * @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'; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['openid_list' => $openids, 'tagid' => $tagId]); + } + + /** + * 获取用户身上的标签列表 + * @param string $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'; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['openid' => $openid]); + } +} \ No newline at end of file diff --git a/src/system/sdks/WeChat/Template.php b/src/system/sdks/WeChat/Template.php new file mode 100644 index 00000000..5722ae0d --- /dev/null +++ b/src/system/sdks/WeChat/Template.php @@ -0,0 +1,97 @@ +registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['industry_id1' => $industry_id1, 'industry_id2' => $industry_id2]); + } + + /** + * 获取设置的行业信息 + * @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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpGetForJson($url); + } + + /** + * 获得模板ID + * @param string $tpl_id 板库中模板的编号,有“TM**”和“OPENTMTM**”等形式 + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function addTemplate($tpl_id) + { + $url = "https://api.weixin.qq.com/cgi-bin/template/api_add_template?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['template_id_short' => $tpl_id]); + } + + /** + * 获取模板列表 + * @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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpGetForJson($url); + } + + /** + * 删除模板ID + * @param string $tpl_id 公众帐号下模板消息ID + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function delPrivateTemplate($tpl_id) + { + $url = "https://api.weixin.qq.com/cgi-bin/template/del_private_template?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['template_id' => $tpl_id]); + } + + /** + * 发送模板消息 + * @param array $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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + +} \ No newline at end of file diff --git a/src/system/sdks/WeChat/User.php b/src/system/sdks/WeChat/User.php new file mode 100644 index 00000000..cf2dd10e --- /dev/null +++ b/src/system/sdks/WeChat/User.php @@ -0,0 +1,134 @@ +registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['openid' => $openid, 'remark' => $remark]); + } + + /** + * 获取用户基本信息(包括UnionID机制) + * @param string $openid + * @param string $lang + * @return array + * @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}"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpGetForJson($url); + } + + /** + * 批量获取用户基本信息 + * @param array $openids + * @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]; + } + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, $data); + } + + /** + * 获取用户列表 + * @param string $next_openid + * @return array + * @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}"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpGetForJson($url); + } + + /** + * 获取标签下粉丝列表 + * @param integer $tagid 标签ID + * @param string $next_openid 第一个拉取的OPENID + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function getUserListByTag($tagid, $next_openid = '') + { + $url = 'https://api.weixin.qq.com/cgi-bin/user/tag/get?access_token=ACCESS_TOKEN'; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['tagid' => $tagid, 'next_openid' => $next_openid]); + } + + /** + * 获取公众号的黑名单列表 + * @param string $begin_openid + * @return array + * @throws Exceptions\InvalidResponseException + * @throws Exceptions\LocalCacheException + */ + public function getBlackList($begin_openid = '') + { + $url = "https://api.weixin.qq.com/cgi-bin/tags/members/getblacklist?access_token=ACCESS_TOKEN"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['begin_openid' => $begin_openid]); + } + + /** + * 批量拉黑用户 + * @param array $openids + * @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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['openid_list' => $openids]); + } + + /** + * 批量取消拉黑用户 + * @param array $openids + * @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"; + $this->registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['openid_list' => $openids]); + } + +} \ No newline at end of file diff --git a/src/system/sdks/WeChat/Wifi.php b/src/system/sdks/WeChat/Wifi.php new file mode 100644 index 00000000..4d23c9a8 --- /dev/null +++ b/src/system/sdks/WeChat/Wifi.php @@ -0,0 +1,271 @@ +registerApi($url, __FUNCTION__, func_get_args()); + return $this->httpPostForJson($url, ['pageindex' => $pageindex, 'pagesize' => $pagesize]); + } + + /** + * 查询门店Wi-Fi信息 + * @param integer $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]); + } + + /** + * 修改门店网络信息 + * @param integer $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 integer $shop_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 integer $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 integer $shop_id 门店ID + * @param string $ssid 无线网络设备的ssid + * @param bool $reset 重置secretkey,false-不重置,true-重置,默认为false + * @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 null|integer $shop_id 根据门店id查询 + * @param null|integer $pageindex 分页下标,默认从1开始 + * @param null|integer $pagesize 每页的个数,默认10个,最大20个 + * @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地址,格式冒号分隔,字符长度17个,并且字母小写,例如:00:1f:7a:ad:5c:a8 + * @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 integer $shop_id 门店ID + * @param string $ssid 已添加到门店下的无线网络名称 + * @param integer $img_id 物料样式编号:0-纯二维码,可用于自由设计宣传材料;1-二维码物料,155mm×215mm(宽×高),可直接张贴 + * @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 integer $shop_id 门店ID + * @param integer $template_id 模板ID,0-默认模板,1-自定义url + * @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 integer $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 integer $shop_id 门店ID + * @param integer $bar_type 微信首页欢迎语的文本内容:0--欢迎光临+公众号名称;1--欢迎光临+门店名称;2--已连接+公众号名称+WiFi;3--已连接+门店名称+Wi-Fi。 + * @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 integer $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,最长时间跨度为30天 + * @param string $end_date 结束日期时间戳,格式yyyy-mm-dd,最长时间跨度为30天 + * @param integer $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 integer $shop_id 门店ID,可设置为0,表示所有门店 + * @param integer $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 integer $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/src/system/sdks/WeMini/Crypt.php b/src/system/sdks/WeMini/Crypt.php new file mode 100644 index 00000000..9e6f0fb7 --- /dev/null +++ b/src/system/sdks/WeMini/Crypt.php @@ -0,0 +1,107 @@ +config->get('appid'), $sessionKey); + $errCode = $pc->decryptData($encryptedData, $iv, $data); + if ($errCode == 0) { + return json_decode($data, true); + } + return false; + } + + /** + * 登录凭证校验 + * @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 $code 用户登录凭证(有效期五分钟) + * @param string $iv 加密算法的初始向量 + * @param string $encryptedData 加密数据( encryptedData ) + * @return array + * @throws \WeChat\Exceptions\InvalidDecryptException + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function userInfo($code, $iv, $encryptedData) + { + $result = $this->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); + } + + /** + * 通过授权码换取手机号 + * @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 支付用户唯一标识 + * @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/src/system/sdks/WeMini/Delivery.php b/src/system/sdks/WeMini/Delivery.php new file mode 100644 index 00000000..a1097134 --- /dev/null +++ b/src/system/sdks/WeMini/Delivery.php @@ -0,0 +1,158 @@ +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/src/system/sdks/WeMini/Guide.php b/src/system/sdks/WeMini/Guide.php new file mode 100644 index 00000000..97233071 --- /dev/null +++ b/src/system/sdks/WeMini/Guide.php @@ -0,0 +1,512 @@ +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 integer $page + * @param integer $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); + } + + /** + * 新建可查询标签类型,支持新建4类可查询标签 + * @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 + * @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 integer $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 integer $type 操作类型 + * @param integer $start 分页查询,起始位置 + * @param integer $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 integer $type 操作类型 + * @param integer $start 分页查询,起始位置 + * @param integer $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/src/system/sdks/WeMini/Image.php b/src/system/sdks/WeMini/Image.php new file mode 100644 index 00000000..2c925e69 --- /dev/null +++ b/src/system/sdks/WeMini/Image.php @@ -0,0 +1,55 @@ +callPostApi($url, ['img_url' => $img_url, 'img' => $img], true); + } + + /** + * 本接口提供基于小程序的条码/二维码识别的API + * @param string $img_url 要检测的图片 url,传这个则不用传 img 参数。 + * @param string $img form-data 中媒体文件标识,有filename、filelength、content-type等信息,传这个则不用穿 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_TOCKEN"; + return $this->callPostApi($url, ['img_url' => $img_url, 'img' => $img], true); + } + + /** + * 本接口提供基于小程序的图片高清化能力 + * @param string $img_url 要检测的图片 url,传这个则不用传 img 参数 + * @param string $img form-data 中媒体文件标识,有filename、filelength、content-type等信息,传这个则不用穿 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_TOCKEN"; + return $this->callPostApi($url, ['img_url' => $img_url, 'img' => $img], true); + } +} \ No newline at end of file diff --git a/src/system/sdks/WeMini/Live.php b/src/system/sdks/WeMini/Live.php new file mode 100644 index 00000000..30bc1609 --- /dev/null +++ b/src/system/sdks/WeMini/Live.php @@ -0,0 +1,156 @@ +callPostApi($url, $data, true); + } + + /** + * 获取直播房间列表 + * @param integer $start 起始拉取房间 + * @param integer $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 + * @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 array $data + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function getGoods($data) + { + $url = "https://api.weixin.qq.com/wxaapi/broadcast/goods/getapproved?access_token=ACCESS_TOKEN"; + return $this->callPostApi($url, $data, true); + } +} \ No newline at end of file diff --git a/src/system/sdks/WeMini/Logistics.php b/src/system/sdks/WeMini/Logistics.php new file mode 100644 index 00000000..80318aa8 --- /dev/null +++ b/src/system/sdks/WeMini/Logistics.php @@ -0,0 +1,179 @@ +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); + } + + /** + * 获取打印员。若需要使用微信打单 PC 软件,才需要调用 + * @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/path/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); + } + + /** + * 配置面单打印员,若需要使用微信打单 PC 软件,才需要调用 + * @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); + } +} \ No newline at end of file diff --git a/src/system/sdks/WeMini/Message.php b/src/system/sdks/WeMini/Message.php new file mode 100644 index 00000000..3bebf7c3 --- /dev/null +++ b/src/system/sdks/WeMini/Message.php @@ -0,0 +1,51 @@ +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/src/system/sdks/WeMini/Newtmpl.php b/src/system/sdks/WeMini/Newtmpl.php new file mode 100644 index 00000000..91e7dcb7 --- /dev/null +++ b/src/system/sdks/WeMini/Newtmpl.php @@ -0,0 +1,130 @@ +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 开发者自行组合好的模板关键词列表,关键词顺序可以自由搭配(例如 [3,5,4] 或 [4,5,3]),最多支持5个,最少2个关键词组合 + * @param string $sceneDesc 服务场景描述,15个字以内 + * @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/src/system/sdks/WeMini/Ocr.php b/src/system/sdks/WeMini/Ocr.php new file mode 100644 index 00000000..a0d524ec --- /dev/null +++ b/src/system/sdks/WeMini/Ocr.php @@ -0,0 +1,90 @@ +callPostApi($url, $data, true); + } + + /** + * 本接口提供基于小程序的营业执照 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_TOCKEN'; + return $this->callPostApi($url, $data, true); + } + + /** + * 本接口提供基于小程序的驾驶证 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_TOCKEN'; + return $this->callPostApi($url, $data, true); + } + + /** + * 本接口提供基于小程序的身份证 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_TOCKEN'; + return $this->callPostApi($url, $data, true); + } + + /** + * 本接口提供基于小程序的通用印刷体 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_TOCKEN'; + return $this->callPostApi($url, $data, true); + } + + /** + * 本接口提供基于小程序的行驶证 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_TOCKEN'; + return $this->callPostApi($url, $data, true); + } +} \ No newline at end of file diff --git a/src/system/sdks/WeMini/Operation.php b/src/system/sdks/WeMini/Operation.php new file mode 100644 index 00000000..c73cacb2 --- /dev/null +++ b/src/system/sdks/WeMini/Operation.php @@ -0,0 +1,26 @@ +callPostApi($url, $data, true); + } +} \ No newline at end of file diff --git a/src/system/sdks/WeMini/Plugs.php b/src/system/sdks/WeMini/Plugs.php new file mode 100644 index 00000000..be7ef124 --- /dev/null +++ b/src/system/sdks/WeMini/Plugs.php @@ -0,0 +1,92 @@ +callPostApi($url, ['action' => 'apply', 'plugin_appid' => $plugin_appid], true); + } + + /** + * 2.查询已添加的插件 + * @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); + } + + /** + * 3.删除已添加的插件 + * @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); + } + + /** + * 4.获取当前所有插件使用方(供插件开发者调用) + * @param integer $page 拉取第page页的数据 + * @param integer $num 表示每页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); + } + + /** + * 5.修改插件使用申请的状态(供插件开发者调用) + * @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/src/system/sdks/WeMini/Poi.php b/src/system/sdks/WeMini/Poi.php new file mode 100644 index 00000000..80f1bcb5 --- /dev/null +++ b/src/system/sdks/WeMini/Poi.php @@ -0,0 +1,73 @@ + $related_name, 'related_credential' => $related_credential, + 'related_address' => $related_address, 'related_proof_material' => $related_proof_material, + ]; + return $this->callPostApi($url, $data, true); + } + + /** + * 查看地点列表 + * @param integer $page 起始页id(从1开始计数) + * @param integer $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/src/system/sdks/WeMini/Qrcode.php b/src/system/sdks/WeMini/Qrcode.php new file mode 100644 index 00000000..fe4d6d78 --- /dev/null +++ b/src/system/sdks/WeMini/Qrcode.php @@ -0,0 +1,98 @@ + "0", "g" => "0", "b" => "0"], $is_hyaline = true, $outType = null) + { + $url = 'https://api.weixin.qq.com/wxa/getwxacode?access_token=ACCESS_TOKEN'; + $this->registerApi($url, __FUNCTION__, func_get_args()); + $data = ['path' => $path, 'width' => $width, 'auto_color' => $auto_color, 'line_color' => $line_color, 'is_hyaline' => $is_hyaline]; + $result = Tools::post($url, Tools::arr2json($data)); + 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); + } + + /** + * 获取小程序码(永久有效) + * 接口B:适用于需要的码数量极多的业务场景 + * @param string $scene 最大32个可见字符,只支持数字 + * @param string $page 必须是已经发布的小程序存在的页面 + * @param integer $width 二维码的宽度 + * @param bool $auto_color 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 + * @param array $line_color auto_color 为 false 时生效 + * @param boolean $is_hyaline 是否需要透明底色 + * @param null|string $outType 输出类型 + * @return array|string + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function createMiniScene($scene, $page, $width = 430, $auto_color = false, $line_color = ["r" => "0", "g" => "0", "b" => "0"], $is_hyaline = true, $outType = null) + { + $url = 'https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=ACCESS_TOKEN'; + $data = ['scene' => $scene, 'width' => $width, 'auto_color' => $auto_color, 'page' => $page, 'line_color' => $line_color, 'is_hyaline' => $is_hyaline]; + $this->registerApi($url, __FUNCTION__, func_get_args()); + $result = Tools::post($url, Tools::arr2json($data)); + 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); + } + + /** + * 获取小程序二维码(永久有效) + * 接口C:适用于需要的码数量较少的业务场景 + * @param string $path 不能为空,最大长度 128 字节 + * @param integer $width 二维码的宽度 + * @param null|string $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()); + $result = Tools::post($url, Tools::arr2json(['path' => $path, 'width' => $width])); + 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); + } +} \ No newline at end of file diff --git a/src/system/sdks/WeMini/Scheme.php b/src/system/sdks/WeMini/Scheme.php new file mode 100644 index 00000000..99689f2e --- /dev/null +++ b/src/system/sdks/WeMini/Scheme.php @@ -0,0 +1,65 @@ +callPostApi($url, $data, true); + } + + /** + * 查询 URL-Scheme + * @param string $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 + * @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/src/system/sdks/WeMini/Search.php b/src/system/sdks/WeMini/Search.php new file mode 100644 index 00000000..8467dafb --- /dev/null +++ b/src/system/sdks/WeMini/Search.php @@ -0,0 +1,25 @@ +callPostApi($url, ['pages' => $pages], true); + } +} \ No newline at end of file diff --git a/src/system/sdks/WeMini/Security.php b/src/system/sdks/WeMini/Security.php new file mode 100644 index 00000000..cc1640c2 --- /dev/null +++ b/src/system/sdks/WeMini/Security.php @@ -0,0 +1,53 @@ +callPostApi($url, ['media' => $media], false); + } + + /** + * 异步校验图片/音频是否含有违法违规内容 + * @param string $media_url + * @param string $media_type + * @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/src/system/sdks/WeMini/Soter.php b/src/system/sdks/WeMini/Soter.php new file mode 100644 index 00000000..569fd654 --- /dev/null +++ b/src/system/sdks/WeMini/Soter.php @@ -0,0 +1,25 @@ +callPostApi($url, $data, true); + } +} \ No newline at end of file diff --git a/src/system/sdks/WeMini/Template.php b/src/system/sdks/WeMini/Template.php new file mode 100644 index 00000000..3f112b70 --- /dev/null +++ b/src/system/sdks/WeMini/Template.php @@ -0,0 +1,90 @@ +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 开发者自行组合好的模板关键词列表,关键词顺序可以自由搭配(例如[3,5,4]或[4,5,3]),最多支持10个关键词组合 + * @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 发送的消息对象数组 + * @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/src/system/sdks/WeMini/Total.php b/src/system/sdks/WeMini/Total.php new file mode 100644 index 00000000..b37e743e --- /dev/null +++ b/src/system/sdks/WeMini/Total.php @@ -0,0 +1,152 @@ +callPostApi($url, ['begin_date' => $begin_date, 'end_date' => $end_date], true); + } + + /** + * 访问分析 + * @param string $begin_date 开始日期 + * @param string $end_date 结束日期,限定查询1天数据,end_date允许设置的最大值为昨日 + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function getWeanalysisAppidDailyVisittrend($begin_date, $end_date) + { + $url = 'https://api.weixin.qq.com/datacube/getweanalysisappiddailyvisittrend?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 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 结束日期,限定查询1天数据,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 结束日期,限定查询1天数据,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 结束日期,限定查询1天数据,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,分别表示查询最近1/7/30天数据,end_date允许设置的最大值为昨日 + * @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/src/system/sdks/WeMini/crypt/errorCode.php b/src/system/sdks/WeMini/crypt/errorCode.php new file mode 100644 index 00000000..1120d31a --- /dev/null +++ b/src/system/sdks/WeMini/crypt/errorCode.php @@ -0,0 +1,19 @@ + + *
  • -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/src/system/sdks/WeMini/crypt/wxBizDataCrypt.php b/src/system/sdks/WeMini/crypt/wxBizDataCrypt.php new file mode 100644 index 00000000..ea80c892 --- /dev/null +++ b/src/system/sdks/WeMini/crypt/wxBizDataCrypt.php @@ -0,0 +1,58 @@ +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/src/system/sdks/WePay/Bill.php b/src/system/sdks/WePay/Bill.php new file mode 100644 index 00000000..1c8d1a82 --- /dev/null +++ b/src/system/sdks/WePay/Bill.php @@ -0,0 +1,50 @@ +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 + * @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/src/system/sdks/WePay/Coupon.php b/src/system/sdks/WePay/Coupon.php new file mode 100644 index 00000000..c8c05088 --- /dev/null +++ b/src/system/sdks/WePay/Coupon.php @@ -0,0 +1,52 @@ +callPostApi($url, $options, true, 'MD5'); + } + + /** + * 查询代金券批次 + * @param array $options + * @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 + * @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/src/system/sdks/WePay/Custom.php b/src/system/sdks/WePay/Custom.php new file mode 100644 index 00000000..c1ac4a83 --- /dev/null +++ b/src/system/sdks/WePay/Custom.php @@ -0,0 +1,54 @@ +callPostApi($url, $options, false, 'MD5', false, false); + } + + /** + * 订单附加信息查询接口 + * @param array $options + * @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 + * @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/src/system/sdks/WePay/Order.php b/src/system/sdks/WePay/Order.php new file mode 100644 index 00000000..a4e2538e --- /dev/null +++ b/src/system/sdks/WePay/Order.php @@ -0,0 +1,160 @@ +callPostApi($url, $options, false, 'MD5'); + } + + /** + * 刷卡支付 + * @param array $options + * @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 + * @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及H5支付参数 + * @param string $prepayId 统一下单预支付码 + * @return array + */ + 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; + } + + /** + * 获取支付规则二维码 + * @param string $productId 商户定义的商品id或者订单号 + * @return string + */ + 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 统一下单预支付码 + * @return array + */ + 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 + * @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 + * @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 + * @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/src/system/sdks/WePay/Redpack.php b/src/system/sdks/WePay/Redpack.php new file mode 100644 index 00000000..c53e7290 --- /dev/null +++ b/src/system/sdks/WePay/Redpack.php @@ -0,0 +1,59 @@ +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 + * @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/src/system/sdks/WePay/Refund.php b/src/system/sdks/WePay/Refund.php new file mode 100644 index 00000000..25525faf --- /dev/null +++ b/src/system/sdks/WePay/Refund.php @@ -0,0 +1,65 @@ +callPostApi($url, $options, true); + } + + /** + * 查询退款 + * @param array $options + * @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); + } + + /** + * 获取退款通知 + * @return array + * @throws \WeChat\Exceptions\InvalidDecryptException + * @throws \WeChat\Exceptions\InvalidResponseException + */ + public function getNotify() + { + $data = Tools::xml2arr(file_get_contents("php://input")); + 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/src/system/sdks/WePay/Transfers.php b/src/system/sdks/WePay/Transfers.php new file mode 100644 index 00000000..b8a14c47 --- /dev/null +++ b/src/system/sdks/WePay/Transfers.php @@ -0,0 +1,48 @@ +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 商户调用企业付款API时使用的商户订单号 + * @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/src/system/sdks/WePay/TransfersBank.php b/src/system/sdks/WePay/TransfersBank.php new file mode 100644 index 00000000..ddff9386 --- /dev/null +++ b/src/system/sdks/WePay/TransfersBank.php @@ -0,0 +1,111 @@ +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); + } + + /** + * 商户企业付款到银行卡操作进行结果查询 + * @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); + } + + /** + * RSA加密处理 + * @param string $string + * @param string $encrypted + * @return string + * @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); + } + + /** + * 获取签名文件内容 + * @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']; + } +} \ No newline at end of file diff --git a/src/system/sdks/WePayV3/Cert.php b/src/system/sdks/WePayV3/Cert.php new file mode 100644 index 00000000..269f8ed6 --- /dev/null +++ b/src/system/sdks/WePayV3/Cert.php @@ -0,0 +1,36 @@ +config['mch_v3_key']); + $result = $this->doRequest('GET', '/v3/certificates'); + foreach ($result['data'] as $vo) { + $this->tmpFile($vo['serial_no'], $aes->decryptToString( + $vo['encrypt_certificate']['associated_data'], + $vo['encrypt_certificate']['nonce'], + $vo['encrypt_certificate']['ciphertext'] + )); + } + } catch (\Exception $exception) { + throw new InvalidResponseException($exception->getMessage(), $exception->getCode()); + } + } +} \ No newline at end of file diff --git a/src/system/sdks/WePayV3/Contracts/BasicWePay.php b/src/system/sdks/WePayV3/Contracts/BasicWePay.php new file mode 100644 index 00000000..d2161b08 --- /dev/null +++ b/src/system/sdks/WePayV3/Contracts/BasicWePay.php @@ -0,0 +1,222 @@ + '', // 微信绑定APPID,需配置 + 'mch_id' => '', // 微信商户编号,需要配置 + 'mch_v3_key' => '', // 微信商户密钥,需要配置 + 'cert_serial' => '', // 商户证书序号,无需配置 + 'cert_public' => '', // 商户公钥内容,需要配置 + 'cert_private' => '', // 商户密钥内容,需要配置 + ]; + + /** + * BasicWePayV3 constructor. + * @param array $options [mch_id, mch_v3_key, cert_public, cert_private] + */ + 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_private'])) { + throw new InvalidArgumentException("Missing Config -- [cert_private]"); + } + if (empty($options['cert_public'])) { + throw new InvalidArgumentException("Missing Config -- [cert_public]"); + } + + 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']; + $this->config['cert_serial'] = openssl_x509_parse($this->config['cert_public'])['serialNumberHex']; + + if (empty($this->config['cert_serial'])) { + throw new InvalidArgumentException("Failed to parse certificate public key"); + } + } + + /** + * 静态创建对象 + * @param array $config + * @return static + */ + 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 $method 请求访问 + * @param string $pathinfo 请求路由 + * @param string $jsondata 请求数据 + * @param bool $verify 是否验证 + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + */ + public function doRequest($method, $pathinfo, $jsondata = '', $verify = false) + { + 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) + ); + list($header, $content) = $this->_doRequestCurl($method, $this->base . $pathinfo, [ + 'data' => $jsondata, 'header' => [ + "Accept: application/json", "Content-Type: application/json", + 'User-Agent: https://thinkadmin.top', "Authorization: WECHATPAY2-SHA256-RSA2048 {$token}", + ], + ]); + 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 { + $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 json_decode($content, true); + } + + /** + * 通过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 签名内容 + * @return string + */ + protected function signBuild($data) + { + $pkeyid = openssl_pkey_get_private($this->config['cert_private']); + openssl_sign($data, $signature, $pkeyid, 'sha256WithRSAEncryption'); + return base64_encode($signature); + } + + /** + * 验证内容签名 + * @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 = '') + { + $cert = $this->tmpFile($serial); + if (empty($cert)) { + Cert::instance($this->config)->download(); + $cert = $this->tmpFile($serial); + } + return @openssl_verify($data, base64_decode($sign), openssl_x509_read($cert), 'sha256WithRSAEncryption'); + } + + /** + * 写入或读取临时文件 + * @param string $name + * @param null|string $content + * @return string + * @throws \WeChat\Exceptions\LocalCacheException + */ + protected function tmpFile($name, $content = null) + { + if (is_null($content)) { + return base64_decode(Tools::getCache($name) ?: ''); + } else { + return Tools::setCache($name, base64_encode($content), 7200); + } + } +} \ No newline at end of file diff --git a/src/system/sdks/WePayV3/Contracts/DecryptAes.php b/src/system/sdks/WePayV3/Contracts/DecryptAes.php new file mode 100644 index 00000000..458731db --- /dev/null +++ b/src/system/sdks/WePayV3/Contracts/DecryptAes.php @@ -0,0 +1,68 @@ +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/src/system/sdks/WePayV3/Order.php b/src/system/sdks/WePayV3/Order.php new file mode 100644 index 00000000..649ea0d9 --- /dev/null +++ b/src/system/sdks/WePayV3/Order.php @@ -0,0 +1,95 @@ + '/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 { + // 创建预支付码 + $result = $this->doRequest('POST', $types[$type], json_encode($data, JSON_UNESCAPED_UNICODE), true); + if (empty($result['prepay_id'])) return $result; + // 支付参数签名 + $time = (string)time(); + $appid = $this->config['appid']; + $prepayId = $result['prepay_id']; + $nonceStr = Tools::createNoncestr(); + if ($type === 'app') { + $sign = $this->signBuild(join("\n", [$appid, $time, $nonceStr, $prepayId, ''])); + return ['partnerId' => $this->config['mch_id'], 'prepayId' => $prepayId, 'package' => 'Sign=WXPay', 'nonceStr' => $nonceStr, 'timeStamp' => $time, 'sign' => $sign]; + } elseif ($type === 'jsapi') { + $sign = $this->signBuild(join("\n", [$appid, $time, $nonceStr, "prepay_id={$prepayId}", ''])); + return ['appId' => $appid, 'timeStamp' => $time, 'nonceStr' => $nonceStr, 'package' => "prepay_id={$prepayId}", 'signType' => 'RSA', 'paySign' => $sign]; + } else { + return $result; + } + } + } + + /** + * 支付订单查询 + * @param string $orderNo 订单单号 + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + */ + public function query($orderNo) + { + $pathinfo = "/v3/pay/transactions/out-trade-no/{$orderNo}"; + return $this->doRequest('GET', "{$pathinfo}?mchid={$this->config['mch_id']}", '', true); + } + + /** + * 支付通知 + * @return array + * @throws \WeChat\Exceptions\InvalidDecryptException + */ + public function notify(array $parameters = []) + { + if (empty($parameters)) { + $body = file_get_contents('php://input'); + $data = json_decode($body, true); + } else { + $data = $parameters; + } + + 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; + } +} diff --git a/src/system/sdks/WePayV3/Refund.php b/src/system/sdks/WePayV3/Refund.php new file mode 100644 index 00000000..0b9e7fac --- /dev/null +++ b/src/system/sdks/WePayV3/Refund.php @@ -0,0 +1,61 @@ +doRequest('POST', '/v3/ecommerce/refunds/apply', json_encode($data, JSON_UNESCAPED_UNICODE), true); + } + + /** + * 退款订单查询 + * @param string $refundNo 退款单号 + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + */ + public function query($refundNo) + { + $pathinfo = "/v3/ecommerce/refunds/out-refund-no/{$refundNo}"; + return $this->doRequest('GET', "{$pathinfo}?sub_mchid={$this->config['mch_id']}", '', true); + } + + /** + * 获取退款通知 + * @return array + * @throws \WeChat\Exceptions\InvalidDecryptException + * @throws \WeChat\Exceptions\InvalidResponseException + */ + public function notify() + { + $data = Tools::xml2arr(file_get_contents("php://input")); + 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/src/system/sdks/_test/alipay-app.php b/src/system/sdks/_test/alipay-app.php new file mode 100644 index 00000000..ea6b61af --- /dev/null +++ b/src/system/sdks/_test/alipay-app.php @@ -0,0 +1,26 @@ +apply([ + 'out_trade_no' => time(), // 商户订单号 + 'total_amount' => '1', // 支付金额 + 'subject' => '支付宝订单标题', // 支付订单描述 + ]); + echo $result; +} catch (\Exception $e) { + echo $e->getMessage(); +} + + diff --git a/src/system/sdks/_test/alipay-bill.php b/src/system/sdks/_test/alipay-bill.php new file mode 100644 index 00000000..e01fa026 --- /dev/null +++ b/src/system/sdks/_test/alipay-bill.php @@ -0,0 +1,24 @@ +apply([ + 'bill_date' => '2020-07-03', // 账单时间(日账单yyyy-MM-dd,月账单 yyyy-MM) + 'bill_type' => 'signcustomer', // 账单类型(trade指商户基于支付宝交易收单的业务账单,signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单) + ]); + echo '
    ';
    +    var_export($result);
    +} catch (Exception $e) {
    +    echo $e->getMessage();
    +}
    \ No newline at end of file
    diff --git a/src/system/sdks/_test/alipay-notify.php b/src/system/sdks/_test/alipay-notify.php
    new file mode 100644
    index 00000000..40ccb9a1
    --- /dev/null
    +++ b/src/system/sdks/_test/alipay-notify.php
    @@ -0,0 +1,27 @@
    +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/src/system/sdks/_test/alipay-pos.php b/src/system/sdks/_test/alipay-pos.php
    new file mode 100644
    index 00000000..41a2f598
    --- /dev/null
    +++ b/src/system/sdks/_test/alipay-pos.php
    @@ -0,0 +1,29 @@
    +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/src/system/sdks/_test/alipay-refund.php b/src/system/sdks/_test/alipay-refund.php
    new file mode 100644
    index 00000000..bf6de6e5
    --- /dev/null
    +++ b/src/system/sdks/_test/alipay-refund.php
    @@ -0,0 +1,27 @@
    +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/src/system/sdks/_test/alipay-scan.php b/src/system/sdks/_test/alipay-scan.php
    new file mode 100644
    index 00000000..ef9ba859
    --- /dev/null
    +++ b/src/system/sdks/_test/alipay-scan.php
    @@ -0,0 +1,28 @@
    +apply([
    +        'out_trade_no' => '14321412', // 订单号
    +        'total_amount' => '13', // 订单金额,单位:元
    +        'subject'      => '订单商品标题', // 订单商品标题
    +    ]);
    +
    +    echo '
    ';
    +    var_export($result);
    +} catch (Exception $e) {
    +    echo $e->getMessage();
    +}
    +
    +
    diff --git a/src/system/sdks/_test/alipay-transfer-account.php b/src/system/sdks/_test/alipay-transfer-account.php
    new file mode 100644
    index 00000000..78776c49
    --- /dev/null
    +++ b/src/system/sdks/_test/alipay-transfer-account.php
    @@ -0,0 +1,25 @@
    +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/src/system/sdks/_test/alipay-transfer-create.php b/src/system/sdks/_test/alipay-transfer-create.php
    new file mode 100644
    index 00000000..6f711126
    --- /dev/null
    +++ b/src/system/sdks/_test/alipay-transfer-create.php
    @@ -0,0 +1,32 @@
    +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/src/system/sdks/_test/alipay-transfer-query.php b/src/system/sdks/_test/alipay-transfer-query.php
    new file mode 100644
    index 00000000..9ef855a9
    --- /dev/null
    +++ b/src/system/sdks/_test/alipay-transfer-query.php
    @@ -0,0 +1,26 @@
    +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/src/system/sdks/_test/alipay-transfer.php b/src/system/sdks/_test/alipay-transfer.php
    new file mode 100644
    index 00000000..f753a5ee
    --- /dev/null
    +++ b/src/system/sdks/_test/alipay-transfer.php
    @@ -0,0 +1,31 @@
    +apply([
    +        'out_biz_no'      => time(), // 订单号
    +        'payee_type'      => 'ALIPAY_LOGONID', // 收款方账户类型(ALIPAY_LOGONID | ALIPAY_USERID)
    +        'payee_account'   => 'demo@sandbox.com', // 收款方账户
    +        'amount'          => '10', // 转账金额
    +        'payer_show_name' => '未寒', // 付款方姓名
    +        'payee_real_name' => '张三', // 收款方真实姓名
    +        'remark'          => '张三', // 转账备注
    +    ]);
    +
    +    echo '
    ';
    +    var_export($result);
    +} catch (Exception $e) {
    +    echo $e->getMessage();
    +}
    +
    diff --git a/src/system/sdks/_test/alipay-wap.php b/src/system/sdks/_test/alipay-wap.php
    new file mode 100644
    index 00000000..289d0131
    --- /dev/null
    +++ b/src/system/sdks/_test/alipay-wap.php
    @@ -0,0 +1,30 @@
    +apply([
    +        'out_trade_no' => time(), // 商户订单号
    +        'total_amount' => '1', // 支付金额
    +        'subject'      => '支付订单描述', // 支付订单描述
    +    ]);
    +
    +    echo $result;
    +} catch (Exception $e) {
    +    echo $e->getMessage();
    +}
    +
    +
    diff --git a/src/system/sdks/_test/alipay-web.php b/src/system/sdks/_test/alipay-web.php
    new file mode 100644
    index 00000000..5db3bc38
    --- /dev/null
    +++ b/src/system/sdks/_test/alipay-web.php
    @@ -0,0 +1,31 @@
    +apply([
    +        'out_trade_no' => time(), // 商户订单号
    +        'total_amount' => '1', // 支付金额
    +        'subject'      => '支付订单描述', // 支付订单描述
    +    ]);
    +
    +    echo $result;
    +} catch (Exception $e) {
    +    echo $e->getMessage();
    +}
    +
    +
    diff --git a/src/system/sdks/_test/alipay.php b/src/system/sdks/_test/alipay.php
    new file mode 100644
    index 00000000..87a7d6fd
    --- /dev/null
    +++ b/src/system/sdks/_test/alipay.php
    @@ -0,0 +1,23 @@
    + true,
    +    // 签名类型(RSA|RSA2)
    +    'sign_type'   => "RSA2",
    +    // 应用ID
    +    'appid'       => '2016090900468879',
    +    // 应用私钥的内容 (1行填写,特别注意:这里的应用私钥通常由支付宝密钥管理工具生成)
    +    'private_key' => 'MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC3pbN7esinxgjE8uxXAsccgGNKIq+PR1LteNTFOy0fsete43ObQCrzd9DO0zaUeBUzpIOnxrKxez7QoZROZMYrinttFZ/V5rbObEM9E5AR5Tv/Fr4IBywoS8ZtN16Xb+fZmibfU91yq9O2RYSvscncU2qEYmmaTenM0QlUO80ZKqPsM5JkgCNdcYZTUeHclWeyER3dSImNtlSKiSBSSTHthb11fkudjzdiUXua0NKVWyYuAOoDMcpXbD6NJmYqEA/iZ/AxtQt08pv0Mow581GPB0Uop5+qA2hCV85DpagE94a067sKcRui0rtkJzHem9k7xVL+2RoFm1fv3RnUkMwhAgMBAAECggEAAetkddzxrfc+7jgPylUIGb8pyoOUTC4Vqs/BgZI9xYAJksNT2QKRsFvHPfItNt4Ocqy8h4tnIL3GCU43C564B4p6AcjhE85GiN/O0BudPOKlfuQQ9mqExqMMHuYeQfz0cmzPDTSGMwWiv9v4KBH2pyvkCCAzNF6uG+rvawb4/NNVuiI7C8Ku/wYsamtbgjMZVOFFdScYgIw1BgA99RUU/fWBLMnTQkoyowSRb9eSmEUHjt/WQt+/QgKAT2WmuX4RhaGy0qcQLbNaJNKXdJ+PVhQrSiasINNtqYMa8GsQuuKsk3X8TCg9K6/lowivt5ruhyWcP2sx93zY/LGzIHgHcQKBgQDoZlcs9RWxTdGDdtH8kk0J/r+QtMijNzWI0a+t+ZsWOyd3rw+uM/8O4JTNP4Y98TvvxhJXewITbfiuOIbW1mxh8bnO/fcz7+RXZKgPDeoTeNo717tZFZGBEyUdH9M9Inqvht7+hjVDIMCYBDomYebdk3Xqo4mDBjLRdVNGrhGmVQKBgQDKS/MgTMK8Ktfnu1KzwCbn/FfHTOrp1a1t1wWPv9AW0rJPYeaP6lOkgIoO/1odG9qDDhdB6njqM+mKY5Yr3N94PHamHbwJUCmbkqEunCWpGzgcQZ1Q254xk9D7UKq/XUqW2WDqDq80GQeNial+fBc46yelQzokwdA+JdIFKoyinQKBgQCBems9V/rTAtkk1nFdt6EGXZEbLS3PiXXhGXo4gqV+OEzf6H/i/YMwJb2hsK+5GQrcps0XQihA7PctEb9GOMa/tu5fva0ZmaDtc94SLR1p5d4okyQFGPgtIp594HpPSEN0Qb9BrUJFeRz0VP6U3dzDPGHo7V4yyqRLgIN6EIcy1QKBgAqdh6mHPaTAHspDMyjJiYEc5cJIj/8rPkmIQft0FkhMUB0IRyAALNlyAUyeK61hW8sKvz+vPR8VEEk5xpSQp41YpuU6pDZc5YILZLfca8F+8yfQbZ/jll6Foi694efezl4yE/rUQG9cbOAJfEJt4o4TEOaEK5XoMbRBKc8pl22lAoGARTq0qOr9SStihRAy9a+8wi2WEwL4QHcmOjH7iAuJxy5b5TRDSjlk6h+0dnTItiFlTXdfpO8KhWA8EoSJVBZ1kcACQDFgMIA+VM+yXydtzMotOn21W4stfZ4I6dHFiujMsnKpNYVpQh3oCrJf4SeXiQDdiSCodqb1HlKkEc6naHQ=',
    +    // 支付宝公钥内容 (1行填写,特别注意:这里不是应用公钥而是支付宝公钥,通常是上传应用公钥换取支付宝公钥,在网页可以复制)
    +    'public_key'  => 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtU71NY53UDGY7JNvLYAhsNa+taTF6KthIHJmGgdio9bkqeJGhHk6ttkTKkLqFgwIfgAkHpdKiOv1uZw6gVGZ7TCu5LfHTqKrCd6Uz+N7hxhY+4IwicLgprcV1flXQLmbkJYzFMZqkXGkSgOsR2yXh4LyQZczgk9N456uuzGtRy7MoB4zQy34PLUkkxR6W1B2ftNbLRGXv6tc7p/cmDcrY6K1bSxnGmfRxFSb8lRfhe0V0UM6pKq2SGGSeovrKHN0OLp+Nn5wcULVnFgATXGCENshRlp96piPEBFwneXs19n+sX1jx60FTR7/rME3sW3AHug0fhZ9mSqW4x401WjdnwIDAQAB',
    +    // 应用公钥的内容(新版资金类接口转 app_cert_sn)
    +    'app_cert'    => '',
    +    // 支付宝根证书内容(新版资金类接口转 alipay_root_cert_sn)
    +    'root_cert'   => '',
    +    // 支付成功通知地址
    +    'notify_url'  => '',
    +    // 网页支付回跳地址
    +    'return_url'  => '',
    +];
    \ No newline at end of file
    diff --git a/src/system/sdks/_test/config.php b/src/system/sdks/_test/config.php
    new file mode 100644
    index 00000000..dea012fa
    --- /dev/null
    +++ b/src/system/sdks/_test/config.php
    @@ -0,0 +1,34 @@
    + function ($name, $value, $expired = 360) {
    +//        var_dump(func_get_args());
    +//    },
    +//    'get' => function ($name) {
    +//        var_dump(func_get_args());
    +//    },
    +//    'del' => function ($name) {
    +//        var_dump(func_get_args());
    +//    },
    +//    'put' => function ($name) {
    +//        var_dump(func_get_args());
    +//    },
    +//];
    +
    +return [
    +    'token'          => 'test',
    +    'appid'          => 'wx60a43dd8161666d4',
    +    'appsecret'      => 'b4e28746f1bd73b5c6684f5e01883c36',
    +    'encodingaeskey' => 'BJIUzE0gqlWy0GxfPp4J1oPTBmOrNDIGPNav1YFH5Z5',
    +    // 配置商户支付参数
    +    '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/src/system/sdks/_test/mini-login.php b/src/system/sdks/_test/mini-login.php
    new file mode 100644
    index 00000000..39adc432
    --- /dev/null
    +++ b/src/system/sdks/_test/mini-login.php
    @@ -0,0 +1,23 @@
    + '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/src/system/sdks/_test/mini-qrc.php b/src/system/sdks/_test/mini-qrc.php
    new file mode 100644
    index 00000000..7301ead5
    --- /dev/null
    +++ b/src/system/sdks/_test/mini-qrc.php
    @@ -0,0 +1,24 @@
    + '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/src/system/sdks/_test/pay-download-bill.php b/src/system/sdks/_test/pay-download-bill.php
    new file mode 100644
    index 00000000..7dcc2734
    --- /dev/null
    +++ b/src/system/sdks/_test/pay-download-bill.php
    @@ -0,0 +1,30 @@
    + '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/src/system/sdks/_test/pay-order-close.php b/src/system/sdks/_test/pay-order-close.php
    new file mode 100644
    index 00000000..1d6500a4
    --- /dev/null
    +++ b/src/system/sdks/_test/pay-order-close.php
    @@ -0,0 +1,27 @@
    +closeOrder($options);
    +
    +    var_export($result);
    +
    +} catch (Exception $e) {
    +
    +    // 出错啦,处理下吧
    +    echo $e->getMessage() . PHP_EOL;
    +
    +}
    \ No newline at end of file
    diff --git a/src/system/sdks/_test/pay-order-create.php b/src/system/sdks/_test/pay-order-create.php
    new file mode 100644
    index 00000000..bc848693
    --- /dev/null
    +++ b/src/system/sdks/_test/pay-order-create.php
    @@ -0,0 +1,43 @@
    + '测试商品',
    +        'out_trade_no'     => time(),
    +        'total_fee'        => '1',
    +        'openid'           => 'o38gpszoJoC9oJYz3UHHf6bEp0Lo',
    +        'trade_type'       => 'JSAPI',
    +        'notify_url'       => 'http://a.com/text.html',
    +        'spbill_create_ip' => '127.0.0.1',
    +    ];
    +    // 生成预支付码
    +    $result = $wechat->createOrder($options);
    +    // 创建JSAPI参数签名
    +    $options = $wechat->createParamsForJsApi($result['prepay_id']);
    +
    +    echo '
    ';
    +    echo "\n--- 创建预支付码 ---\n";
    +    var_export($result);
    +
    +    echo "\n\n--- JSAPI 及 H5 参数 ---\n";
    +    var_export($options);
    +
    +} catch (Exception $e) {
    +
    +    // 出错啦,处理下吧
    +    echo $e->getMessage() . PHP_EOL;
    +
    +}
    \ No newline at end of file
    diff --git a/src/system/sdks/_test/pay-order-notify.php b/src/system/sdks/_test/pay-order-notify.php
    new file mode 100644
    index 00000000..f32ce667
    --- /dev/null
    +++ b/src/system/sdks/_test/pay-order-notify.php
    @@ -0,0 +1,32 @@
    +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/src/system/sdks/_test/pay-order-query.php b/src/system/sdks/_test/pay-order-query.php
    new file mode 100644
    index 00000000..9e96c3c8
    --- /dev/null
    +++ b/src/system/sdks/_test/pay-order-query.php
    @@ -0,0 +1,30 @@
    + '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/src/system/sdks/_test/pay-redpack-create.php b/src/system/sdks/_test/pay-redpack-create.php
    new file mode 100644
    index 00000000..1997ed20
    --- /dev/null
    +++ b/src/system/sdks/_test/pay-redpack-create.php
    @@ -0,0 +1,41 @@
    + 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/src/system/sdks/_test/pay-refund-create.php b/src/system/sdks/_test/pay-refund-create.php
    new file mode 100644
    index 00000000..03ebb0bf
    --- /dev/null
    +++ b/src/system/sdks/_test/pay-refund-create.php
    @@ -0,0 +1,32 @@
    + '1008450740201411110005820873',
    +        'out_refund_no'  => '商户退款单号',
    +        '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/src/system/sdks/_test/pay-refund-query.php b/src/system/sdks/_test/pay-refund-query.php
    new file mode 100644
    index 00000000..03f302de
    --- /dev/null
    +++ b/src/system/sdks/_test/pay-refund-query.php
    @@ -0,0 +1,32 @@
    + '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/src/system/sdks/_test/pay-transfers-create.php b/src/system/sdks/_test/pay-transfers-create.php
    new file mode 100644
    index 00000000..0ebe0ca1
    --- /dev/null
    +++ b/src/system/sdks/_test/pay-transfers-create.php
    @@ -0,0 +1,36 @@
    + 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/src/system/sdks/_test/pay-transfersbank-create.php b/src/system/sdks/_test/pay-transfersbank-create.php
    new file mode 100644
    index 00000000..d2145979
    --- /dev/null
    +++ b/src/system/sdks/_test/pay-transfersbank-create.php
    @@ -0,0 +1,34 @@
    + 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/src/system/sdks/_test/pay-v3-config-cert.php b/src/system/sdks/_test/pay-v3-config-cert.php
    new file mode 100644
    index 00000000..47883d13
    --- /dev/null
    +++ b/src/system/sdks/_test/pay-v3-config-cert.php
    @@ -0,0 +1,16 @@
    +download();
    +
    +} catch (\Exception $exception) {
    +    // 出错啦,处理下吧
    +    echo $exception->getMessage() . PHP_EOL;
    +}
    \ No newline at end of file
    diff --git a/src/system/sdks/_test/pay-v3-config.php b/src/system/sdks/_test/pay-v3-config.php
    new file mode 100644
    index 00000000..fcbaebe1
    --- /dev/null
    +++ b/src/system/sdks/_test/pay-v3-config.php
    @@ -0,0 +1,25 @@
    + '',
    +    // 微信商户编号ID
    +    'mch_id'       => '',
    +    // 微信商户V3接口密钥
    +    'mch_v3_key'   => '',
    +    // 微信商户证书公钥,支持证书内容或文件路径
    +    'cert_public'  => $certPublic,
    +    // 微信商户证书私钥,支持证书内容或文件路径
    +    'cert_private' => $certPrivate,
    +];
    \ No newline at end of file
    diff --git a/src/system/sdks/_test/pay-v3-order-create.php b/src/system/sdks/_test/pay-v3-order-create.php
    new file mode 100644
    index 00000000..5448b6af
    --- /dev/null
    +++ b/src/system/sdks/_test/pay-v3-order-create.php
    @@ -0,0 +1,30 @@
    +create('jsapi', [
    +        'appid'        => 'wx60a43dd8161666d4',
    +        'mchid'        => $config['mch_id'],
    +        'description'  => '商品描述',
    +        'out_trade_no' => (string)time(),
    +        'notify_url'   => 'https://thinkadmin.top',
    +        'payer'        => ['openid' => 'o38gps3vNdCqaggFfrBRCRikwlWY'],
    +        'amount'       => ['total' => 2, 'currency' => 'CNY'],
    +    ]);
    +
    +    echo '
    ';
    +    echo "\n--- 创建支付参数 ---\n";
    +    var_export($result);
    +
    +} catch (\Exception $exception) {
    +    // 出错啦,处理下吧
    +    echo $exception->getMessage() . PHP_EOL;
    +}
    \ No newline at end of file
    diff --git a/src/system/sdks/_test/wechat-jssdk-sign.php b/src/system/sdks/_test/wechat-jssdk-sign.php
    new file mode 100644
    index 00000000..cf48f9ac
    --- /dev/null
    +++ b/src/system/sdks/_test/wechat-jssdk-sign.php
    @@ -0,0 +1,26 @@
    +getJsSign('http://a.com/test.php');
    +
    +    var_export($result);
    +
    +} catch (Exception $e) {
    +
    +    // 出错啦,处理下吧
    +    echo $e->getMessage() . PHP_EOL;
    +
    +}
    \ No newline at end of file
    diff --git a/src/system/sdks/_test/wechat-menu-get.php b/src/system/sdks/_test/wechat-menu-get.php
    new file mode 100644
    index 00000000..e2a9e4a3
    --- /dev/null
    +++ b/src/system/sdks/_test/wechat-menu-get.php
    @@ -0,0 +1,26 @@
    +get();
    +
    +    var_export($result);
    +
    +} catch (Exception $e) {
    +
    +    // 出错啦,处理下吧
    +    echo $e->getMessage() . PHP_EOL;
    +
    +}
    \ No newline at end of file
    diff --git a/src/system/sdks/_test/wechat-qrcode-create.php b/src/system/sdks/_test/wechat-qrcode-create.php
    new file mode 100644
    index 00000000..c33cb7e7
    --- /dev/null
    +++ b/src/system/sdks/_test/wechat-qrcode-create.php
    @@ -0,0 +1,30 @@
    +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/src/system/sdks/_test/wechat-user-get.php b/src/system/sdks/_test/wechat-user-get.php
    new file mode 100644
    index 00000000..e945e6f7
    --- /dev/null
    +++ b/src/system/sdks/_test/wechat-user-get.php
    @@ -0,0 +1,33 @@
    +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/src/system/sdks/_test/work-config.php b/src/system/sdks/_test/work-config.php
    new file mode 100644
    index 00000000..de3e19b7
    --- /dev/null
    +++ b/src/system/sdks/_test/work-config.php
    @@ -0,0 +1,6 @@
    + '', // 企业ID
    +    'appsecret' => '', // 应用的凭证密钥
    +];
    \ No newline at end of file
    diff --git a/src/system/sdks/_test/work-department.php b/src/system/sdks/_test/work-department.php
    new file mode 100644
    index 00000000..41593d03
    --- /dev/null
    +++ b/src/system/sdks/_test/work-department.php
    @@ -0,0 +1,18 @@
    +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/src/system/sdks/include.php b/src/system/sdks/include.php new file mode 100644 index 00000000..d4ed6eab --- /dev/null +++ b/src/system/sdks/include.php @@ -0,0 +1,15 @@ + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE.