Browse Source

增加支付相关sdk

tags/6.2.5
tianya 2 years ago
parent
commit
a3a3393f77
100 changed files with 10055 additions and 56 deletions
  1. +55
    -6
      src/admin/sys_payment.php
  2. +190
    -27
      src/admin/templets/sys_payment.htm
  3. +0
    -3
      src/install/sql-dfdata.txt
  4. +12
    -16
      src/install/sql-dftables.txt
  5. +18
    -1
      src/install/update.txt
  6. +9
    -3
      src/system/common.inc.php
  7. +9
    -0
      src/system/sdks/.gitignore
  8. +35
    -0
      src/system/sdks/AliPay/App.php
  9. +34
    -0
      src/system/sdks/AliPay/Bill.php
  10. +35
    -0
      src/system/sdks/AliPay/Pos.php
  11. +34
    -0
      src/system/sdks/AliPay/Scan.php
  12. +67
    -0
      src/system/sdks/AliPay/Trade.php
  13. +91
    -0
      src/system/sdks/AliPay/Transfer.php
  14. +34
    -0
      src/system/sdks/AliPay/Wap.php
  15. +34
    -0
      src/system/sdks/AliPay/Web.php
  16. +134
    -0
      src/system/sdks/We.php
  17. +659
    -0
      src/system/sdks/WeChat/Card.php
  18. +356
    -0
      src/system/sdks/WeChat/Contracts/BasicAliPay.php
  19. +218
    -0
      src/system/sdks/WeChat/Contracts/BasicPushEvent.php
  20. +235
    -0
      src/system/sdks/WeChat/Contracts/BasicWeChat.php
  21. +194
    -0
      src/system/sdks/WeChat/Contracts/BasicWePay.php
  22. +28
    -0
      src/system/sdks/WeChat/Contracts/BasicWeWork.php
  23. +115
    -0
      src/system/sdks/WeChat/Contracts/DataArray.php
  24. +181
    -0
      src/system/sdks/WeChat/Contracts/DataError.php
  25. +65
    -0
      src/system/sdks/WeChat/Contracts/MyCurlFile.php
  26. +177
    -0
      src/system/sdks/WeChat/Contracts/Prpcrypt.php
  27. +458
    -0
      src/system/sdks/WeChat/Contracts/Tools.php
  28. +233
    -0
      src/system/sdks/WeChat/Custom.php
  29. +118
    -0
      src/system/sdks/WeChat/Draft.php
  30. +27
    -0
      src/system/sdks/WeChat/Exceptions/InvalidArgumentException.php
  31. +27
    -0
      src/system/sdks/WeChat/Exceptions/InvalidDecryptException.php
  32. +27
    -0
      src/system/sdks/WeChat/Exceptions/InvalidInstanceException.php
  33. +28
    -0
      src/system/sdks/WeChat/Exceptions/InvalidResponseException.php
  34. +29
    -0
      src/system/sdks/WeChat/Exceptions/LocalCacheException.php
  35. +88
    -0
      src/system/sdks/WeChat/Freepublish.php
  36. +52
    -0
      src/system/sdks/WeChat/Limit.php
  37. +188
    -0
      src/system/sdks/WeChat/Media.php
  38. +95
    -0
      src/system/sdks/WeChat/Menu.php
  39. +86
    -0
      src/system/sdks/WeChat/Oauth.php
  40. +218
    -0
      src/system/sdks/WeChat/Pay.php
  41. +162
    -0
      src/system/sdks/WeChat/Product.php
  42. +63
    -0
      src/system/sdks/WeChat/Qrcode.php
  43. +151
    -0
      src/system/sdks/WeChat/Receive.php
  44. +186
    -0
      src/system/sdks/WeChat/Scan.php
  45. +102
    -0
      src/system/sdks/WeChat/Script.php
  46. +350
    -0
      src/system/sdks/WeChat/Shake.php
  47. +111
    -0
      src/system/sdks/WeChat/Tags.php
  48. +97
    -0
      src/system/sdks/WeChat/Template.php
  49. +134
    -0
      src/system/sdks/WeChat/User.php
  50. +271
    -0
      src/system/sdks/WeChat/Wifi.php
  51. +107
    -0
      src/system/sdks/WeMini/Crypt.php
  52. +158
    -0
      src/system/sdks/WeMini/Delivery.php
  53. +512
    -0
      src/system/sdks/WeMini/Guide.php
  54. +55
    -0
      src/system/sdks/WeMini/Image.php
  55. +156
    -0
      src/system/sdks/WeMini/Live.php
  56. +179
    -0
      src/system/sdks/WeMini/Logistics.php
  57. +51
    -0
      src/system/sdks/WeMini/Message.php
  58. +130
    -0
      src/system/sdks/WeMini/Newtmpl.php
  59. +90
    -0
      src/system/sdks/WeMini/Ocr.php
  60. +26
    -0
      src/system/sdks/WeMini/Operation.php
  61. +92
    -0
      src/system/sdks/WeMini/Plugs.php
  62. +73
    -0
      src/system/sdks/WeMini/Poi.php
  63. +98
    -0
      src/system/sdks/WeMini/Qrcode.php
  64. +65
    -0
      src/system/sdks/WeMini/Scheme.php
  65. +25
    -0
      src/system/sdks/WeMini/Search.php
  66. +53
    -0
      src/system/sdks/WeMini/Security.php
  67. +25
    -0
      src/system/sdks/WeMini/Soter.php
  68. +90
    -0
      src/system/sdks/WeMini/Template.php
  69. +152
    -0
      src/system/sdks/WeMini/Total.php
  70. +19
    -0
      src/system/sdks/WeMini/crypt/errorCode.php
  71. +58
    -0
      src/system/sdks/WeMini/crypt/wxBizDataCrypt.php
  72. +50
    -0
      src/system/sdks/WePay/Bill.php
  73. +52
    -0
      src/system/sdks/WePay/Coupon.php
  74. +54
    -0
      src/system/sdks/WePay/Custom.php
  75. +160
    -0
      src/system/sdks/WePay/Order.php
  76. +59
    -0
      src/system/sdks/WePay/Redpack.php
  77. +65
    -0
      src/system/sdks/WePay/Refund.php
  78. +48
    -0
      src/system/sdks/WePay/Transfers.php
  79. +111
    -0
      src/system/sdks/WePay/TransfersBank.php
  80. +36
    -0
      src/system/sdks/WePayV3/Cert.php
  81. +222
    -0
      src/system/sdks/WePayV3/Contracts/BasicWePay.php
  82. +68
    -0
      src/system/sdks/WePayV3/Contracts/DecryptAes.php
  83. +95
    -0
      src/system/sdks/WePayV3/Order.php
  84. +61
    -0
      src/system/sdks/WePayV3/Refund.php
  85. +26
    -0
      src/system/sdks/_test/alipay-app.php
  86. +24
    -0
      src/system/sdks/_test/alipay-bill.php
  87. +27
    -0
      src/system/sdks/_test/alipay-notify.php
  88. +29
    -0
      src/system/sdks/_test/alipay-pos.php
  89. +27
    -0
      src/system/sdks/_test/alipay-refund.php
  90. +28
    -0
      src/system/sdks/_test/alipay-scan.php
  91. +25
    -0
      src/system/sdks/_test/alipay-transfer-account.php
  92. +32
    -0
      src/system/sdks/_test/alipay-transfer-create.php
  93. +26
    -0
      src/system/sdks/_test/alipay-transfer-query.php
  94. +31
    -0
      src/system/sdks/_test/alipay-transfer.php
  95. +30
    -0
      src/system/sdks/_test/alipay-wap.php
  96. +31
    -0
      src/system/sdks/_test/alipay-web.php
  97. +23
    -0
      src/system/sdks/_test/alipay.php
  98. +34
    -0
      src/system/sdks/_test/config.php
  99. +23
    -0
      src/system/sdks/_test/mini-login.php
  100. +24
    -0
      src/system/sdks/_test/mini-qrc.php

+ 55
- 6
src/admin/sys_payment.php View File

@@ -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');

?>

+ 190
- 27
src/admin/templets/sys_payment.htm View File

@@ -1,5 +1,6 @@
<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
@@ -10,6 +11,7 @@
<script src="../static/web/js/jquery.min.js"></script>
<script src="../static/web/js/bootstrap.min.js"></script>
</head>

