微信小程序入门学习教程,从入门到精通,微信小程序开放能力详解(14)

以下是对微信小程序开放能力中核心知识点的详细整理,包括每个功能的原理说明、使用方法、完整代码示例(含详细注释),以及综合性案例。


微信小程序开放能力详解

1. 微信登录与授权

1.1 小程序登录流程

小程序登录用于获取用户唯一标识(openid)和会话密钥(session_key),用于后续用户身份识别和数据解密。

登录流程:
  1. 小程序调用 wx.login() 获取临时登录凭证(code)
  2. 将 code 发送到开发者服务器
  3. 服务器用 code + AppID + AppSecret 向微信接口换取 openid 和 session_key
  4. 服务器生成自定义登录态(如 token)返回给小程序
前端代码(小程序端):
// pages/login/login.js
Page({
  onLoad() {
    this.wxLogin();
  },

  async wxLogin() {
    try {
      // 1. 获取临时登录凭证 code
      const res = await wx.login();
      const code = res.code;
      if (!code) {
        console.error('登录失败!获取 code 失败');
        return;
      }

      // 2. 发送 code 到开发者服务器
      const serverRes = await wx.request({
        url: 'https://your-server.com/api/login', // 替换为你的后端地址
        method: 'POST',
        data: { code },
        header: { 'content-type': 'application/json' }
      });

      const { token, openid } = serverRes.data;
      if (token) {
        // 3. 保存 token 到本地(用于后续请求认证)
        wx.setStorageSync('token', token);
        console.log('登录成功,openid:', openid);
      } else {
        console.error('服务器登录失败');
      }
    } catch (err) {
      console.error('登录异常:', err);
    }
  }
});
后端示例(Node.js + Express):
// server.js
const express = require('express');
const axios = require('axios');
const app = express();

app.use(express.json());

app.post('/api/login', async (req, res) => {
  const { code } = req.body;
  const appId = 'YOUR_APPID';
  const appSecret = 'YOUR_APPSECRET';

  try {
    // 向微信服务器换取 openid 和 session_key
    const wxRes = await axios.get(
      `https://api.weixin.qq.com/sns/jscode2session?appid=${appId}&secret=${appSecret}&js_code=${code}&grant_type=authorization_code`
    );

    const { openid, session_key } = wxRes.data;
    if (!openid) {
      return res.status(400).json({ error: '获取 openid 失败' });
    }

    // 此处应生成自定义 token(如 JWT)
    const token = generateToken(openid); // 自定义函数

    // 可选:将 session_key 存入数据库(用于后续解密)
    saveSession(openid, session_key);

    res.json({ token, openid });
  } catch (err) {
    console.error('登录失败:', err);
    res.status(500).json({ error: '服务器错误' });
  }
});

function generateToken(openid) {
  // 简化:实际应使用 JWT 等安全机制
  return Buffer.from(openid).toString('base64');
}

2. 小程序授权管理

2.1 获取用户信息(需用户主动触发)

⚠️ 自 2021 年 4 月起,wx.getUserInfo 不再返回用户信息,必须通过 按钮触发 + <button open-type="getUserInfo"> 获取。

前端代码:
<!-- pages/profile/profile.wxml -->
<button open-type="getUserInfo" bindgetuserinfo="onGetUserInfo">
  获取用户信息
</button>
// pages/profile/profile.js
Page({
  onGetUserInfo(e) {
    const { userInfo, rawData, signature, encryptedData, iv } = e.detail;

    if (userInfo) {
      // 用户已授权,可直接使用 userInfo
      console.log('用户信息:', userInfo);
      wx.setStorageSync('userInfo', userInfo);
    } else {
      // 用户拒绝授权
      wx.showToast({ title: '授权失败', icon: 'none' });
    }
  }
});

💡 注意:encryptedDataiv 可用于后端解密获取更完整的用户数据(如 unionid)。


3. 开放数据校验与解密

3.1 解密用户敏感数据(如手机号、用户信息)

需在后端使用 session_keyencryptedDataiv 进行 AES 解密。

