所有文章 > 日积月累 > SSPanel-Uim 对接微信小程序:实现用户管理与支付一体化
SSPanel-Uim 对接微信小程序:实现用户管理与支付一体化

SSPanel-Uim 对接微信小程序:实现用户管理与支付一体化

SSPanel-Uim 是一个功能强大的用户管理面板,广泛应用于 Shadowsocks、V2Ray 等代理服务的用户管理与计费系统。随着微信小程序的普及,越来越多的开发者希望将 SSPanel-Uim 与微信小程序进行对接,以实现用户管理、支付、通知等功能的一体化。本文将详细介绍如何将 SSPanel-Uim 与微信小程序进行对接,并提供实操性强的代码示例。

1. 准备工作

1.1 环境要求

  • SSPanel-Uim:确保你已经安装并配置好 SSPanel-Uim,且能够正常运行。
  • 微信小程序:拥有一个已注册的微信小程序,并获取到 AppID 和 AppSecret。
  • 服务器:确保你的服务器能够正常访问 SSPanel-Uim 和微信小程序的 API。

1.2 获取微信小程序 AppID 和 AppSecret

  1. 登录微信公众平台(https://mp.weixin.qq.com/)。
  2. 进入小程序管理后台,找到“开发” -> “开发设置”。
  3. 获取 AppID 和 AppSecret。

1.3 配置 SSPanel-Uim

  1. 登录 SSPanel-Uim 后台。
  2. 进入“系统设置” -> “支付设置”,配置微信支付相关参数。
  3. 进入“系统设置” -> “通知设置”,配置微信小程序通知相关参数。

2. 微信小程序登录对接

2.1 微信小程序登录流程

  1. 用户在小程序端点击登录按钮,调用微信登录接口获取 code
  2. code 发送到服务器端,服务器端通过 code 获取用户的 openidsession_key
  3. 服务器端根据 openid 查询或创建用户,并返回自定义登录态(如 token)给小程序端。

2.2 代码示例

2.2.1 小程序端代码

// 小程序端登录逻辑
wx.login({
success: function (res) {
if (res.code) {
// 将 code 发送到服务器端
wx.request({
url: 'https://your-server-domain.com/api/wechat/login',
method: 'POST',
data: {
code: res.code
},
success: function (res) {
// 登录成功,保存 token
wx.setStorageSync('token', res.data.token);
},
fail: function (err) {
console.error('登录失败', err);
}
});
} else {
console.error('获取 code 失败', res);
}
}
});

2.2.2 服务器端代码

// 服务器端登录逻辑
public function wechatLogin(Request $request) {
$code = $request->input('code');
$appid = 'your-wechat-appid';
$secret = 'your-wechat-secret';

// 通过 code 获取 openid 和 session_key
$url = "https://api.weixin.qq.com/sns/jscode2session?appid={$appid}&secret={$secret}&js_code={$code}&grant_type=authorization_code";
$response = file_get_contents($url);
$data = json_decode($response, true);

if (isset($data['openid'])) {
$openid = $data['openid'];
$user = User::where('wechat_openid', $openid)->first();

if (!$user) {
// 创建新用户
$user = new User();
$user->wechat_openid = $openid;
$user->save();
}

// 生成 token
$token = $this->generateToken($user);

return response()->json(['token' => $token]);
} else {
return response()->json(['error' => '登录失败'], 400);
}
}

private function generateToken($user) {
// 生成 token 的逻辑,可以使用 JWT 或其他方式
return md5($user->id . time());
}

3. 微信支付对接

3.1 微信支付流程

  1. 用户在小程序端选择套餐并点击支付按钮。
  2. 小程序端调用服务器端接口生成支付订单。
  3. 服务器端调用微信支付统一下单接口生成预支付订单,并返回支付参数给小程序端。
  4. 小程序端调用微信支付接口完成支付。
  5. 服务器端接收微信支付回调,更新订单状态。

3.2 代码示例

3.2.1 小程序端代码

// 小程序端支付逻辑
wx.request({
url: 'https://your-server-domain.com/api/wechat/pay',
method: 'POST',
data: {
package_id: 1, // 套餐 ID
user_id: wx.getStorageSync('user_id')
},
success: function (res) {
const paymentData = res.data;
wx.requestPayment({
timeStamp: paymentData.timeStamp,
nonceStr: paymentData.nonceStr,
package: paymentData.package,
signType: paymentData.signType,
paySign: paymentData.paySign,
success: function (res) {
console.log('支付成功', res);
},
fail: function (err) {
console.error('支付失败', err);
}
});
},
fail: function (err) {
console.error('生成支付订单失败', err);
}
});

3.2.2 服务器端代码

// 服务器端支付逻辑
public function wechatPay(Request $request) {
$packageId = $request->input('package_id');
$userId = $request->input('user_id');

// 获取套餐信息
$package = Package::find($packageId);
if (!$package) {
return response()->json(['error' => '套餐不存在'], 400);
}

// 生成订单
$order = new Order();
$order->user_id = $userId;
$order->package_id = $packageId;
$order->amount = $package->price;
$order->status = 'pending';
$order->save();

// 调用微信支付统一下单接口
$appid = 'your-wechat-appid';
$mch_id = 'your-wechat-mch-id';
$key = 'your-wechat-api-key';
$nonceStr = md5(uniqid());
$body = '套餐购买';
$out_trade_no = $order->id;
$total_fee = $order->amount * 100; // 单位为分
$spbill_create_ip = $request->ip();
$notify_url = 'https://your-server-domain.com/api/wechat/pay/notify';
$trade_type = 'JSAPI';
$openid = User::find($userId)->wechat_openid;

$params = [
'appid' => $appid,
'mch_id' => $mch_id,
'nonce_str' => $nonceStr,
'body' => $body,
'out_trade_no' => $out_trade_no,
'total_fee' => $total_fee,
'spbill_create_ip' => $spbill_create_ip,
'notify_url' => $notify_url,
'trade_type' => $trade_type,
'openid' => $openid
];

// 生成签名
ksort($params);
$stringA = '';
foreach ($params as $key => $value) {
$stringA .= $key . '=' . $value . '&';
}
$stringSignTemp = $stringA . 'key=' . $key;
$sign = strtoupper(md5($stringSignTemp));

$params['sign'] = $sign;

// 发送请求
$xml = $this->arrayToXml($params);
$response = $this->postXmlCurl($xml, 'https://api.mch.weixin.qq.com/pay/unifiedorder');
$result = $this->xmlToArray($response);

if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
// 返回支付参数给小程序端
$paymentData = [
'timeStamp' => time(),
'nonceStr' => $nonceStr,
'package' => 'prepay_id=' . $result['prepay_id'],
'signType' => 'MD5',
'paySign' => $this->generatePaySign($appid, $result['prepay_id'], $nonceStr, $key)
];

return response()->json($paymentData);
} else {
return response()->json(['error' => '生成支付订单失败'], 400);
}
}