<body>
<table cellpadding="3" cellspacing="1" align="center" class="table maintable my-3">
<tr>
@@ -17,7 +19,8 @@
<table cellspacing="0" cellpadding="0" class="table table-borderless w-100">
<tr>
<td>支付接口设置</td>
<td align="right"><span><a href="https://www.dedebiz.com/help" target="_blank" class="btn btn-success btn-sm">更多接口</a></span></td>
<td align="right"><span><a href="https://www.dedebiz.com/help" target="_blank"
class="btn btn-success btn-sm">更多接口</a></span></td>
</tr>
</table>
</td>
@@ -26,19 +29,24 @@
<td colspan="4">
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li class="nav-item" role="presentation">
<button type="button" id="wechat-tab" class="nav-link active" data-toggle="tab" data-target="#wechat" role="tab" aria-controls="wechat" aria-selected="true">微信支付</button>
<button type="button" id="wechat-tab" class="nav-link active" data-toggle="tab"
data-target="#wechat" role="tab" aria-controls="wechat" aria-selected="true">微信支付</button>
</li>
<li class="nav-item" role="presentation">
<button type="button" id="alipay-tab" class="nav-link" data-toggle="tab" data-target="#alipay" role="tab" aria-controls="alipay" aria-selected="false">支付宝</button>
<button type="button" id="alipay-tab" class="nav-link" data-toggle="tab" data-target="#alipay"
role="tab" aria-controls="alipay" aria-selected="false">支付宝</button>
</li>
<li class="nav-item" role="presentation">
<button type="button" id="bank-tab" class="nav-link" data-toggle="tab" data-target="#bank" role="tab" aria-controls="bank" aria-selected="false">银行转帐</button>
<button type="button" id="bank-tab" class="nav-link" data-toggle="tab" data-target="#bank"
role="tab" aria-controls="bank" aria-selected="false">银行转帐</button>
</li>
<li class="nav-item" role="presentation">
<button type="button" id="balance-tab" class="nav-link" data-toggle="tab" data-target="#balance" role="tab" aria-controls="balance" aria-selected="false">余额支付</button>
<button type="button" id="balance-tab" class="nav-link" data-toggle="tab" data-target="#balance"
role="tab" aria-controls="balance" aria-selected="false">余额支付</button>
</li>
<li class="nav-item" role="presentation">
<button type="button" id="cod-tab" class="nav-link" data-toggle="tab" data-target="#cod" role="tab" aria-controls="cod" aria-selected="false">货到付款</button>
<button type="button" id="cod-tab" class="nav-link" data-toggle="tab" data-target="#cod"
role="tab" aria-controls="cod" aria-selected="false">货到付款</button>
</li>
</ul>
<div class="tab-content py-3" id="myTabContent">
@@ -46,7 +54,8 @@
<form>
<div class="form-group">
<label>描述</label>
<p>微信支付是腾讯公司的支付业务品牌,微信支付商户平台支持线下场所、公众号、小程序、PC网站、APP、企业微信等经营场景快速接入微信支付。微信支付全面打通O2O生活消费领域,提供专业的互联网+行业解决方案,微信支付支持微信红包和微信理财通,是移动支付的首选。</p>
<p>微信支付是腾讯公司的支付业务品牌,微信支付商户平台支持线下场所、公众号、小程序、PC网站、APP、企业微信等经营场景快速接入微信支付。微信支付全面打通O2O生活消费领域,提供专业的互联网+行业解决方案,微信支付支持微信红包和微信理财通,是移动支付的首选。
</p>
</div>
<div class="form-group">
<label for="iptWechatAppID">AppID</label>
@@ -66,18 +75,23 @@
</div>
<div class="form-group">
<label for="iptWechatSerialNo">商户证书序列号</label>
<input type="appid" class="form-control" id="iptWechatSerialNo">
<input type="text" class="form-control" id="iptWechatSerialNo">
</div>
<div class="form-group">
<label>商户证书文件</label>
<div class="custom-file">
<input type="file" class="custom-file-input" id="iptWechatPrivateKey">
<label class="custom-file-label" data-browse="选择" for="iptWechatPrivateKey">选择商户证书文件...</label>
<label class="custom-file-label" data-browse="选择"
for="iptWechatPrivateKey">选择商户证书文件...</label>
</div>
</div>
<div class="form-group">
<label for="iptWechatSortrank">排序(越小越靠前)</label>
<input type="text" class="form-control" id="iptWechatSortrank" value="0">
</div>
<div class="form-group form-check">
<input type="checkbox" class="form-check-input" id="WechatEnabled">
<label class="form-check-label" for="WechatEnabled"> 是否启用</label>
<input type="checkbox" class="form-check-input" id="iptWechatEnabled">
<label class="form-check-label" for="iptWechatEnabled"> 是否启用</label>
</div>
</form>
</div>
@@ -85,7 +99,8 @@
<form>
<div class="form-group">
<label for="iptAppID">描述</label>
<p>支付宝,全球领先的独立第三方支付平台,致力于为广大用户提供安全快速的电子支付/网上支付/安全支付/手机支付体验,及转账收款/水电煤缴费/信用卡还款/AA收款等生活服务应用。</p>
<p>支付宝,全球领先的独立第三方支付平台,致力于为广大用户提供安全快速的电子支付/网上支付/安全支付/手机支付体验,及转账收款/水电煤缴费/信用卡还款/AA收款等生活服务应用。
</p>
</div>
<div class="form-group">
<label for="iptAlipayAPPID">APPID</label>
@@ -99,30 +114,37 @@
<label>应用公钥证书</label>
<div class="custom-file">
<input type="file" class="custom-file-input" id="iptAlipayAppCertPublicKey">
<label class="custom-file-label" data-browse="选择" for="iptAlipayAppCertPublicKey">选择应用公钥证书文件...</label>
<label class="custom-file-label" data-browse="选择"
for="iptAlipayAppCertPublicKey">选择应用公钥证书文件...</label>
</div>
</div>
<div class="form-group">
<label>支付宝公钥证书</label>
<div class="custom-file">
<input type="file" class="custom-file-input" id="iptAlipayCertPublicKey">
<label class="custom-file-label" data-browse="选择" for="iptAlipayCertPublicKey">选择支付宝公钥证书文件...</label>
<label class="custom-file-label" data-browse="选择"
for="iptAlipayCertPublicKey">选择支付宝公钥证书文件...</label>
</div>
</div>
<div class="form-group">
<label>支付宝根证书</label>
<div class="custom-file">
<input type="file" class="custom-file-input" id="iptAlipayRootCert">
<label class="custom-file-label" data-browse="选择" for="iptAlipayRootCert">选择支付宝根证书文件...</label>
<label class="custom-file-label" data-browse="选择"
for="iptAlipayRootCert">选择支付宝根证书文件...</label>
</div>
</div>
<div class="form-group">
<label for="iptAlipaySignType">签名类型</label>
<input type="text" class="form-control" id="iptAlipaySignType" value="RSA2" disabled>
</div>
<div class="form-group">
<label for="iptAlipaySortrank">排序(越小越靠前)</label>
<input type="text" class="form-control" id="iptAlipaySortrank" value="0">
</div>
<div class="form-group form-check">
<input type="checkbox" class="form-check-input" id="AlipayEnabled">
<label class="form-check-label" for="AlipayEnabled"> 是否启用</label>
<input type="checkbox" class="form-check-input" id="iptAlipayEnabled">
<label class="form-check-label" for="iptAlipayEnabled"> 是否启用</label>
</div>
</form>
</div>
@@ -134,19 +156,23 @@
</div>
<div class="form-group">
<label for="iptBankAccountName">账户名</label>
<input type="appid" class="form-control" id="iptBankAccountName">
<input type="text" class="form-control" id="iptBankAccountName">
</div>
<div class="form-group">
<label for="iptBankAccountNO">账号</label>
<input type="appid" class="form-control" id="iptBankAccountNO">
<input type="text" class="form-control" id="iptBankAccountNO">
</div>
<div class="form-group">
<label for="iptBankName">开户行</label>
<input type="appid" class="form-control" id="iptBankName">
<input type="text" class="form-control" id="iptBankName">
</div>
<div class="form-group">
<label for="iptBankSortrank">排序(越小越靠前)</label>
<input type="text" class="form-control" id="iptBankSortrank" value="0">
</div>
<div class="form-group form-check">
<input type="checkbox" class="form-check-input" id="BankEnabled">
<label class="form-check-label" for="BankEnabled"> 是否启用</label>
<input type="checkbox" class="form-check-input" id="iptBankEnabled">
<label class="form-check-label" for="iptBankEnabled"> 是否启用</label>
</div>
</form>
</div>
@@ -156,9 +182,13 @@
<label>描述</label>
<p>支持采用积分进行支付。</p>
</div>
<div class="form-group">
<label for="iptBalanceSortrank">排序(越小越靠前)</label>
<input type="text" class="form-control" id="iptBalanceSortrank" value="0">
</div>
<div class="form-group form-check">
<input type="checkbox" class="form-check-input" id="BalanceEnabled">
<label class="form-check-label" for="BalanceEnabled">是否启用</label>
<input type="checkbox" class="form-check-input" id="iptBalanceEnabled">
<label class="form-check-label" for="iptBalanceEnabled">是否启用</label>
</div>
</form>
</div>
@@ -168,13 +198,18 @@
<label>描述</label>
<p>购买后直接发货,到货后再进行支付。</p>
</div>
<div class="form-group">
<label for="iptCodSortrank">排序(越小越靠前)</label>
<input type="text" class="form-control" id="iptCodSortrank" value="0">
</div>
<div class="form-group form-check">
<input type="checkbox" class="form-check-input" id="CodEnabled">
<label class="form-check-label" for="CodEnabled"> 是否启用</label>
<input type="checkbox" class="form-check-input" id="iptCodEnabled">
<label class="form-check-label" for="iptCodEnabled"> 是否启用</label>
</div>
</form>
</div>
<div class="text-center"><button type="submit" class="btn btn-success">保存</button></div>
<div class="text-center"><button id="btnSave" type="submit" class="btn btn-success">保存</button>
</div>
</div>
</td>
</tr>
@@ -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)
})
})

</script>
</body>

</html>

+ 0
- 3
src/install/sql-dfdata.txt View File

@@ -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','<p>如果这个软件总是不能下载的请点击报告错误合作<br>\t\n下载本站资源,如果服务器暂不能下载请过一段时间重试<br>\t\n如果遇到什么问题,请联系我们,我们将在那里提供更多,更好的资源</p>','0','0','0','0');
INSERT INTO `#@__stepselect` VALUES ('1','血型','blood','1','1'),


+ 12
- 16
src/install/sql-dftables.txt View File

@@ -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,


+ 18
- 1
src/install/update.txt View File

@@ -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');
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);

+ 9
- 3
src/system/common.inc.php View File

@@ -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}");
}
}
}
//自动加载类库处理


+ 9
- 0
src/system/sdks/.gitignore View File

@@ -0,0 +1,9 @@
/.git
/.idea
/.DS_Store
/vendor
/Cache
/Test/cert
/nbproject
/composer.lock
/_test/cert

+ 35
- 0
src/system/sdks/AliPay/App.php View File

@@ -0,0 +1,35 @@
<?php
namespace AliPay;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicAliPay;

/**
* 支付宝App支付网关
* Class App
* @package AliPay
*/
class App extends BasicAliPay
{

/**
* App constructor.
* @param array $options
*/
public function __construct(array $options)
{
parent::__construct($options);
$this->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());
}
}