小程序端获取手机号(按钮触发):
<!-- pages/phone/phone.wxml -->
<button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber">
  获取手机号
</button>
// pages/phone/phone.js
Page({
  getPhoneNumber(e) {
    const { encryptedData, iv, cloudID } = e.detail;
    if (!encryptedData || !iv) {
      wx.showToast({ title: '获取失败', icon: 'none' });
      return;
    }

    // 发送加密数据到后端解密
    wx.request({
      url: 'https://your-server.com/api/decrypt-phone',
      method: 'POST',
      data: { encryptedData, iv, openid: wx.getStorageSync('openid') },
      success(res) {
        console.log('解密后的手机号:', res.data.phoneNumber);
      }
    });
  }
});
后端解密(Node.js):
const crypto = require('crypto');

function decryptData(encryptedData, iv, sessionKey) {
  // Base64 解码
  const encrypted = Buffer.from(encryptedData, 'base64');
  const ivBuf = Buffer.from(iv, 'base64');
  const sessionKeyBuf = Buffer.from(sessionKey, 'base64');

  // AES-128-CBC 解密
  let decipher = crypto.createDecipheriv('aes-128-cbc', sessionKeyBuf, ivBuf);
  decipher.setAutoPadding(false);

  let decoded = decipher.update(encrypted, '', 'binary');
  decoded += decipher.final('binary');

  // 去除 PKCS#7 填充
  const pad = decoded.charCodeAt(decoded.length - 1);
  decoded = decoded.substring(0, decoded.length - pad);

  return JSON.parse(decoded);
}

// 路由处理
app.post('/api/decrypt-phone', (req, res) => {
  const { encryptedData, iv, openid } = req.body;
  const sessionKey = getSessionKeyFromDB(openid); // 从数据库获取之前保存的 session_key

  try {
    const phoneInfo = decryptData(encryptedData, iv, sessionKey);
    res.json({ phoneNumber: phoneInfo.phoneNumber });
  } catch (err) {
    res.status(400).json({ error: '解密失败' });
  }
});

4. 微信支付

4.1 小程序支付流程

  1. 用户在小程序下单
  2. 小程序请求开发者服务器创建订单
  3. 服务器调用微信统一下单 API,获取 prepay_id
  4. 服务器返回支付参数给小程序
  5. 小程序调用 wx.requestPayment 发起支付
小程序端发起支付:
// pages/order/pay.js
Page({
  async payOrder() {
    try {
      // 1. 请求后端创建订单并获取支付参数
      const res = await wx.request({
        url: 'https://your-server.com/api/create-order',
        method: 'POST',
        data: { goodsId: '123', total: 100 }
      });

      const { timeStamp, nonceStr, package, signType, paySign } = res.data;

      // 2. 调用微信支付
      await wx.requestPayment({
        timeStamp,        // 时间戳(字符串)
        nonceStr,         // 随机字符串
        package,          // 统一下单返回的 prepay_id 格式:prepay_id=***
        signType,         // 签名算法,通常为 'MD5' 或 'HMAC-SHA256'
        paySign           // 签名
      });

      wx.showToast({ title: '支付成功' });
    } catch (err) {
      wx.showToast({ title: '支付失败', icon: 'none' });
    }
  }
});
后端统一下单(Node.js 示例):
const crypto = require('crypto');

function sign(params, key) {
  // 按 key 字典序排序
  const sorted = Object.keys(params)
    .sort()
    .map(k => `${k}=${params[k]}`)
    .join('&');
  const str = sorted + '&key=' + key;
  return crypto.createHash('md5').update(str, 'utf8').digest('hex').toUpperCase();
}

