一、微信优势

1、什么场景下使用微信分享

● 线下推广、线上传播
● 分享渠道:h5、小程序、APP
● 分享方式:微信好友、朋友圈、QQ好友、QQ空间、微博

2、微信分享带来的收益

● 拉新
● 留存
● 提升用户的粘性
● 品牌传播

3、微信分享的好处

● 标题直观
● 内容清晰
● 醒目的logo
● 统一的分享外观, 用户体验非常好

二、微信公众号h5网页授权登录

1、初始化express项目

$ cnpm i express-generator -g
$ express -h
$ express sharepay_server
$ cnpm i && node bin/www || pm2 start bin/www

2、安装依赖

cnpm i request memory-cache create-hash --save-dev

// request 请求
// memory-cache 内存缓存
// create-hash 加密算法

3、微信公众号网页授权流程

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

● 通过此链接用户同意授权, 获取code

// appid 公众号的唯一标识
// redirect_uri 授权后重定向的回调链接地址
// response_type 返回类型, 固定写入code
// scope 应用授权作用域
// 【snsapi_base 不弹出授权页面,直接跳转,只能获取用户openid】
// 【snsapi_userinfo 弹出授权页面, 可通过openid拿到一些用户信息】

● 同意授权后, 页面将跳转到redirect_uri/?code=CODE&state=STATE

// code 作为换取access_token的票据

● 紧接着通过code换取网页授权的access_token

https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

// appid 公众号的唯一标识
// secret 公众号的秘钥
// code 换取access_token的票据
// grant_type 固定写入authorization_code

// 返回值
// access_token 网页授权接口调用凭证
// expires_in access_token接口调用凭证超时时间(s)
// refresh_token 用户刷新access_token
// openid 用户唯一标识
// scope 用户授权的作用域

● 获取用户信息确保scope为snsapi_userinfo, 根据access_token和openid拉取用户信息

https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN

// 入参
// access_token 网页授权接口调用凭证
// openid 用户的唯一标识
// lang 语言

4、JS-SDK签名算法

● 首先需要获取token

https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

● 然后获取ticket

https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi

● 加密算法 create-hash

5、Node连接Mongodb

$ cnpm i mongodb --save-dev

/**
* @description Mongodb公共文件,统一操作数据库
*/
let MongoClient = require(‘mongodb’).MongoClient
let util = require(‘…/…/util/util’)
let url = ‘mongodb://127.0.0.1:27017/sharepayServer’

exports.query = (data, table) => {return new Promise((resolve, reject) => {connect((dbase, db) => {dbase.collection(table).find(data).toArray((err, res) => {if(err){throw err;}else{db.close();resolve(util.handleSuccess(res))}})})})
}exports.insert = (data, table) => {return new Promise((resolve, reject) => {connect((dbase, db) => {dbase.collection(table).insertOne(data, (err, res) => {if (err) {throw err;} else {db.close();resolve(util.handleSuccess(res))}})})})
}function connect(callbck) {MongoClient.connect(url, (err, db) => {if(err) throw err;let dbase = db.db('sharepayServer');callbck(dbase, db);})
}

6、服务端代码

// config
module.exports = {
wx: {
appId: ‘wx02432db3c653775b’,
appSecret: ‘08cf50652e306a9e6d038a400f552bf1’
},
mp: {
appId: ‘wx2d964cda16732f19’,
appSecret: ‘7f03a96c1a8ba04449b2a3f9431a9438’
}
}

// util
module.exports = {
// 生成随机数
createNonceStr() {
return Math.random().toString(36).substr(2, 15)
},
// 生成时间戳
createTimeStamp() {
return new Date().getTime() / 1000 + ‘’
},
// Object转换成json并进行排序
raw(args) {
let keys = Object.keys(args).sort()
let obj = {}
keys.forEach((key) => {
obj[key] = args[key]
})
// 将对象转换为&分割的参数
let val = ‘’
for (let k in obj) {
val += ‘&’ + k + ‘=’ + obj[k]
}
return val.substr(1)
},
// 对请求结果进行统一封装处理
handleResponse(err, response, body) {
if(!err && response.statusCode == ‘200’) {
let data = JSON.parse(body)
if(data && !data.errcode) {
return this.handleSuccess(data)
} else {
return this.handleFail(data.errmsg, data.errcode)
}
} else {
return this.handleFail(err, 10009)
}
},
handleSuccess(data = ‘’) {
return {
code: 0,
data,
msg: ‘’
}
},
handleFail(msg = ‘’, code = 10001) {
return {
code,
data: ‘’,
msg
}
}
}