+ 34
- 0
src/system/sdks/AliPay/Bill.php View File

@@ -0,0 +1,34 @@
<?php
namespace AliPay;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicAliPay;

/**
* 支付宝电子面单下载
* Class Bill
* @package AliPay
*/
class Bill extends BasicAliPay
{
/**
* Bill constructor.
* @param array $options
*/
public function __construct(array $options)
{
parent::__construct($options);
$this->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);
}
}

+ 35
- 0
src/system/sdks/AliPay/Pos.php View File

@@ -0,0 +1,35 @@
<?php
namespace AliPay;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicAliPay;

/**
* 支付宝刷卡支付
* Class Pos
* @package AliPay
*/
class Pos extends BasicAliPay
{
/**
* Pos constructor.
* @param array $options
*/
public function __construct(array $options)
{
parent::__construct($options);
$this->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);
}
}

+ 34
- 0
src/system/sdks/AliPay/Scan.php View File

@@ -0,0 +1,34 @@
<?php
namespace AliPay;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicAliPay;

/**
* 支付宝扫码支付
* Class Scan
* @package AliPay
*/
class Scan extends BasicAliPay
{
/**
* Scan constructor.
* @param array $options
*/
public function __construct(array $options)
{
parent::__construct($options);
$this->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);
}
}

+ 67
- 0
src/system/sdks/AliPay/Trade.php View File

@@ -0,0 +1,67 @@
<?php
namespace AliPay;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicAliPay;

/**
* 支付宝标准接口
* Class Trade
* @package AliPay
*/
class Trade extends BasicAliPay
{

/**
* 设置交易接口地址
* @param string $method
* @return $this
*/
public function setMethod($method)
{
$this->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);
}
}

+ 91
- 0
src/system/sdks/AliPay/Transfer.php View File

@@ -0,0 +1,91 @@
<?php
namespace AliPay;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicAliPay;
use WeChat\Exceptions\InvalidArgumentException;

/**
* 支付宝转账到账户
* Class Transfer
* @package AliPay
*/
class Transfer extends BasicAliPay
{

/**
* 旧版 向指定支付宝账户转账
* @param array $options
* @return array|bool
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function apply($options)
{
$this->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]");
}
}
}

+ 34
- 0
src/system/sdks/AliPay/Wap.php View File

@@ -0,0 +1,34 @@
<?php
namespace AliPay;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicAliPay;

/**
* 手机WAP网站支付支持
* Class Wap
* @package AliPay
*/
class Wap extends BasicAliPay
{
/**
* Wap constructor.
* @param array $options
*/
public function __construct(array $options)
{
parent::__construct($options);
$this->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();
}
}

+ 34
- 0
src/system/sdks/AliPay/Web.php View File

@@ -0,0 +1,34 @@
<?php
namespace AliPay;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicAliPay;

/**
* 支付宝网站支付
* Class Web
* @package AliPay
*/
class Web extends BasicAliPay
{
/**
* Web constructor.
* @param array $options
*/
public function __construct(array $options)
{
parent::__construct($options);
$this->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();
}
}

+ 134
- 0
src/system/sdks/We.php View File

@@ -0,0 +1,134 @@
<?php
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\DataArray;
use WeChat\Exceptions\InvalidInstanceException;

/**
* 加载缓存器
*
* Class We
* @library WeChatDeveloper
* @author Anyon<zoujingli@qq.com>
* @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");
}

}

+ 659
- 0
src/system/sdks/WeChat/Card.php View File

@@ -0,0 +1,659 @@
<?php
namespace WeChat;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 卡券管理
* Class Card
* @package WeChat
*/
class Card extends BasicWeChat
{
/**
* 创建卡券
* @param array $data
* @return array
* @throws Exceptions\InvalidResponseException
* @throws Exceptions\LocalCacheException
*/
public function create(array $data)
{
$url = "https://api.weixin.qq.com/card/create?access_token=ACCESS_TOKEN";
$this->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);
}

}

+ 356
- 0
src/system/sdks/WeChat/Contracts/BasicAliPay.php View File

@@ -0,0 +1,356 @@
<?php
namespace WeChat\Contracts;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Exceptions\InvalidArgumentException;
use WeChat\Exceptions\InvalidResponseException;