app.post('/api/create-order', async (req, res) => {
  const { goodsId, total, openid } = req.body;

  // 调用微信统一下单接口
  const params = {
    appid: 'YOUR_APPID',
    mch_id: 'YOUR_MCH_ID',
    nonce_str: Math.random().toString(36).substr(2, 15),
    body: '商品支付',
    out_trade_no: 'ORDER_' + Date.now(), // 商户订单号
    total_fee: total, // 单位:分
    spbill_create_ip: '127.0.0.1',
    notify_url: 'https://your-server.com/api/pay-notify',
    trade_type: 'JSAPI',
    openid: openid
  };

  params.sign = sign(params, 'YOUR_API_KEY'); // 商户 API 密钥

  const xml = objectToXml(params); // 需实现 XML 序列化
  const wxRes = await axios.post('https://api.mch.weixin.qq.com/pay/unifiedorder', xml, {
    headers: { 'Content-Type': 'application/xml' }
  });

  const result = xmlToJs(wxRes.data); // 需实现 XML 解析

  if (result.return_code === 'SUCCESS' && result.result_code === 'SUCCESS') {
    const prepayId = result.prepay_id;

    // 构造小程序支付参数
    const payParams = {
      appId: 'YOUR_APPID',
      timeStamp: Math.floor(Date.now() / 1000).toString(),
      nonceStr: Math.random().toString(36).substr(2, 15),
      package: `prepay_id=${prepayId}`,
      signType: 'MD5'
    };
    payParams.paySign = sign(payParams, 'YOUR_API_KEY');

    res.json(payParams);
  } else {
    res.status(400).json({ error: '下单失败' });
  }
});

5. 分享、收藏与转发

5.1 自定义分享内容

Page({
  onShareAppMessage() {
    return {
      title: '快来一起使用这个小程序!',
      path: '/pages/index/index?from=share',
      imageUrl: 'https://example.com/share.jpg' // 自定义分享图
    };
  }
});

⚠️ 必须用户主动点击页面右上角菜单或 button open-type="share" 才能触发。


6. 小程序订阅消息

6.1 引导用户订阅消息

// 请求订阅
wx.requestSubscribeMessage({
  tmplIds: ['模板ID1', '模板ID2'],
  success(res) {
    console.log('订阅成功:', res);
  },
  fail(err) {
    console.error('订阅失败:', err);
  }
});

6.2 后端发送订阅消息

// 获取 access_token
const tokenRes = await axios.get(
  `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=SECRET`
);
const accessToken = tokenRes.data.access_token;

// 发送消息
await axios.post(
  `https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=${accessToken}`,
  {
    touser: openid,
    template_id: '模板ID',
    page: '/pages/order/detail?id=123',
    data: {
      thing1: { value: '订单已发货' },
      time2: { value: '2025-10-10 10:00' }
    }
  }
);

综合性案例:电商小程序核心流程

场景:用户登录 → 浏览商品 → 下单支付 → 支付成功后发送订阅消息

1. 登录并获取用户信息

// app.js
App({
  onLaunch() {
    wx.login().then(res => {
      // 发送 code 到后端登录
    });
  }
});

2. 商品页“立即购买”按钮

<button bindtap="buyNow">立即购买</button>

3. 支付成功后订阅通知

// 支付成功回调中
wx.requestPayment({ ... }).then(() => {
  // 弹出订阅弹窗
  wx.requestSubscribeMessage({
    tmplIds: ['ORDER_STATUS_TEMPLATE_ID'],
    success() {
      // 可选:记录用户已订阅
    }
  });

  // 跳转到订单页
  wx.navigateTo({ url: '/pages/order/success' });
});

4. 后端在发货时发送订阅消息

// 当订单状态变为“已发货”
sendSubscribeMessage(openid, {
  thing1: { value: '您的订单已发货' },
  time2: { value: new Date().toLocaleString() }
});

本章小结

功能是否需用户触发是否需后端参与安全要求
登录否(自动)session_key 保密
获取用户信息是(按钮)否(基础信息)/是(完整信息)encryptedData 解密需 session_key
获取手机号是(按钮)必须后端解密
微信支付是(用户确认)签名验证、HTTPS
订阅消息是(用户授权)模板 ID 审核
分享是(菜单/按钮)

📌 所有涉及用户隐私或支付的操作,必须通过后端完成关键逻辑,前端仅负责交互和展示。


如需进一步扩展(如云开发简化后端、使用云函数解密等),可结合微信云开发能力实现无服务器架构。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值