private function arrayToXml($arr) {
$xml = "<xml>";
foreach ($arr as $key => $val) {
if (is_numeric($val)) {
$xml .= "<" . $key . ">" . $val . "</" . $key . ">";
} else {
$xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
}
}
$xml .= "</xml>";
return $xml;
}

private function xmlToArray($xml) {
libxml_disable_entity_loader(true);
$values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $values;
}

private function postXmlCurl($xml, $url, $useCert = false, $second = 30) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
$data = curl_exec($ch);
curl_close($ch);
return $data;
}

private function generatePaySign($appid, $prepay_id, $nonceStr, $key) {
$params = [
'appId' => $appid,
'timeStamp' => (string)time(),
'nonceStr' => $nonceStr,
'package' => 'prepay_id=' . $prepay_id,
'signType' => 'MD5'
];

ksort($params);
$stringA = '';
foreach ($params as $key => $value) {
$stringA .= $key . '=' . $value . '&';
}
$stringSignTemp = $stringA . 'key=' . $key;
$sign = strtoupper(md5($stringSignTemp));

return $sign;
}

3.2.3 支付回调处理

// 支付回调处理
public function wechatPayNotify(Request $request) {
$xml = file_get_contents('php://input');
$data = $this->xmlToArray($xml);

if ($data['return_code'] == 'SUCCESS' && $data['result_code'] == 'SUCCESS') {
$orderId = $data['out_trade_no'];
$order = Order::find($orderId);

if ($order && $order->status == 'pending') {
$order->status = 'paid';
$order->save();

// 更新用户套餐信息
$user = User::find($order->user_id);
$user->package_id = $order->package_id;
$user->save();
}

return response()->xml(['return_code' => 'SUCCESS', 'return_msg' => 'OK']);
} else {
return response()->xml(['return_code' => 'FAIL', 'return_msg' => '支付失败']);
}
}

4. 微信小程序通知对接

4.1 微信小程序通知流程

  1. 用户在 SSPanel-Uim 中触发某些操作(如套餐到期、流量不足等)。
  2. SSPanel-Uim 调用微信小程序通知接口,发送模板消息给用户。

4.2 代码示例

4.2.1 发送模板消息

// 发送模板消息
public function sendTemplateMessage($openid, $templateId, $data, $page = null) {
$accessToken = $this->getAccessToken();
$url = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token={$accessToken}";

$params = [
'touser' => $openid,
'template_id' => $templateId,
'page' => $page,
'data' => $data
];

$response = $this->postJsonCurl($url, $params);
return json_decode($response, true);
}

private function getAccessToken() {
$appid = 'your-wechat-appid';
$secret = 'your-wechat-secret';
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$appid}&secret={$secret}";
$response = file_get_contents($url);
$data = json_decode($response, true);
return $data['access_token'];
}

private function postJsonCurl($url, $data) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
$result = curl_exec($ch);
curl_close($ch);
return $result;
}

4.2.2 示例:套餐到期通知

// 套餐到期通知
public function sendPackageExpireNotice($userId) {
$user = User::find($userId);
$openid = $user->wechat_openid;
$templateId = 'your-template-id';
$data = [
'keyword1' => ['value' => '套餐即将到期'],
'keyword2' => ['value' => date('Y-m-d H:i:s', strtotime('+7 days'))],
'keyword3' => ['value' => '请及时续费']
];

$this->sendTemplateMessage($openid, $templateId, $data);
}

5. 总结

通过本文的介绍,你已经了解了如何将 SSPanel-Uim 与微信小程序进行对接,包括用户登录、支付、通知等功能的实现。这些功能的实现不仅提升了用户体验,还为 SSPanel-Uim 的管理和运营带来了极大的便利。希望本文的内容能够帮助你在实际项目中顺利实现 SSPanel-Uim 与微信小程序的对接。

如果你在对接过程中遇到任何问题,欢迎在评论区留言,我们将尽力为你解答。

#你可能也喜欢这些API文章!