// 依赖引入
let express = require(‘express’)
let cache = require(‘memory-cache’)
let createHash = require(‘create-hash’)
let config = require(‘./config’).wx
let common = require(‘…/common/index’)
let dao = require(‘…/common/db’)
let util = require(‘…/…/util/util’)
let router = express.Router()

// 用户授权重定向
router.get('/redirect', (req, res) => {// 获取重定向地址let redirectUrl = req.query.url// 将重定向url进行缓存,便于公用cache.put('redirectUrl', redirectUrl)let scope = req.query.scopelet callback = 'http://m.imooc.com/api/wechat/getOpenId'let authorizeUrl = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${config.appId}&redirect_uri=${callback}&response_type=code&scope=${scope}&state=STATE#wechat_redirect`res.redirect(authorizeUrl)
})// 根据code获取用户的openId
router.get('/getOpenId', async (req, res) => {let code = req.query.codeif(!code) {util.handleFail('当前未获取到code码')} else {let result = await common.getAccessToken(code)if(result.code == 0) {let data = result.datalet expire_time = 1000 * 60 * 60 * 2cache.put('access_token', data.access_token, expire_time)cache.put('openId', data.openid, expire_time)res.cookie('openId', data.openid, { maxAge: expire_time })// 查询数据库看看有没有openid信息let openId = data.openidlet userRes = await dao.query({'openid': openId}, 'users')if (userRes.code == 0) {if (userRes.data.length > 0) {let redirectUrl = cache.get('redirectUrl')res.redirect(redirectUrl)} else {let userData = await common.getUserInfo(data.access_token, openId)let insertData = await dao.insert(userData.data,'users')if (insertData.code == 0) {let redirectUrl = cache.get('redirectUrl');res.redirect(redirectUrl);} else {res.json(insertData);}}} else {res.json(userRes)}} else {res.json(result)}}
})// 拉取用户信息
router.get('/getUserInfo', async (req, res) => {let access_token = cache.get('access_token')let openId = cache.get('openId')let result = await common.getUserInfo(access_token, openId)res.json(result)
})// jssdk签名算法
router.get('/jssdk', async (req, res) => {let url = req.query.urllet result = await common.getToken()if (result.code == 0) {let tocken = result.data.access_tokencache.put('token', tocken)let ticket = await common.getTicket(tocken)if (ticket.code == 0) {let data = ticket.datalet params = {noncestr: util.createNonceStr(),jsapi_ticket: data.ticket,timestamp: util.createTimeStamp(),url}let str = util.raw(params)// 加密算法 create-hashlet sign = createHash('sha1').update(str).digest('hex')res.json(util.handleSuccess({appId: config.appId, // 必填,公众号的唯一标识timestamp: params.timestamp, // 必填,生成签名的时间戳nonceStr: params.nonceStr, // 必填,生成签名的随机串signature: sign,// 必填,签名jsApiList: ['updateAppMessageShareData','updateTimelineShareData','onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ','onMenuShareQZone','chooseWXPay'] // 必填,需要使用的JS接口列表}))}}
})module.exports = router

/**
* @description 微信接口统一封装处理
*/
let request = require(‘request’)
let config = require(‘…/pay/config’).wx
let util = require(‘…/…/util/util’)

exports.getAccessToken = (code) => {let tokenUrl = `https://api.weixin.qq.com/sns/oauth2/access_token?appid=${config.appId}&secret=${config.appSecret}&code=${code}&grant_type=authorization_code`return new Promise((resolve, reject) => {request.get(tokenUrl, (err, response, body) => {let result = util.handleResponse(err, response, body)resolve(result)})})
}exports.getUserInfo = (access_token, openId) => {let userinfo = `https://api.weixin.qq.com/sns/userinfo?access_token=${access_token}&openid=${openId}&lang=zh_CN`return new Promise((resolve, reject) => {request.get(userinfo, (err, response, body) => {let result = util.handleResponse(err, response, body)resolve(result)})})
}// 获取基础接口的token
exports.getToken = () => {let token = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${config.appId}&secret=${config.appSecret}`return new Promise((resolve, reject) => {request.get(token, (err, response, body) => {let result = util.handleResponse(err, response, body)resolve(result)})})
}// 根据token获取ticket
exports.getTicket = (token) => {let ticket = `https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=${token}&type=jsapi`return new Promise((resolve, reject) => {request.get(ticket, (err, response, body) => {let result = util.handleResponse(err, response, body)resolve(result)})})
}

7、客户端代码

// API
export default {
wechatRedirect:‘/api/wechat/redirect?url=http%3A%2F%2Fm.imooc.com%2F%23%2Findex&scope=snsapi_userinfo’,
wechatConfig:‘/api/wechat/jssdk’,
getUserInfo:‘/api/wechat/getUserInfo’,
payWallet: ‘/api/wechat/pay/payWallet’
}

import API from ‘./api/index’
import wx from ‘weixin-js-sdk’
import util from ‘./util/index’
export default {
name: ‘app’,
mounted () {
this.checkUserAuth()
},
methods: {
// 检查用户是否授权过
checkUserAuth () {
let openId = this.KaTeX parse error: Expected 'EOF', got '}' at position 170: … } }̲, // 获取…http.get(API.wechatConfig+‘?url=’+location.href.split(‘#’)[0]).then(function(response){
let res = response.data;
if(res.code == 0){
let data = res.data;
wx.config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: data.appId, // 必填,公众号的唯一标识
timestamp: data.timestamp, // 必填,生成签名的时间戳
nonceStr: data.nonceStr, // 必填,生成签名的随机串
signature: data.signature,// 必填,签名
jsApiList: data.jsApiList // 必填,需要使用的JS接口列表
})
wx.ready(()=>{
util.initShareInfo(wx);
})
}
})
}
}
}

三、小程序登录分享

1、小程序分享的好处

● 体验友好
● 利于传播
● 只能分享给好友
● 不能分享第三方应用

2、小程序公共机制

目录结构

|- assets 【静态资源】
|— images 【图片】
|— wxss 【公共样式】
|- env 【环境配置*】
|— index.js
|- http 【服务请求*】
|— api.js 【dao层api地址】
|— request.js 【fetch请求封装】
|- pages 【页面】
|- utils 【工具*】
|— router.js 【通用路由跳转】
|— store.js 【通用本地存储】
|— util.js 【通用工具方法】
|- app.js 【小程序入口文件*】
|- app.json 【小程序入口配置】
|- app.wxss 【小程序全局样式】

// env
module.exports = {
mockApi: ‘’,
Dev: {
baseApi: ‘http://localhost:3000’
},
Test: {},
Slave: {},
Prod: {}
}

// http-api
module.exports = {
getSession: ‘/api/mp/getSession’,
login: ‘/api/mp/login’,
payWallet: ‘/api/mp/pay/payWallet’
}

// http-request
/*** @description 公共请求方法
*/
let store = require('../utils/store.js')
let system = store.getSystemInfo();
const clientInfo = {'clientType': 'mp','appnm': 'share_pay','model': system.model,'os': system.system,'screen': system.screenWidth + '*' + system.screenHeight,'version': App.version,'channel': 'miniprogram'
}
const errMsg = '服务异常,请稍后重试';module.exports = {fetch: (url, data = {}, option = {}) => {let { loading = true, toast = true, isMock = false, method = 'get' } = optionreturn new Promise((resolve, reject) => {let env = isMock ? App.config.mockApi : App.config.baseApiwx.request({url: env + url,data,method,header: {'clientInfo': JSON.stringify(clientInfo)},success: (result) => {let res = result.data // {code:0,data:'',message:''}if (res.code == 0) {if (loading) {wx.hideLoading();}resolve(res.data)} else {if (toast) {wx.showToast({mask: true,title: res.messsage,icon: 'none'})} else {wx.hideLoading()}reject(res)}},fail: (e = { code: -1, msg: errMsg, errMsg }) => {let msg = e.errMsg;if (msg == 'request:fail timeout') {msg = '服务请求超时,请稍后处理'}wx.showToast({title: msg,icon: 'none'})reject(e)}})})}
}

// util-router.js
/**
* @description 通用的路由跳转文件
*/
const routerPath = {
‘index’: ‘/pages/index/index’,
‘pay’: ‘/pages/pay/pay’,
‘activity’: ‘/pages/activity/activity’
}

module.exports = {// 页面跳转push(path, option = {}) {if (typeof path == 'string') {option.path = path} else {option = path}// 获取url地址let url = routerPath[option.path]let { query = {}, openType, duration } = optionlet params = this.parse(query)if (params) {url += '?' + params}duration ? setTimeout(() => {this.to(openType, url)}, duration) : this.to(openType, url)},to(openType, url) {let obj = { url }if (openType == 'redirect') {wx.redirectTo(obj)} else if (openType == 'reLaunch') {wx.reLaunch(obj)} else if (openType == 'back') {wx.navigateBack({delta: 1})} else {wx.navigateTo(obj)}},parse(data) {let arr = []for (let key in data) {arr.push(key + '=' + data[key])}return arr.join('&')}
}// util-store.js
/*** @description Storage通用存储文件定义*/
const STORAGE_KEY = 'share_pay'
module.exports = {setItem(key, value, module_name) {if (module_name) {let module_name_info = this.getItem(module_name);module_name_info[key] = value;wx.setStorageSync(module_name, module_name_info)} else {wx.setStorageSync(key, value)}},// 获取值getItem(key, module_name) {if (module_name) {let val = this.getItem(module_name);if (val) return val[key];return '';} else {return wx.getStorageSync(key);}},// 删除或者清空值clear(key) {key ? wx.removeStorageSync(key) : wx.clearStorageSync();},getSystemInfo() {return wx.getSystemInfoSync();}
}

// app.js
/**
* @description 小程序入口
*/
let Api = require(‘./http/api’)
let request = require(‘./http/request’)
let router = require(‘./utils/router’)
let config = require(‘./env/index’)
let env = ‘Dev’

App.version = '1.0.0' // 开发版本
App.config = config[env] // 根据环境变量获取对应的配置信息
App.config.env = env
App.config.mockApi = config.mockApi App({Api,router,get: request.fetch,post: (url, data, option) => {option.method = 'post'return request.fetch(url, data, option)},onLaunch: function () {},globalData: {userInfo: null}
})

3、小程序授权登录及分享

● 根据userId/openId判断当前用户是否登录
● 调用login获取到code
● 调用服务器端根据code换取openid
● 通过用户授权获取用户信息, 存入到后台数据库

const app = getApp()
let store = require(‘./…/…/utils/store.js’)
let Api = app.Api
let $router = app.router
Page({
data: {
userId: store.getItem(‘userId’)
},
onLoad: function () {
// 判断用户是否登录
if (!this.data.userId) {
this.getSession()
}
},
// 获取登录code, 换取openid
getSession() {
wx.login({
success: (res) => {
if (res.code) {
app.get(Api.getSession, {
code: res.code
}).then(res => {
store.setItem(‘openId’, res.openid);
}).catch((err) => {
console.log(‘error:’ + res.message)
})
}
}
})
},
// 获取用户信息
getUserInfo(e) {
let userInfo = e.detail.userInfo
userInfo.openid = store.getItem(‘openId’)
app.get(Api.login, {
userInfo
}).then(res => {
store.setItem(‘userId’, res.userId)
this.setData({
userId: res.userId
})
})
},
recharge() {
$router.push(‘pay’)
},
activity() {
$router.push(‘activity’)
},
onShareAppMessage() {
return {
title: ‘欢迎体验分享’,
path: ‘/pages/index/index’,
imageUrl: ‘/assets/images/share_mp_logo.png’
}
}
})

4、服务端代码

let express = require(‘express’)
let router = express.Router()
let request = require(‘request’)
let config = require(‘./config’)
let util = require(‘./…/…/util/util’)
let dao = require(‘./…/common/db’)

config = Object.assign({}, config.mp)// 获取session接口
router.get('/getSession', (req, res) => {let code = req.query.codeif (!code) {res.json(util.handleFail('code不能为空', 10001))return}let sessionUrl = `https://api.weixin.qq.com/sns/jscode2session?appid=${config.appId}&secret=${config.appSecret}&js_code=${code}&grant_type=authorization_code`request(sessionUrl, (err, response, body) => {let result = util.handleResponse(err, response, body)res.json(result)})
})// 小程序授权登录
router.get('/login', async (req, res) => {let userInfo = JSON.parse(req.query.userInfo)if (!userInfo) {res.json(util.handleFail('用户信息不能为空', 10002))} else {// 查询当前用户是否已经注册let userRes = await dao.query({ openid: userInfo.openid }, 'users_mp')if (userRes.code == 0) {if (userRes.data.length > 0) {res.json(util.handleSuccess({userId: userRes.data[0]._id}))} else {let insertData = await dao.insert(userInfo, 'users_mp')if (insertData.code == 0) {let result = await dao.query({ openid: userInfo.openid }, 'users_mp');res.json(util.handleSuccess({userId: result.data[0]._id}))} else {res.json(insertData)}}} else {res.json(userRes)}}
})module.exports = router

四、小程序云登录

1、云开发好处

● 节约开发和运维成本
● 关注核心功能开发
● 和普通小程序开发一致
● 拥有微信更多的能力

2、调用云函数登录

// 云函数 - getOpenId
const cloud = require(‘wx-server-sdk’)
cloud.init()

// 云函数入口函数
exports.main = async (event, context) => {const wxContext = cloud.getWXContext()return {event,openid: wxContext.OPENID,appid: wxContext.APPID,unionid: wxContext.UNIONID,}
}

// 云函数 - login

const cloud = require('wx-server-sdk')
cloud.init()// 连接数据库
const db = cloud.database()const _users = db.collection('users')exports.main = async (event, context) => {// 获取上下文const wxContext = cloud.getWXContext()// 判断用户是否存在const result = await _users.where({openid: wxContext.OPENID}).limit(1).get()if(result.data.length == 0) {// 如果用户不存在, 则需要插入到数据库然后返回userId(_id)await _users.add({data: {...event.userInfo,openid: wxContext.OPENID}})// 再次查询const result = await _users.where({openid: wxContext.OPENID}).limit(1).get()return {userId: result.data[0]._id}} else {// 用户存在, 则直接返回userIdreturn {userId: result.data[0]._id}}
}

const app = getApp()
let store = require(‘./…/…/utils/store.js’)
let $router = app.router
Page({
data: {
userId: store.getItem(‘userId’)
},
onLoad() {
this.getOpenId()
},

    getOpenId() {wx.cloud.callFunction({name: 'getOpenId'}).then(res => {let { openid } = res.resultstore.setItem('openId', openid)})},// 获取用户信息getUserInfo(e) {// 获取用户信息let userInfo = e.detail.userInfo// 调用云函数wx.cloud.callFunction({name: 'login',data: {userInfo}}).then(res => {store.setItem('userId', res.result.userId)this.setData({userId: res.result.userId})})},recharge() { $router.push('pay')},activity() { $router.push('activity')},onShareAppMessage() {return {title: '欢迎体验分享',path: '/pages/index/index',imageUrl: '/assets/images/share_mp_logo.png'}}
})

五、小程序支付

1、前沿

● 微信认证
● 微信支付认证
○ 注册商户号/关联商户号
○ H5配置支付授权目录
○ 非h5 授权AppId
○ 设置API秘钥
● https证书

2、支付流程

前端

● https://developers.weixin.qq.com/miniprogram/dev/api/open-api/payment/wx.requestPayment.html

后端

● 拼接常规参数
● 生成签名
● 拼接xml数据
● 调用统一下单接口
● 获取预支付ID: prepay_id
● 生成支付SDK
● 定义回调接口, 接收微信支付消息

3、服务端代码

// config.js
// 商户号 - 用于支付
mch:{
mch_id: ‘’, // 商户号id
key:‘’ // 秘钥
}

// 小程序支付
router.get(‘/pay/payWallet’, (req, res) => {
let openId = req.query.openId // 用户openid
let appId = config.appId;
let attach = “thanks use my pay” // 附加数据
let body = “welcome” // 详细内容
let total_fee = req.query.money // 支付费用(分)
let notify_url = “http://localhost:3000/api/mp/pay/callback” // 通知地址
let ip = “123.57.2.144” //ip地址
wxpay.order(appId, attach, body, openId, total_fee, notify_url, ip).then((result) => {
res.json(util.handleSuccess(result));
}).catch((result) => {
res.json(util.handleSuccess(result.toString()))
})
res.json(util.handleSuccess())
})

// 支付回调通知
router.get('/pay/callback', (req, res) => {res.json(util.handleSuccess())
})

/**
* @description 微信小程序、H5通用支付封装
*/
let config = require(‘./…/pay/config’)
let request = require(‘request’)
let util = require(‘…/…/util/util’)
let createHash = require(‘create-hash’)
let xml = require(‘xml2js’)
config = config.mch

module.exports = {order: (appid, attach, body, openid, total_fee, notify_url, ip) => {return new Promise((resolve, reject) => {let nonce_str = util.createNonceStr() // 随机数let out_trade_no = util.getTradeId('mp') // 交易订单号// 支付前需要先获取支付签名let sign = this.getPrePaySign(appid, attach, body, openid, total_fee, notify_url, ip, nonce_str, out_trade_no)// 通过参数和签名组装xml数据let sendData = this.wxSendData(appid, attach, body, openid, total_fee, notify_url, ip, nonce_str, out_trade_no, sign)// 调用统一下单接口let self = thislet url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'request({url,method: 'POST',body: sendData}, (err, response, body) => {if (!err && response.statusCode == 200) {// 通过xml2js来处理xml的数据xml.parseString(body.toString('utf-8'), (error, res) => {if (!error) {// 获取到转换后的内容let data = res.xmlif (data.return_code[0] == 'SUCCESS' && data.result_code[0] == 'SUCCESS') {// 获取预支付的IDlet prepay_id = data.prepay_id || []// 返回小程序端需要获取的参数值let payResult = self.getPayParams(appid, prepay_id[0])resolve(payResult)}}})} else {resolve(util.handleFail(err))}})})},//  生成预支付的签名getPrePaySign: (appid, attach, body, openid, total_fee, notify_url, ip, nonce_str, out_trade_no) => {let params = {appid,attach,body,mch_id: config.mch_id,nonce_str,notify_url,openid,out_trade_no,spbill_create_ip: ip,total_fee,trade_type: 'JSAPI'}let string = util.raw(params) + '&key=' + config.keylet sign = createHash('md5').update(string).digest('hex')return sign.toUpperCase()},// 签名成功后 ,根据参数拼接组装XML格式的数据,调用下单接口wxSendData: (appid, attach, body, openid, total_fee, notify_url, ip, nonce_str, out_trade_no, sign) => {let data = '<xml>' +'<appid><![CDATA[' + appid + ']]></appid>' +'<attach><![CDATA[' + attach + ']]></attach>' +'<body><![CDATA[' + body + ']]></body>' +'<mch_id><![CDATA[' + config.mch_id + ']]></mch_id>' +'<nonce_str><![CDATA[' + nonce_str + ']]></nonce_str>' +'<notify_url><![CDATA[' + notify_url + ']]></notify_url>' +'<openid><![CDATA[' + openid + ']]></openid>' +'<out_trade_no><![CDATA[' + out_trade_no + ']]></out_trade_no>' +'<spbill_create_ip><![CDATA[' + ip + ']]></spbill_create_ip>' +'<total_fee><![CDATA[' + total_fee + ']]></total_fee>' +'<trade_type><![CDATA[JSAPI]]></trade_type>' +'<sign><![CDATA[' + sign + ']]></sign>' +'</xml>'return data},// 生成前端需要的sdk的配置getPayParams: (appId, prepay_id) => {let params = {appId,timeStamp: util.createTimeStamp(),nonceStr: util.createNonceStr(),package: 'prepay_id=' + prepay_id,signType: 'MD5'}let paySign = util.getSign(params, config.key)params.paySign = paySignreturn params}
}

六、h5支付

https://www.yuque.com/robinson/fe-guide/gt9hb4

1、准备

● h5支付后台可以是http/https, 需配置支付授权目录
● 小程序所有的接口都必须是https, 无需配置授权目录
● 小程序云开发只有云函数, 无需配置授权目录
● 域名、服务器、证书准备
● 微信认证、企业/个体主体认证
● 支付认证, 开通商户平台
● 公众号、小程序、小程序云绑定商户号

2、服务端代码

// 微信支付
router.get(‘/pay/payWallet’, (req, res) => {
let openId = req.cookies.openId
let appId = config.appId
// 商品简单描述
let body = ‘welcome’
// 钱数, 如果是post请求则使用req.body获取参数
let total_fee = req.query.money
// 微信成功回调地址, 用于保存用户支付订单信息
let notify_url = ‘http://m.imooc.com/api/wechat/pay/callback’
// 通过微信支付认证的商户ID
let mch_id = config.mch_id
// 附加数据
let attach = ‘123’
// 调用微信支付API的ip地址
let ip = ‘123.57.2.144’
// 封装好的微信下单接口
wxpay
.order(appId, attach, body, mch_id, openId, total_fee, notify_url, ip)
.then(result => {
res.json(util.handleSuccess(result))
})
.catch((result) => {
res.json(util.handleFail(result))
})
})

// 支付成功的回调
router.post('/pay/callback', (req, res) => {xml.parseString(req.rawBody.toString('utf-8'), async(error, xmlData) => {if (error) {logger.error(error);res.send('fail')return;}let data = xmlData.xmllet order = {openId: data.openid[0],totalFee: data.total_fee[0],isSubscribe: data.is_subscribe[0],orderId: data.out_trade_no[0],transactionId: data.transaction_id[0],tradeType: data.trade_type[0],timeEnd: data.time_end[0]}// 插入订单数据let result = await dao.insert(order, 'orders');if (result.code == 0) {// 向微信发送成功数据let data = '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></r' +'eturn_msg></xml>';res.send(data);} else {res.send('FAIl');}})
})

3、前端代码

this.KaTeX parse error: Expected '}', got 'EOF' at end of input: … _this.route.push(‘/index’);
} else if (res.errMsg == “chooseWXPay:fail”) {
alert(‘支付取消’);
}
}.bind(this),
cancel: function () {
alert(‘支付取消’);
}.bind(this),
fail: function (res) {
alert(res.message || ‘支付失败’);
}.bind(this)
})
}
});

微信小程序(登录、分享、支付)相关推荐

  1. 微信小程序登录、支付流程简介

    登录流程 登录流程 1. 获取用户的头像和昵称信息(可选) 2. 通过wx.login() 获取code 3. 把code+用户信息(可选)发送给后端 4. 后端通过code+appid+AppSec ...

  2. 微信小程序登录获取Token以及微信支付

    微信小程序登录获取Token 在调用登录接口api之前,要先获取以下五个参数 后四个参数获取:通过为button按钮添加 open-type="getUserInfo" (固定写法 ...

  3. plsql developer无监听程序_微信小程序支持分享到朋友圈啦!技术解读跟我来

    千呼万唤始出来!微信小程序页面分享到朋友圈的功能,终于在安卓系统灰度测试了!目前只在安卓系统!只在安卓系统!只在安卓系统!iOS系统还没有办法体验. 首先,我们看一下官方文档的描述,解读一下小程序分享 ...

  4. 小程序 微信统计表格_微信小程序登录机制

    " 不是0,也不是1,有0也有1 " 总有一个瞬间,你想记录当下的一些事情,所以有了这篇文章,不会口吐芬芳,我直接开门了,但愿能让你见山. 1. 背景 21 届的校园招聘已经打响了 ...

  5. 微信小程序封装分享与分销功能

    在微信小程序中,可以很简单的分享一个页面,比微信H5简单多了,然而,分享出去的页面(也叫卡片),打开后只是一个单独的页面,没有底部导航栏,点击返回按钮的时候就直接退出小程序了.如果需要去到首页,还必须 ...

  6. 【SpringBoot学习】39、SpringBoot 集成 wxJava 微信小程序:订单支付

    文章目录 SpringBoot 集成 wxJava 微信小程序:订单支付 1.整合 wxJava 小程序 2.支付配置类 3.application.yml 配置 4.授权登录流程 5.uniapp ...

  7. SpringBoot实现微信小程序登录功能

    SpringBoot实现微信小程序登录 微信小程序登录流程 登录流程图 前端代码 后端代码 微信小程序登录流程 微信小程序官方文档:微信小程序官方文档 第一次学习微信小程序的登录,以前也好奇微信小程序 ...

  8. 微信小程序登录方法,授权登录及获取微信用户手机号

    ✅作者简介: 大家好五一快乐,我是痴心阿文,你们的学友哥,今天给大家分享微信小程序登录方法!

  9. 微信小程序:分享及其好友代付款

    微信小程序:分享及其好友代付款 先贴一个微信的官方通告 大概的意思是说从2018年10月后的分享功能,取消了success,fail,complete三个函数的功能,用户分享必须是用户主动触发 首先还 ...

  10. 用Spring Boot完成微信小程序登录

    使用Spring Boot完成微信小程序登录 由于微信最近的版本更新,wx.getUserInfo()的这个接口即将失效,将用wx.getUserProfile()替换,所以近期我也对自己的登录进行更 ...

最新文章

  1. 再见了,Python!!
  2. 怎样用计算机进入手机驱动程序,手机驱动怎么安装?
  3. 商业新知产品总监王宇:创业公司不要太看重「增长黑客」运营理念
  4. 分享8款简单大气的jQuery/CSS3图片特效
  5. ActiveX控件的另类免费签名法
  6. 小程序api 分享scene_抛弃微信小程序API的嵌套回调吧!
  7. 数据结构之广义表的相关知识点
  8. 参数估计:文本分析的参数估计方法
  9. pythonjson构建二维数组_在Python中从JSON构建表
  10. ubuntu18.04修改vscode字体
  11. 修改网课播放器倍速播放
  12. java学术论文_java毕业设计论文-学术参考网
  13. epson连接计算机后无法打印,如何解决连接到Epson打印机后计算机无法打印的问题...
  14. 新广告法违规词、敏感词在线检测工具 淘宝违规词检测、查询
  15. iEx.ec——云计算业务的区块链革命
  16. plc梯形图转c语言,plc梯形图转换成stl程序的实例教程
  17. MATLAB 读取和显示 bin 文件数据
  18. 将 5 万行 Java 代码移植到 Go 学到的经验
  19. 字节跳动全链路压测(Rhino)的实践
  20. 抽象类和抽象方法_30酷抽象和背景Photoshop教程

热门文章

  1. 撇开PUE,评估数据中心增长的真正环境影响
  2. Grammar-based construction 语法驱动的构造
  3. 【山无遮,海无拦】LeetCode题集 线性枚举之最值算法
  4. SSL-ZYC 游戏
  5. 成为Linux大神——必须要具备的基本技能!
  6. jquery选择器可以利用后代和直系后代选择器连续选择元素
  7. 机器学习PAI为你自动写歌词,妈妈再也不用担心我的freestyle了(提供数据、代码)...
  8. 今天写了个自定义函数验证身份证号是否符合规则
  9. 中国汽车高级驾驶辅助系统(ADAS)行业十四五规划及投资动态分析报告2022-2028年版
  10. 详解FindBugs的各项检测器