/**
* 支付宝支付基类
* Class AliPay
* @package AliPay\Contracts
*/
abstract class BasicAliPay
{

/**
* 支持配置
* @var DataArray
*/
protected $config;

/**
* 当前请求数据
* @var DataArray
*/
protected $options;

/**
* DzContent数据
* @var DataArray
*/
protected $params;

/**
* 静态缓存
* @var static
*/
protected static $cache;

/**
* 正常请求网关
* @var string
*/
protected $gateway = 'https://openapi.alipay.com/gateway.do?charset=utf-8';

/**
* AliPay constructor.
* @param array $options
*/
public function __construct($options)
{
$this->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 = "<form id='alipaysubmit' name='alipaysubmit' action='{$this->gateway}' method='post'>";
foreach ($this->options->get() as $key => $value) {
$value = str_replace("'", '&apos;', $value);
$html .= "<input type='hidden' name='{$key}' value='{$value}'/>";
}
$html .= "<input type='submit' value='ok' style='display:none;'></form>";
return "{$html}<script>document.forms['alipaysubmit'].submit();</script>";
}

/**
* 新版 从证书中提取序列号
* @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);

}

+ 218
- 0
src/system/sdks/WeChat/Contracts/BasicPushEvent.php View File

@@ -0,0 +1,218 @@
<?php
namespace WeChat\Contracts;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Exceptions\InvalidArgumentException;
use WeChat\Exceptions\InvalidDecryptException;
use WeChat\Exceptions\InvalidResponseException;

/**
* 微信通知处理基本类
* Class BasicPushEvent
* @package WeChat\Contracts
*/
class BasicPushEvent
{
/**
* 公众号APPID
* @var string
*/
protected $appid;

/**
* 公众号推送XML内容
* @var string
*/
protected $postxml;

/**
* 公众号推送加密类型
* @var string
*/
protected $encryptType;

/**
* 公众号的推送请求参数
* @var DataArray
*/
protected $input;

/**
* 当前公众号配置对象
* @var DataArray
*/
protected $config;

/**
* 公众号推送内容对象
* @var DataArray
*/
protected $receive;

/**
* 准备回复的消息内容
* @var array
*/
protected $message;

/**
* BasicPushEvent constructor.
* @param array $options
* @throws \WeChat\Exceptions\InvalidResponseException
*/
public function __construct(array $options)
{
if (empty($options['appid'])) {
throw new InvalidArgumentException("Missing Config -- [appid]");
}
if (empty($options['appsecret'])) {
throw new InvalidArgumentException("Missing Config -- [appsecret]");
}
if (empty($options['token'])) {
throw new InvalidArgumentException("Missing Config -- [token]");
}
// 参数初始化
$this->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 = "<xml><Encrypt><![CDATA[%s]]></Encrypt><MsgSignature><![CDATA[%s]]></MsgSignature><TimeStamp>%s</TimeStamp><Nonce><![CDATA[%s]]></Nonce></xml>";
$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');
}
}

+ 235
- 0
src/system/sdks/WeChat/Contracts/BasicWeChat.php View File

@@ -0,0 +1,235 @@
<?php
namespace WeChat\Contracts;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Exceptions\InvalidArgumentException;
use WeChat\Exceptions\InvalidResponseException;

/**
* Class BasicWeChat
* @package WeChat\Contracts
*/
class BasicWeChat
{

/**
* 当前微信配置
* @var DataArray
*/
public $config;

/**
* 访问AccessToken
* @var string
*/
public $access_token = '';

/**
* 当前请求方法参数
* @var array
*/
protected $currentMethod = [];

/**
* 当前模式
* @var bool
*/
protected $isTry = false;

/**
* 静态缓存
* @var static
*/
protected static $cache;

/**
* 注册代替函数
* @var string
*/
protected $GetAccessTokenCallback;

/**
* BasicWeChat constructor.
* @param array $options
*/
public function __construct(array $options)
{
if (empty($options['appid'])) {
throw new InvalidArgumentException("Missing Config -- [appid]");
}
if (empty($options['appsecret'])) {
throw new InvalidArgumentException("Missing Config -- [appsecret]");
}
if (isset($options['GetAccessTokenCallback']) && is_callable($options['GetAccessTokenCallback'])) {
$this->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 高一平 <iam@gaoyiping.com>
*
* 当用户使用自己的缓存驱动时,直接实例化对象后可直接设置 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);
}

}

+ 194
- 0
src/system/sdks/WeChat/Contracts/BasicWePay.php View File

@@ -0,0 +1,194 @@
<?php
namespace WeChat\Contracts;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Exceptions\InvalidArgumentException;
use WeChat\Exceptions\InvalidResponseException;

/**
* 微信支付基础类
* Class BasicPay
* @package WeChat\Contracts
*/
class BasicWePay
{
/**
* 商户配置
* @var DataArray
*/
protected $config;

/**
* 当前请求数据
* @var DataArray
*/
protected $params;

/**
* 静态缓存
* @var static
*/
protected static $cache;

/**
* WeChat constructor.
* @param array $options
*/
public function __construct(array $options)
{
if (empty($options['appid'])) {
throw new InvalidArgumentException("Missing Config -- [appid]");
}
if (empty($options['mch_id'])) {
throw new InvalidArgumentException("Missing Config -- [mch_id]");
}
if (empty($options['mch_key'])) {
throw new InvalidArgumentException("Missing Config -- [mch_key]");
}
if (!empty($options['cache_path'])) {
Tools::$cache_path = $options['cache_path'];
}
$this->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;
}
}

+ 28
- 0
src/system/sdks/WeChat/Contracts/BasicWeWork.php View File

@@ -0,0 +1,28 @@
<?php
namespace WeChat\Contracts;
if (!defined('DEDEINC')) exit('dedebiz');
/**
* 企业微信基础类
* Class BasicWeWork
* @package WeChat\Contracts
*/
class BasicWeWork extends BasicWeChat
{
/**
* 获取访问 AccessToken
* @return string
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function getAccessToken()
{
if ($this->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'];
}

}

+ 115
- 0
src/system/sdks/WeChat/Contracts/DataArray.php View File

@@ -0,0 +1,115 @@
<?php
namespace WeChat\Contracts;
if (!defined('DEDEINC')) exit('dedebiz');
use ArrayAccess;

/**
* Class DataArray
* @package WeChat
*/
class DataArray implements ArrayAccess
{

/**
* 当前配置值
* @var array
*/
private $config = [];

/**
* Config constructor.
* @param array $options
*/
public function __construct(array $options)
{
$this->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;
}
}

+ 181
- 0
src/system/sdks/WeChat/Contracts/DataError.php View File

@@ -0,0 +1,181 @@
<?php
namespace WeChat\Contracts;
if (!defined('DEDEINC')) exit('dedebiz');
/**
* 错误消息处理
* Class DataError
* @package WeChat\Contracts
*/
class DataError
{
/**
* 接口代码错误
* @var array
*/
static $message = [
-1 => '系统繁忙,此时请开发者稍候再试',
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;
}

}

+ 65
- 0
src/system/sdks/WeChat/Contracts/MyCurlFile.php View File

@@ -0,0 +1,65 @@
<?php
namespace WeChat\Contracts;
if (!defined('DEDEINC')) exit('dedebiz');

/**
* 自定义CURL文件类
* Class MyCurlFile
* @package WeChat\Contracts
*/
class MyCurlFile extends \stdClass
{
/**
* 当前数据类型
* @var string
*/
public $datatype = 'MY_CURL_FILE';

/**
* MyCurlFile constructor.
* @param string|array $filename
* @param string $mimetype
* @param string $postname
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function __construct($filename, $mimetype = '', $postname = '')
{
if (is_array($filename)) {
foreach ($filename as $k => $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);
}

}

+ 177
- 0
src/system/sdks/WeChat/Contracts/Prpcrypt.php View File

@@ -0,0 +1,177 @@
<?php
if (!defined('DEDEINC')) exit('dedebiz');
/**
* PKCS7算法 - 加解密
* Class PKCS7Encoder
*/
class PKCS7Encoder
{

public static $blockSize = 32;

/**
* 对需要加密的明文进行填充补位
* @param string $text 需要进行填充补位操作的明文
* @return string 补齐明文字符串
*/
function encode($text)
{
$amount_to_pad = PKCS7Encoder::$blockSize - (strlen($text) % PKCS7Encoder::$blockSize);
if ($amount_to_pad == 0) {
$amount_to_pad = PKCS7Encoder::$blockSize;
}
list($pad_chr, $tmp) = [chr($amount_to_pad), ''];
for ($index = 0; $index < $amount_to_pad; $index++) {
$tmp .= $pad_chr;
}
return $text . $tmp;
}

/**
* 对解密后的明文进行补位删除
* @param string $text 解密后的明文
* @return string 删除填充补位后的明文
*/
function decode($text)
{
$pad = ord(substr($text, -1));
if ($pad < 1 || $pad > 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;
}

}

+ 458
- 0
src/system/sdks/WeChat/Contracts/Tools.php View File

@@ -0,0 +1,458 @@
<?php
namespace WeChat\Contracts;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Exceptions\InvalidArgumentException;
use WeChat\Exceptions\InvalidResponseException;
use WeChat\Exceptions\LocalCacheException;

/**
* 网络请求支持
* Class Tools
* @package WeChat\Contracts
*/
class Tools
{
/**
* 缓存路径
* @var null
*/
public static $cache_path = null;

/**
* 缓存写入操作
* @var array
*/
public static $cache_callable = [
'set' => 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 "<xml>" . self::_arr2xml($data) . "</xml>";
}

/**
* 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 .= '<![CDATA[' . preg_replace("/[\\x00-\\x08\\x0b-\\x0c\\x0e-\\x1f]/", '', $val) . ']]>';
} else {
$content .= $val;
}
$content .= "</{$key}>";
}
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;
}
}

+ 233
- 0
src/system/sdks/WeChat/Custom.php View File

@@ -0,0 +1,233 @@
<?php
namespace WeChat;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;
use WeChat\Contracts\Tools;

/**
* 客服消息处理
* Class Custom
* @package WeChat
*/
class Custom extends BasicWeChat
{
/**
* 添加客服帐号
* @param string $kf_account 客服账号
* @param string $nickname 客服昵称
* @return array
* @throws Exceptions\InvalidResponseException
* @throws Exceptions\LocalCacheException
*/
public function addAccount($kf_account, $nickname)
{
$data = ['kf_account' => $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]);
}


}

+ 118
- 0
src/system/sdks/WeChat/Draft.php View File

@@ -0,0 +1,118 @@
<?php
namespace WeChat;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 微信草稿箱管理
* Class Draft
* @author taoxin
* @package WeChat
*/
class Draft extends BasicWeChat
{
/**
* 新建草稿
* @param $articles
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function add($articles)
{
$url = "https://api.weixin.qq.com/cgi-bin/draft/add?access_token=ACCESS_TOKEN";
$this->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]);
}

}

+ 27
- 0
src/system/sdks/WeChat/Exceptions/InvalidArgumentException.php View File

@@ -0,0 +1,27 @@
<?php
namespace WeChat\Exceptions;
if (!defined('DEDEINC')) exit('dedebiz');
/**
* 接口参数异常
* Class InvalidArgumentException
* @package WeChat
*/
class InvalidArgumentException extends \InvalidArgumentException
{
/**
* @var array
*/
public $raw = [];

/**
* InvalidArgumentException constructor.
* @param string $message
* @param integer $code
* @param array $raw
*/
public function __construct($message, $code = 0, $raw = [])
{
parent::__construct($message, intval($code));
$this->raw = $raw;
}
}

+ 27
- 0
src/system/sdks/WeChat/Exceptions/InvalidDecryptException.php View File

@@ -0,0 +1,27 @@
<?php
namespace WeChat\Exceptions;
if (!defined('DEDEINC')) exit('dedebiz');
/**
* 加密解密异常
* Class InvalidResponseException
* @package WeChat
*/
class InvalidDecryptException extends \Exception
{
/**
* @var array
*/
public $raw = [];

/**
* InvalidDecryptException constructor.
* @param string $message
* @param integer $code
* @param array $raw
*/
public function __construct($message, $code = 0, $raw = [])
{
parent::__construct($message, intval($code));
$this->raw = $raw;
}
}

+ 27
- 0
src/system/sdks/WeChat/Exceptions/InvalidInstanceException.php View File

@@ -0,0 +1,27 @@
<?php
namespace WeChat\Exceptions;
if (!defined('DEDEINC')) exit('dedebiz');
/**
* 加载类异常
* Class InvalidInstanceException
* @package WeChat\Exceptions
*/
class InvalidInstanceException extends \Exception
{
/**
* @var array
*/
public $raw = [];

/**
* InvalidResponseException constructor.
* @param string $message
* @param integer $code
* @param array $raw
*/
public function __construct($message, $code = 0, $raw = [])
{
parent::__construct($message, intval($code));
$this->raw = $raw;
}
}

+ 28
- 0
src/system/sdks/WeChat/Exceptions/InvalidResponseException.php View File

@@ -0,0 +1,28 @@
<?php
namespace WeChat\Exceptions;
if (!defined('DEDEINC')) exit('dedebiz');
/**
* 返回异常
* Class InvalidResponseException
* @package WeChat
*/
class InvalidResponseException extends \Exception
{
/**
* @var array
*/
public $raw = [];

/**
* InvalidResponseException constructor.
* @param string $message
* @param integer $code
* @param array $raw
*/
public function __construct($message, $code = 0, $raw = [])
{
parent::__construct($message, intval($code));
$this->raw = $raw;
}

}

+ 29
- 0
src/system/sdks/WeChat/Exceptions/LocalCacheException.php View File

@@ -0,0 +1,29 @@
<?php
namespace WeChat\Exceptions;
if (!defined('DEDEINC')) exit('dedebiz');
/***
* 本地缓存异常
* Class LocalCacheException
* @package WeChat
*/
class LocalCacheException extends \Exception
{

/**
* @var array
*/
public $raw = [];

/**
* LocalCacheException constructor.
* @param string $message
* @param integer $code
* @param array $raw
*/
public function __construct($message, $code = 0, $raw = [])
{
parent::__construct($message, intval($code));
$this->raw = $raw;
}

}

+ 88
- 0
src/system/sdks/WeChat/Freepublish.php View File

@@ -0,0 +1,88 @@
<?php
namespace WeChat;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 发布能力
* Class Freepublish
* @author taoxin
* @package WeChat
*/
class Freepublish extends BasicWeChat
{
/**
* 发布接口
* 开发者需要先将图文素材以草稿的形式保存(见“草稿箱/新建草稿”,如需从已保存的草稿中选择,见“草稿箱/获取草稿列表”)
* @param mixed $media_id 选择要发布的草稿的media_id
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function submit($media_id)
{
$url = "https://api.weixin.qq.com/cgi-bin/freepublish/submit?access_token=ACCESS_TOKEN";
$this->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]);
}
}

+ 52
- 0
src/system/sdks/WeChat/Limit.php View File

@@ -0,0 +1,52 @@
<?php
namespace WeChat;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 接口调用频次限制
* Class Limit
* @package WeChat
*/
class Limit extends BasicWeChat
{

/**
* 公众号调用或第三方平台帮公众号调用对公众号的所有api调用(包括第三方帮其调用)次数进行清零
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function clearQuota()
{
$url = 'https://api.weixin.qq.com/cgi-bin/clear_quota?access_token=ACCESS_TOKEN';
return $this->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);
}
}

+ 188
- 0
src/system/sdks/WeChat/Media.php View File

@@ -0,0 +1,188 @@
<?php
namespace WeChat;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;
use WeChat\Contracts\Tools;
use WeChat\Exceptions\InvalidResponseException;

/**
* 微信素材管理
* Class Media
* @package WeChat
*/
class Media extends BasicWeChat
{
/**
* 新增临时素材
* @param string $filename 文件名称
* @param string $type 媒体文件类型(image|voice|video|thumb)
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function add($filename, $type = 'image')
{
if (!in_array($type, ['image', 'voice', 'video', 'thumb'])) {
throw new InvalidResponseException('Invalid Media Type.', '0');
}
$url = "https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type={$type}";
$this->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]);
}
}

+ 95
- 0
src/system/sdks/WeChat/Menu.php View File

@@ -0,0 +1,95 @@
<?php
namespace WeChat;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 微信菜单管理
* Class Menu
* @package WeChat
*/
class Menu extends BasicWeChat
{

/**
* 自定义菜单查询接口
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function get()
{
$url = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN";
$this->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]);
}
}

+ 86
- 0
src/system/sdks/WeChat/Oauth.php View File

@@ -0,0 +1,86 @@
<?php
namespace WeChat;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 微信网页授权
* Class Oauth
* @package WeChat
*/
class Oauth extends BasicWeChat
{

/**
* Oauth 授权跳转接口
* @param string $redirect_url 授权回跳地址
* @param string $state 为重定向后会带上state参数(填写a-zA-Z0-9的参数值,最多128字节)
* @param string $scope 授权类类型(可选值snsapi_base|snsapi_userinfo)
* @return string
*/
public function getOauthRedirect($redirect_url, $state = '', $scope = 'snsapi_base')
{
$appid = $this->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);
}
}

+ 218
- 0
src/system/sdks/WeChat/Pay.php View File

@@ -0,0 +1,218 @@
<?php
namespace WeChat;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWePay;
use WePay\Bill;
use WePay\Order;
use WePay\Refund;
use WePay\Transfers;
use WePay\TransfersBank;

/**
* 微信支付商户
* Class Pay
* @package WeChat\Contracts
*/
class Pay extends BasicWePay
{

/**
* 统一下单
* @param array $options
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function createOrder(array $options)
{
return Order::instance($this->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);
}
}

+ 162
- 0
src/system/sdks/WeChat/Product.php View File

@@ -0,0 +1,162 @@
<?php
namespace WeChat;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 商店管理
* Class Product
* @package WeChat
*/
class Product extends BasicWeChat
{
/**
* 提交审核/取消发布商品
* @param string $keystandard 商品编码标准
* @param string $keystr 商品编码内容
* @param string $status 设置发布状态。on为提交审核,off为取消发布
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function modStatus($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(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);
}

}

+ 63
- 0
src/system/sdks/WeChat/Qrcode.php View File

@@ -0,0 +1,63 @@
<?php
namespace WeChat;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 二维码管理
* Class Qrcode
* @package WeChat
*/
class Qrcode extends BasicWeChat
{

/**
* 创建二维码ticket
* @param string|integer $scene 场景
* @param int $expire_seconds 有效时间
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function create($scene, $expire_seconds = 0)
{
if (is_integer($scene)) { // 二维码场景类型
$data = ['action_info' => ['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]);
}
}

+ 151
- 0
src/system/sdks/WeChat/Receive.php View File

@@ -0,0 +1,151 @@
<?php
namespace WeChat;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicPushEvent;

/**
* 公众号推送管理
* Class Receive
* @package WeChat
*/
class Receive extends BasicPushEvent
{

/**
* 转发多客服消息
* @param string $account
* @return $this
*/
public function transferCustomerService($account = '')
{
$this->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;
}
}

+ 186
- 0
src/system/sdks/WeChat/Scan.php View File

@@ -0,0 +1,186 @@
<?php
namespace WeChat;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 扫一扫接入管理
* Class Scan
* @package WeChat
*/
class Scan extends BasicWeChat
{
/**
* 获取商户信息
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function getMerchantInfo()
{
$url = "https://api.weixin.qq.com/scan/merchantinfo/get?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 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]);
}

}

+ 102
- 0
src/system/sdks/WeChat/Script.php View File

@@ -0,0 +1,102 @@
<?php
namespace WeChat;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;
use WeChat\Contracts\Tools;
use WeChat\Exceptions\InvalidResponseException;

/**
* 微信前端支持
* Class Script
* @package WeChat
*/
class Script extends BasicWeChat
{

/**
* 删除JSAPI授权TICKET
* @param string $type TICKET类型(wx_card|jsapi)
* @param string $appid 强制指定有效APPID
* @return void
*/
public function delTicket($type = 'jsapi', $appid = null)
{
is_null($appid) && $appid = $this->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));
}
}

+ 350
- 0
src/system/sdks/WeChat/Shake.php View File

@@ -0,0 +1,350 @@
<?php
namespace WeChat;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;
use WeChat\Contracts\Tools;

/**
* 揺一揺周边
* Class Shake
* @package WeChat
*/
class Shake extends BasicWeChat
{
/**
* 申请开通功能
* @param array $data
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function register(array $data)
{
$url = "https://api.weixin.qq.com/shakearound/account/register?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 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);
}

}

+ 111
- 0
src/system/sdks/WeChat/Tags.php View File

@@ -0,0 +1,111 @@
<?php
namespace WeChat;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 用户标签管理
* Class Tags
* @package WeChat
*/
class Tags extends BasicWeChat
{
/**
* 获取粉丝标签列表
* @throws Exceptions\InvalidResponseException
* @throws Exceptions\LocalCacheException
*/
public function getTags()
{
$url = "https://api.weixin.qq.com/cgi-bin/tags/get?access_token=ACCESS_TOKEN";
$this->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]);
}
}

+ 97
- 0
src/system/sdks/WeChat/Template.php View File

@@ -0,0 +1,97 @@
<?php
namespace WeChat;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 模板消息
* Class Template
* @package WeChat
*/
class Template extends BasicWeChat
{
/**
* 设置所属行业
* @param string $industry_id1 公众号模板消息所属行业编号
* @param string $industry_id2 公众号模板消息所属行业编号
* @return array
* @throws Exceptions\InvalidResponseException
* @throws Exceptions\LocalCacheException
*/
public function setIndustry($industry_id1, $industry_id2)
{
$url = "https://api.weixin.qq.com/cgi-bin/template/api_set_industry?access_token=ACCESS_TOKEN";
$this->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);
}


}

+ 134
- 0
src/system/sdks/WeChat/User.php View File

@@ -0,0 +1,134 @@
<?php
namespace WeChat;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 微信粉丝管理
* Class User
* @package WeChat
*/
class User extends BasicWeChat
{

/**
* 设置用户备注名
* @param string $openid
* @param string $remark
* @return array
* @throws Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function updateMark($openid, $remark)
{
$url = 'https://api.weixin.qq.com/cgi-bin/user/info/updateremark?access_token=ACCESS_TOKEN';
$this->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]);
}

}

+ 271
- 0
src/system/sdks/WeChat/Wifi.php View File

@@ -0,0 +1,271 @@
<?php
namespace WeChat;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 门店 WIFI 管理
* Class Wifi
* @package WeChat
*/
class Wifi extends BasicWeChat
{

/**
* 获取 Wi-Fi 门店列表
* @param integer $pageindex 分页下标,默认从1开始
* @param integer $pagesize 每页的个数,默认10个,最大20个
* @return array
* @throws Exceptions\InvalidResponseException
* @throws Exceptions\LocalCacheException
*/
public function getShopList($pageindex = 1, $pagesize = 2)
{
$url = 'https://api.weixin.qq.com/bizwifi/shop/list?access_token=ACCESS_TOKEN';
$this->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]);
}

}

+ 107
- 0
src/system/sdks/WeMini/Crypt.php View File

@@ -0,0 +1,107 @@
<?php
namespace WeMini;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;
use WeChat\Contracts\Tools;
use WeChat\Exceptions\InvalidDecryptException;
use WeChat\Exceptions\InvalidResponseException;
use WXBizDataCrypt;


/**
* 数据加密处理
* Class Crypt
* @package WeMini
*/
class Crypt extends BasicWeChat
{

/**
* 数据签名校验
* @param string $iv
* @param string $sessionKey
* @param string $encryptedData
* @return bool|array
*/
public function decode($iv, $sessionKey, $encryptedData)
{
require_once __DIR__ . DIRECTORY_SEPARATOR . 'crypt' . DIRECTORY_SEPARATOR . 'wxBizDataCrypt.php';
$pc = new WXBizDataCrypt($this->config->get('appid'), $sessionKey);
$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);
}
}

+ 158
- 0
src/system/sdks/WeMini/Delivery.php View File

@@ -0,0 +1,158 @@
<?php
namespace WeMini;
if (!defined('DEDEINC')) exit('dedebiz');

use WeChat\Contracts\BasicWeChat;

/**
* 小程序即时配送
* Class Delivery
* @package WeMini
*/
class Delivery extends BasicWeChat
{

/**
* 异常件退回商家商家确认收货接口
* @param array $data
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function abnormalConfirm($data)
{
$url = 'https://api.weixin.qq.com/cgi-bin/express/local/business/order/confirm_return?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 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);
}

}

+ 512
- 0
src/system/sdks/WeMini/Guide.php View File

@@ -0,0 +1,512 @@
<?php
namespace WeMini;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 小程序导购助手
* Class Guide
* @package WeMini
*/
class Guide extends BasicWeChat
{
/**
* 服务号添加导购
* @param array $data
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function addGuideAcct($data)
{
$url = 'https://api.weixin.qq.com/cgi-bin/guide/addguideacct?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 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);
}
}

+ 55
- 0
src/system/sdks/WeMini/Image.php View File

@@ -0,0 +1,55 @@
<?php
namespace WeMini;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 小程序图像处理
* Class Image
* @package WeMini
*/
class Image extends BasicWeChat
{

/**
* 本接口提供基于小程序的图片智能裁剪能力
* @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 aiCrop($img_url, $img)
{
$url = "https://api.weixin.qq.com/cv/img/aicrop?access_token=ACCESS_TOCKEN";
return $this->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);
}
}

+ 156
- 0
src/system/sdks/WeMini/Live.php View File

@@ -0,0 +1,156 @@
<?php
namespace WeMini;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 小程序直播接口
* Class Live
* @package WeMini
*/
class Live extends BasicWeChat
{
/**
* 创建直播间
* @param array $data
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function create($data)
{
$url = 'https://api.weixin.qq.com/wxaapi/broadcast/room/create?access_token=ACCESS_TOKEN';
return $this->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);
}
}

+ 179
- 0
src/system/sdks/WeMini/Logistics.php View File

@@ -0,0 +1,179 @@
<?php
namespace WeMini;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 小程序物流助手
* Class Logistics
* @package WeMini
*/
class Logistics extends BasicWeChat
{
/**
* 生成运单
* @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/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 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);
}
}

+ 51
- 0
src/system/sdks/WeMini/Message.php View File

@@ -0,0 +1,51 @@
<?php
namespace WeMini;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 小程序动态消息
* Class Message
* @package WeMini
*/
class Message extends BasicWeChat
{
/**
* 动态消息,创建被分享动态消息的 activity_id
* @param array $data
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function createActivityId($data)
{
$url = 'https://api.weixin.qq.com/cgi-bin/message/wxopen/activityid/create?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 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);
}
}

+ 130
- 0
src/system/sdks/WeMini/Newtmpl.php View File

@@ -0,0 +1,130 @@
<?php
namespace WeMini;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 公众号小程序订阅消息支持
* Class Mini
* @package WeChat
*/
class Newtmpl extends BasicWeChat
{
/**
* 获取小程序账号的类目
* @param array $data 类目信息列表
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function addCategory($data)
{
$url = 'https://api.weixin.qq.com/cgi-bin/wxopen/addcategory?access_token=ACCESS_TOKEN';
return $this->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);
}
}

+ 90
- 0
src/system/sdks/WeMini/Ocr.php View File

@@ -0,0 +1,90 @@
<?php
namespace WeMini;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 小程序ORC服务
* Class Ocr
* @package WeMini
*/
class Ocr extends BasicWeChat
{
/**
* 本接口提供基于小程序的银行卡 OCR 识别
* @param array $data
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function bankcard($data)
{
$url = 'https://api.weixin.qq.com/cv/ocr/bankcard?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 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);
}
}

+ 26
- 0
src/system/sdks/WeMini/Operation.php View File

@@ -0,0 +1,26 @@
<?php
namespace WeMini;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 小程序运维中心
* Class Operation
* @package WeMini
*/
class Operation extends BasicWeChat
{

/**
* 实时日志查询
* @param array $data
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function realtimelogSearch($data)
{
$url = 'https://api.weixin.qq.com/wxaapi/userlog/userlog_search?access_token=ACCESS_TOKEN';
return $this->callPostApi($url, $data, true);
}
}

+ 92
- 0
src/system/sdks/WeMini/Plugs.php View File

@@ -0,0 +1,92 @@
<?php
namespace WeMini;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 微信小程序插件管理
* Class Plugs
* @package WeMini
*/
class Plugs extends BasicWeChat
{
/**
* 1.申请使用插件
* @param string $plugin_appid 插件appid
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function apply($plugin_appid)
{
$url = 'https://api.weixin.qq.com/wxa/plugin?access_token=ACCESS_TOKEN';
return $this->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);
}
}

+ 73
- 0
src/system/sdks/WeMini/Poi.php View File

@@ -0,0 +1,73 @@
<?php
namespace WeMini;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 微信小程序地址管理
* Class Poi
* @package WeMini
*/
class Poi extends BasicWeChat
{
/**
* 添加地点
* @param string $related_name 经营资质主体
* @param string $related_credential 经营资质证件号
* @param string $related_address 经营资质地址
* @param string $related_proof_material 相关证明材料照片临时素材mediaid
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function addBearByPoi($related_name, $related_credential, $related_address, $related_proof_material)
{
$url = 'https://api.weixin.qq.com/wxa/addnearbypoi?access_token=ACCESS_TOKEN';
$data = [
'related_name' => $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);
}
}

+ 98
- 0
src/system/sdks/WeMini/Qrcode.php View File

@@ -0,0 +1,98 @@
<?php
namespace WeMini;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;
use WeChat\Contracts\Tools;

/**
* 微信小程序二维码管理
* Class Qrcode
* @package WeMini
*/
class Qrcode extends BasicWeChat
{

/**
* 获取小程序码(永久有效)
* 接口A: 适用于需要的码数量较少的业务场景
* @param string $path 不能为空,最大长度 128 字节
* @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 createMiniPath($path, $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/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);
}
}

+ 65
- 0
src/system/sdks/WeMini/Scheme.php View File

@@ -0,0 +1,65 @@
<?php
namespace WeMini;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 小程序 URL-Scheme
* Class Scheme
* @package WeMini
*/
class Scheme extends BasicWeChat
{

/**
* 创建 URL-Scheme
* @param array $data
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function create($data)
{
$url = 'https://api.weixin.qq.com/wxa/generatescheme?access_token=ACCESS_TOKEN';
return $this->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);
}
}

+ 25
- 0
src/system/sdks/WeMini/Search.php View File

@@ -0,0 +1,25 @@
<?php
namespace WeMini;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 小程序搜索
* Class Search
* @package WeMini
*/
class Search extends BasicWeChat
{
/**
* 提交小程序页面url及参数信息
* @param array $pages
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function submitPages($pages)
{
$url = 'https://api.weixin.qq.com/cgi-bin/guide/getguideacct?access_token=ACCESS_TOKEN';
return $this->callPostApi($url, ['pages' => $pages], true);
}
}

+ 53
- 0
src/system/sdks/WeMini/Security.php View File

@@ -0,0 +1,53 @@
<?php
namespace WeMini;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 小程序内容安全
* Class Security
* @package WeMini
*/
class Security extends BasicWeChat
{

/**
* 校验一张图片是否含有违法违规内容
* @param string $media 要检测的图片文件,格式支持PNG、JPEG、JPG、GIF,图片尺寸不超过 750px x 1334px
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function imgSecCheck($media)
{
$url = 'https://api.weixin.qq.com/wxa/img_sec_check?access_token=ACCESS_TOKEN';
return $this->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);
}
}

+ 25
- 0
src/system/sdks/WeMini/Soter.php View File

@@ -0,0 +1,25 @@
<?php
namespace WeMini;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 小程序生物认证
* Class Soter
* @package WeMini
*/
class Soter extends BasicWeChat
{
/**
* SOTER 生物认证秘钥签名验证
* @param array $data
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function verifySignature($data)
{
$url = 'https://api.weixin.qq.com/cgi-bin/soter/verify_signature?access_token=ACCESS_TOKEN';
return $this->callPostApi($url, $data, true);
}
}

+ 90
- 0
src/system/sdks/WeMini/Template.php View File

@@ -0,0 +1,90 @@
<?php
namespace WeMini;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 公众号小程序模板消息支持
* Class Mini
* @package WeChat
*/
class Template extends BasicWeChat
{

/**
* 获取小程序模板库标题列表
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function getTemplateLibraryList()
{
$url = 'https://api.weixin.qq.com/cgi-bin/wxopen/template/library/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 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);
}
}

+ 152
- 0
src/system/sdks/WeMini/Total.php View File

@@ -0,0 +1,152 @@
<?php
namespace WeMini;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWeChat;

/**
* 微信小程序数据接口
* Class Total
* @package WeMini
*/
class Total extends BasicWeChat
{
/**
* 数据分析接口
* @param string $begin_date 开始日期
* @param string $end_date 结束日期,限定查询1天数据,end_date允许设置的最大值为昨日
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function getWeanalysisAppidDailySummarytrend($begin_date, $end_date)
{
$url = 'https://api.weixin.qq.com/datacube/getweanalysisappiddailysummarytrend?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 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);
}
}

+ 19
- 0
src/system/sdks/WeMini/crypt/errorCode.php View File

@@ -0,0 +1,19 @@
<?php
if (!defined('DEDEINC')) exit('dedebiz');/**
* error code 说明.
* <ul>
* <li>-41001: encodingAesKey 非法</li>
* <li>-41003: aes 解密失败</li>
* <li>-41004: 解密后得到的buffer非法</li>
* <li>-41005: base64加密失败</li>
* <li>-41016: base64解密失败</li>
* </ul>
*/
class ErrorCode
{
public static $OK = 0;
public static $IllegalAesKey = -41001;
public static $IllegalIv = -41002;
public static $IllegalBuffer = -41003;
public static $DecodeBase64Error = -41004;
}

+ 58
- 0
src/system/sdks/WeMini/crypt/wxBizDataCrypt.php View File

@@ -0,0 +1,58 @@
<?php
if (!defined('DEDEINC')) exit('dedebiz');
/**
* 对微信小程序用户加密数据的解密示例代码
* Class WXBizDataCrypt
* @copyright Copyright (c) 1998-2014 Tencent Inc.
*/
class WXBizDataCrypt
{
private $appid;
private $sessionKey;

/**
* 构造函数
* @param $sessionKey string 用户在小程序登录后获取的会话密钥
* @param $appid string 小程序的appid
*/
public function __construct($appid, $sessionKey)
{
$this->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;
}

}


+ 50
- 0
src/system/sdks/WePay/Bill.php View File

@@ -0,0 +1,50 @@
<?php
namespace WePay;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWePay;
use WeChat\Contracts\Tools;
use WeChat\Exceptions\InvalidResponseException;

/**
* 微信商户账单及评论
* Class Bill
* @package WePay
*/
class Bill extends BasicWePay
{
/**
* 下载对账单
* @param array $options 静音参数
* @param null|string $outType 输出类型
* @return bool|string
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function download(array $options, $outType = null)
{
$this->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);
}
}

+ 52
- 0
src/system/sdks/WePay/Coupon.php View File

@@ -0,0 +1,52 @@
<?php
namespace WePay;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWePay;

/**
* 微信商户代金券
* Class Coupon
* @package WePay
*/
class Coupon extends BasicWePay
{
/**
* 发放代金券
* @param array $options
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function create(array $options)
{
$url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/send_coupon";
return $this->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);
}

}

+ 54
- 0
src/system/sdks/WePay/Custom.php View File

@@ -0,0 +1,54 @@
<?php
namespace WePay;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWePay;

/**
* 微信扩展上报海关
* Class Custom
* @package WePay
*/
class Custom extends BasicWePay
{

/**
* 订单附加信息提交接口
* @param array $options
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function add(array $options = [])
{
$url = 'https://api.mch.weixin.qq.com/cgi-bin/mch/customs/customdeclareorder';
return $this->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);
}

}

+ 160
- 0
src/system/sdks/WePay/Order.php View File

@@ -0,0 +1,160 @@
<?php
namespace WePay;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWePay;
use WeChat\Contracts\Tools;

/**
* 微信商户订单
* Class Order
* @package WePay
*/
class Order extends BasicWePay
{

/**
* 统一下单
* @param array $options
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function create(array $options)
{
$url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
return $this->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);
}
}

+ 59
- 0
src/system/sdks/WePay/Redpack.php View File

@@ -0,0 +1,59 @@
<?php
namespace WePay;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWePay;

/**
* 微信红包支持
* Class Redpack
* @package WePay
*/
class Redpack extends BasicWePay
{

/**
* 发放普通红包
* @param array $options
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function create(array $options)
{
$this->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);
}

}

+ 65
- 0
src/system/sdks/WePay/Refund.php View File

@@ -0,0 +1,65 @@
<?php
namespace WePay;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWePay;
use WeChat\Contracts\Tools;
use WeChat\Exceptions\InvalidDecryptException;
use WeChat\Exceptions\InvalidResponseException;

/**
* 微信商户退款
* Class Refund
* @package WePay
*/
class Refund extends BasicWePay
{

/**
* 创建退款订单
* @param array $options
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function create(array $options)
{
$url = 'https://api.mch.weixin.qq.com/secapi/pay/refund';
return $this->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());
}
}
}

+ 48
- 0
src/system/sdks/WePay/Transfers.php View File

@@ -0,0 +1,48 @@
<?php
namespace WePay;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWePay;

/**
* 微信商户打款到零钱
* Class Transfers
* @package WePay
*/
class Transfers extends BasicWePay
{

/**
* 企业付款到零钱
* @param array $options
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function create(array $options)
{
$this->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);
}

}

+ 111
- 0
src/system/sdks/WePay/TransfersBank.php View File

@@ -0,0 +1,111 @@
<?php
namespace WePay;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\BasicWePay;
use WeChat\Contracts\Tools;
use WeChat\Exceptions\InvalidArgumentException;
use WeChat\Exceptions\InvalidDecryptException;
use WeChat\Exceptions\InvalidResponseException;

/**
* 微信商户打款到银行卡
* Class TransfersBank
* @package WePay
*/
class TransfersBank extends BasicWePay
{

/**
* 企业付款到银行卡
* @param array $options
* @return array
* @throws \WeChat\Exceptions\InvalidDecryptException
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
*/
public function create(array $options)
{
if (!isset($options['partner_trade_no'])) {
throw new InvalidArgumentException('Missing Options -- [partner_trade_no]');
}
if (!isset($options['enc_bank_no'])) {
throw new InvalidArgumentException('Missing Options -- [enc_bank_no]');
}
if (!isset($options['enc_true_name'])) {
throw new InvalidArgumentException('Missing Options -- [enc_true_name]');
}
if (!isset($options['bank_code'])) {
throw new InvalidArgumentException('Missing Options -- [bank_code]');
}
if (!isset($options['amount'])) {
throw new InvalidArgumentException('Missing Options -- [amount]');
}
$this->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'];
}
}

+ 36
- 0
src/system/sdks/WePayV3/Cert.php View File

@@ -0,0 +1,36 @@
<?php
namespace WePayV3;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Exceptions\InvalidResponseException;
use WePayV3\Contracts\BasicWePay;
use WePayV3\Contracts\DecryptAes;

/**
* 平台证书管理
* Class Cert
* @package WePayV3
*/
class Cert extends BasicWePay
{
/**
* 商户平台下载证书
* @return void
* @throws \WeChat\Exceptions\InvalidResponseException
*/
public function download()
{
try {
$aes = new DecryptAes($this->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());
}
}
}

+ 222
- 0
src/system/sdks/WePayV3/Contracts/BasicWePay.php View File

@@ -0,0 +1,222 @@
<?php
namespace WePayV3\Contracts;
if (!defined('DEDEINC')) exit('dedebiz');

use WeChat\Contracts\Tools;
use WeChat\Exceptions\InvalidArgumentException;
use WeChat\Exceptions\InvalidResponseException;
use WePayV3\Cert;

/**
* 微信支付基础类
* Class BasicWePay
* @package WePayV3
*/
abstract class BasicWePay
{
/**
* 接口基础地址
* @var string
*/
protected $base = 'https://api.mch.weixin.qq.com';

/**
* 实例对象静态缓存
* @var array
*/
static $cache = [];

/**
* 配置参数
* @var array
*/
protected $config = [
'appid' => '', // 微信绑定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);
}
}
}

+ 68
- 0
src/system/sdks/WePayV3/Contracts/DecryptAes.php View File

@@ -0,0 +1,68 @@
<?php
namespace WePayV3\Contracts;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Exceptions\InvalidArgumentException;
use WeChat\Exceptions\InvalidDecryptException;

/**
* Aes 解密工具类
* Class DecryptAes
* @package WePayV3\Contracts
*/
class DecryptAes
{

private $aesKey;

const KEY_LENGTH_BYTE = 32;
const AUTH_TAG_LENGTH_BYTE = 16;

/**
* Constructor
* @param string $aesKey
*/
public function __construct($aesKey)
{
if (strlen($aesKey) != self::KEY_LENGTH_BYTE) {
throw new InvalidArgumentException('无效的ApiV3Key,长度应为32个字节');
}
$this->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');
}
}

+ 95
- 0
src/system/sdks/WePayV3/Order.php View File

@@ -0,0 +1,95 @@
<?php
namespace WePayV3;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\Tools;
use WeChat\Exceptions\InvalidArgumentException;
use WePayV3\Contracts\BasicWePay;
use WePayV3\Contracts\DecryptAes;

/**
* 订单支付接口
* Class Order
* @package WePayV3
*/
class Order extends BasicWePay
{
const WXPAY_H5 = 'h5';
const WXPAY_APP = 'app';
const WXPAY_JSAPI = 'jsapi';
const WXPAY_NATIVE = 'native';

/**
* 创建支付订单
* @param string $type 支付类型
* @param array $data 支付参数
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
*/
public function create($type, $data)
{
$types = [
'h5' => '/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;
}
}

+ 61
- 0
src/system/sdks/WePayV3/Refund.php View File

@@ -0,0 +1,61 @@
<?php
namespace WePayV3;
if (!defined('DEDEINC')) exit('dedebiz');
use WeChat\Contracts\Tools;
use WeChat\Exceptions\InvalidDecryptException;
use WeChat\Exceptions\InvalidResponseException;
use WePayV3\Contracts\BasicWePay;

/**
* 订单退款接口
* Class Refund
* @package WePayV3
*/
class Refund extends BasicWePay
{
/**
* 创建退款订单
* @param array $data 退款参数
* @return array
* @throws \WeChat\Exceptions\InvalidResponseException
*/
public function create($data)
{
return $this->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());
}
}
}

+ 26
- 0
src/system/sdks/_test/alipay-app.php View File

@@ -0,0 +1,26 @@
<?php
if (!defined('DEDEINC')) exit('dedebiz');
// 1. 手动加载入口文件
include "../include.php";

// 2. 准备公众号配置参数
$config = include "./alipay.php";

try {
// 实例支付对象
// $pay = \We::AliPayApp($config);
// $pay = new \AliPay\App($config);
$pay = \AliPay\App::instance($config);

// 请参考(请求参数):https://docs.open.alipay.com/api_1/alipay.trade.app.pay
$result = $pay->apply([
'out_trade_no' => time(), // 商户订单号
'total_amount' => '1', // 支付金额
'subject' => '支付宝订单标题', // 支付订单描述
]);
echo $result;
} catch (\Exception $e) {
echo $e->getMessage();
}



+ 24
- 0
src/system/sdks/_test/alipay-bill.php View File

@@ -0,0 +1,24 @@
<?php
if (!defined('DEDEINC')) exit('dedebiz');
// 1. 手动加载入口文件
include "../include.php";

// 2. 准备公众号配置参数
$config = include "./alipay.php";

try {
// 实例支付对象
// $pay = new \AliPay\Bill($config);
// $pay = \We::AliPayBill($config);
$pay = \AliPay\Bill::instance($config);

// 请参考(请求参数):https://docs.open.alipay.com/api_15/alipay.data.dataservice.bill.downloadurl.query
$result = $pay->apply([
'bill_date' => '2020-07-03', // 账单时间(日账单yyyy-MM-dd,月账单 yyyy-MM)
'bill_type' => 'signcustomer', // 账单类型(trade指商户基于支付宝交易收单的业务账单,signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单)
]);
echo '<pre>';
var_export($result);
} catch (Exception $e) {
echo $e->getMessage();
}

+ 27
- 0
src/system/sdks/_test/alipay-notify.php View File

@@ -0,0 +1,27 @@
<?php
if (!defined('DEDEINC')) exit('dedebiz');
// 1. 手动加载入口文件
include "../include.php";

// 2. 准备公众号配置参数
$config = include "./alipay.php";

try {
// 实例支付对象
// $pay = \We::AliPayApp($config);
// $pay = new \AliPay\App($config);
$pay = \AliPay\App::instance($config);

$data = $pay->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();
}

+ 29
- 0
src/system/sdks/_test/alipay-pos.php View File

@@ -0,0 +1,29 @@
<?php
if (!defined('DEDEINC')) exit('dedebiz');
// 1. 手动加载入口文件
include "../include.php";

// 2. 准备公众号配置参数
$config = include "./alipay.php";

try {
// 实例支付对象
// $pay = We::AliPayPos($config);
// $pay = new \AliPay\Pos($config);
$pay = \AliPay\Pos::instance($config);

// 参数链接:https://docs.open.alipay.com/api_1/alipay.trade.pay
$result = $pay->apply([
'out_trade_no' => '4312412343', // 订单号
'total_amount' => '13', // 订单金额,单位:元
'subject' => '订单商品标题', // 订单商品标题
'auth_code' => '123456', // 授权码
]);

echo '<pre>';
var_export($result);
} catch (Exception $e) {
echo $e->getMessage();
}



+ 27
- 0
src/system/sdks/_test/alipay-refund.php View File

@@ -0,0 +1,27 @@
<?php
if (!defined('DEDEINC')) exit('dedebiz');
// 1. 手动加载入口文件
include "../include.php";

// 2. 准备公众号配置参数
$config = include "./alipay.php";

// 原商户订单号
$out_trade_no = '56737188841424';
// 申请退款金额
$refund_fee = '1.00';

try {
// 实例支付对象
// $pay = We::AliPayApp($config);
// $pay = new \AliPay\App($config);
$pay = \AliPay\App::instance($config);

// 参考链接:https://docs.open.alipay.com/api_1/alipay.trade.refund
$result = $pay->refund($out_trade_no, $refund_fee);

echo '<pre>';
var_export($result);
} catch (Exception $e) {
echo $e->getMessage();
}

+ 28
- 0
src/system/sdks/_test/alipay-scan.php View File

@@ -0,0 +1,28 @@
<?php
if (!defined('DEDEINC')) exit('dedebiz');
// 1. 手动加载入口文件
include "../include.php";

// 2. 准备公众号配置参数
$config = include "./alipay.php";

try {
// 实例支付对象
// $pay = We::AliPayScan($config);
// $pay = new \AliPay\Scan($config);
$pay = \AliPay\Scan::instance($config);

// 参考链接:https://docs.open.alipay.com/api_1/alipay.trade.precreate
$result = $pay->apply([
'out_trade_no' => '14321412', // 订单号
'total_amount' => '13', // 订单金额,单位:元
'subject' => '订单商品标题', // 订单商品标题
]);

echo '<pre>';
var_export($result);
} catch (Exception $e) {
echo $e->getMessage();
}



+ 25
- 0
src/system/sdks/_test/alipay-transfer-account.php View File

@@ -0,0 +1,25 @@
<?php
if (!defined('DEDEINC')) exit('dedebiz');
// 1. 手动加载入口文件
include "../include.php";

// 2. 准备公众号配置参数
$config = include "./alipay.php";

try {
// 实例支付对象
// $pay = We::AliPayTransfer($config);
// $pay = new \AliPay\Transfer($config);
$pay = \AliPay\Transfer::instance($config);

// 参考链接:https://docs.open.alipay.com/api_28/alipay.fund.account.query/
$result = $pay->queryAccount([
'alipay_user_id' => $config['appid'], // 订单号
'account_scene_code' => 'SCENE_000_000_000',
]);
echo '<pre>';
var_export($result);
} catch (Exception $e) {
echo $e->getMessage();
}


+ 32
- 0
src/system/sdks/_test/alipay-transfer-create.php View File

@@ -0,0 +1,32 @@
<?php
if (!defined('DEDEINC')) exit('dedebiz');
// 1. 手动加载入口文件
include "../include.php";

// 2. 准备公众号配置参数
$config = include "./alipay.php";

try {
// 实例支付对象
// $pay = We::AliPayTransfer($config);
// $pay = new \AliPay\Transfer($config);
$pay = \AliPay\Transfer::instance($config);

// 参考链接:https://docs.open.alipay.com/api_28/alipay.fund.trans.uni.transfer/
$result = $pay->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 '<pre>';
var_export($result);
} catch (Exception $e) {
echo $e->getMessage();
}


+ 26
- 0
src/system/sdks/_test/alipay-transfer-query.php View File

@@ -0,0 +1,26 @@
<?php
if (!defined('DEDEINC')) exit('dedebiz');
// 1. 手动加载入口文件
include "../include.php";

// 2. 准备公众号配置参数
$config = include "./alipay.php";

try {
// 实例支付对象
// $pay = We::AliPayTransfer($config);
// $pay = new \AliPay\Transfer($config);
$pay = \AliPay\Transfer::instance($config);

// 参考链接:https://docs.open.alipay.com/api_28/alipay.fund.trans.common.query/
$result = $pay->queryResult([
'out_biz_no' => '201808080001', // 订单号
'product_code' => 'TRANS_ACCOUNT_NO_PWD',
'biz_scene' => 'DIRECT_TRANSFER',
]);
echo '<pre>';
var_export($result);
} catch (Exception $e) {
echo $e->getMessage();
}


+ 31
- 0
src/system/sdks/_test/alipay-transfer.php View File

@@ -0,0 +1,31 @@
<?php
if (!defined('DEDEINC')) exit('dedebiz');
// 1. 手动加载入口文件
include "../include.php";

// 2. 准备公众号配置参数
$config = include "./alipay.php";

try {
// 实例支付对象
// $pay = We::AliPayTransfer($config);
// $pay = new \AliPay\Transfer($config);
$pay = \AliPay\Transfer::instance($config);

// 参考链接:https://docs.open.alipay.com/api_28/alipay.fund.trans.toaccount.transfer
$result = $pay->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 '<pre>';
var_export($result);
} catch (Exception $e) {
echo $e->getMessage();
}


+ 30
- 0
src/system/sdks/_test/alipay-wap.php View File

@@ -0,0 +1,30 @@
<?php
if (!defined('DEDEINC')) exit('dedebiz');
// 1. 手动加载入口文件
include "../include.php";

// 2. 准备公众号配置参数
$config = include "./alipay.php";
// 参考公共参数 https://docs.open.alipay.com/203/107090/
$config['notify_url'] = 'http://pay.thinkadmin.top/test/alipay-notify.php';
$config['return_url'] = 'http://pay.thinkadmin.top/test/alipay-success.php';

try {
// 实例支付对象
// $pay = We::AliPayWap($config);
// $pay = new \AliPay\Wap($config);
$pay = \AliPay\Wap::instance($config);

// 参考链接:https://docs.open.alipay.com/api_1/alipay.trade.wap.pay
$result = $pay->apply([
'out_trade_no' => time(), // 商户订单号
'total_amount' => '1', // 支付金额
'subject' => '支付订单描述', // 支付订单描述
]);

echo $result;
} catch (Exception $e) {
echo $e->getMessage();
}



+ 31
- 0
src/system/sdks/_test/alipay-web.php View File

@@ -0,0 +1,31 @@
<?php
if (!defined('DEDEINC')) exit('dedebiz');
// 1. 手动加载入口文件
include "../include.php";

// 2. 准备公众号配置参数
$config = include "./alipay.php";

// 参考公共参数 https://docs.open.alipay.com/203/107090/
$config['notify_url'] = 'http://pay.thinkadmin.top/test/alipay-notify.php';
$config['return_url'] = 'http://pay.thinkadmin.top/test/alipay-success.php';

try {
// 实例支付对象
// $pay = We::AliPayWeb($config);
// $pay = new \AliPay\Web($config);
$pay = \AliPay\Web::instance($config);

// 参考链接:https://docs.open.alipay.com/api_1/alipay.trade.page.pay
$result = $pay->apply([
'out_trade_no' => time(), // 商户订单号
'total_amount' => '1', // 支付金额
'subject' => '支付订单描述', // 支付订单描述
]);

echo $result;
} catch (Exception $e) {
echo $e->getMessage();
}



+ 23
- 0
src/system/sdks/_test/alipay.php View File

@@ -0,0 +1,23 @@
<?php
if (!defined('DEDEINC')) exit('dedebiz');

return [
// 沙箱模式
'debug' => 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' => '',
];

+ 34
- 0
src/system/sdks/_test/config.php View File

@@ -0,0 +1,34 @@
<?php
if (!defined('DEDEINC')) exit('dedebiz');

// 配置缓存处理函数
//\WeChat\Contracts\Tools::$cache_callable = [
// 'set' => 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' => '',
];

+ 23
- 0
src/system/sdks/_test/mini-login.php View File

@@ -0,0 +1,23 @@
<?php
if (!defined('DEDEINC')) exit('dedebiz');include '../include.php';

// 小程序配置
$config = [
'appid' => '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 '<pre>';
//print_r($mini->session($code));
print_r($mini->decode($iv, $sessionKey, $decode));
//print_r($mini->userInfo($code, $iv, $decode));

+ 24
- 0
src/system/sdks/_test/mini-qrc.php View File

@@ -0,0 +1,24 @@
<?php
if (!defined('DEDEINC')) exit('dedebiz');include '../include.php';

// 小程序配置
$config = [
'appid' => 'wx6bb7b70258da09c6',
'appsecret' => '78b7b8d65bd67b078babf951d4342b42',
];

//We::config($config);

// $mini = We::WeMiniQrcode($config);
// $mini = new WeMini\Qrcode($config);
$mini = \WeMini\Qrcode::instance($config);

//echo '<pre>';
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());
}

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

Loading…
Cancel
Save