npm 安装依赖包

npm init -y                              # 初始化package.json文件
npm install express                     # 安装express框架
npm install axios                       # 安装axios远程请求工具包
npm install qs                          # 安装用于字符串转换的工具包qs
npm install mongoose                    # 安装mongodb工具包
npm install xml2js                      # 安装用于转换微信支付返回的xml格式数据为json格式的工具包xml2js
npm install moment                      # 安装用于计算access_token和jsapi_ticket的过期时间的工具包moment
npm install fs                          # 安装用于读取本地文件的工具包fs
npm install cookie-parser               # 安装cookie解析工具包cookie-parser
npm install alipay-sdk                  # 安装支付宝SDK alipay-sdk
npm install crypto                      # 安装微信支付签名工具包crypto,使用MD5算法签名
npm install sha1                        # 安装微信支付JSAPI签名工具包sha1,使用SHA1算法签名
npm install url                         # 安装用于获取支付路径URL拼接的工具包url
npm install ejs                         # 安装ejs模板,生成前端页面

服务器环境配置

  1. 复制本地公钥到远程服务器
adduser web                              # 远程主机添加用户
sudo vim /etc/sudoers                   # 打开sudoers文件
web ALL=(ALL:ALL)  ALL                 # 追加配置内容# 远程服务器配置ssh登录
ssh-copy-id -i ~/ssh/id_rsa.pub web@ip # 复制本地公钥到远程主机
ssh web@ip                             # 远程登录
  1. 服务器软件安装
sudo apt-get update                  # 更新资源
sudo apt-get install mongodb -y         # 安装mongodb数据库
sudo apt-get install nginx -y           # 安装nginx服务器
sudo apt-get install git -y             # 安装git
sudo apt-get install nodejs -y          # 安装nodejs
sudo apt-get install npm -y             # 安装npm
sudo npm i n -g                         # 安装nodejs的模块n,用来安装最新版本的nodejs
sudo n latest                           # 安装最新版本的node
sudo npm i nrm -g                       # 安装nrm模块,用来切换npm安装代码时的源
nrm use taobao                          # 使用taobao镜像
sudo npm i pm2 -g                       # 安装pm2模块
  1. 生成 ssh 密钥
ssh-key-gen                          # 在服务器上生成一个密钥,用于拉取仓库代码
  1. nginx 配置
upstream my_nodejs_upstream {server 127.0.0.1:3001;keepalive 64;
}server {listen 443 ssl;server_name www.my-website.com;ssl_certificate_key /etc/ssl/main.key;ssl_certificate     /etc/ssl/main.crt;location / {proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-Real-IP $remote_addr;proxy_set_header Host $http_host;proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade";proxy_pass http://my_nodejs_upstream/;proxy_redirect off;proxy_read_timeout 240s;}
}
  1. https 证书申请

微信支付的牵涉网络安全问题,因此需要使用 https 协议。https 需要申请证书,可以购买第三方机构的证书。这里使用网上一个免费的证书,证书官网:https://letsencrypt.org/。官方有相关的申请安装说明。这里使用一个开源的脚本库代码实现。

# https://github.com/acmesh-official/acme.sh
curl https://get.acme.sh | sh                   # 安装
source ~/.bashrc                                # 重新加载环境变量# 此处需要注意设置你的域名是能访问到的
# 会在网站项目中创建文件夹.well-known
# http://www.your-app.com/.well-known/
acme.sh --issue -d www.your-app.com -w /home/web/www/pay-app-final/public
# 这步执行完成之后会把申请成功的证书放在~/.acme.sh/目录里
# 将证书安装到网站的路径下
acme.sh --installcert -d www.your-app.com \--keypath       /home/web/www/ssl/app-pay.wechat.com.key  \--fullchainpath /home/web/www/ssl/app-pay.wechat.com.key.pem \--reloadcmd     "sudo service nginx force-reload"
# 生成 dhparam.pem 文件,如果没有此文件,你的网站安全级别会被评为B
openssl dhparam -out /home/web/www/ssl/dhparam.pem 2048# 修改nginx启用SSL
http {# 新增ssl_protocols TLSv1 TLSv1.1 TLSv1.2;ssl_prefer_server_ciphers on;# 兼容其他老浏览器的 ssl_ciphers 设置请访问 https://wiki.mozilla.org/Security/Server_Side_TLSserver {listen 80 default_server;# 新增listen 443 ssl;ssl_certificate         /home/web/www/ssl/app-pay.wechat.com.key.pem;ssl_certificate_key     /home/web/www/ssl/app-pay.wechat.com.key;# ssl_dhparamssl_dhparam             /home/web/www/ssl/dhparam.pem;# 其他省略}
}# 检查nginx的配置并重新启动
sudo service nginx configtest
sudo service nginx restart

该证书有效期是 90 天,需要定期 renew 重新申请,这部分 acme.sh 已经做了,在安装的时候往 crontab 增加了一行每天执行的命令

crontab -l                                       # 查看定时任务
  1. 自动部署脚本
# 写脚本直接远程执行命令
# << deploy
#    ... 这部分内容为远程执行的脚本,根据需要直接编写
#    deploy这个名字是自己起的 叫什么都行
# deploy
ssh web@ip << deploycd /home/web/www/pay-app-finalgit pullnpm ipm2 restart pay-appexit
deploy
  1. 代码远程调试
# 使用ssh的远程代理功能,把对远程服务器的请求映射到本地端口,对授权登录、支付回调等操作做远程调试
# v 显示详细的调试信息
# n 重定向stdin 到 /dev/null
# N 不要求分配shell,有些场景下ssh禁止账号请求shell终端
# t 强制配置 pseudo-tty
# R 远程转发
ssh -vnNtR 3003:localhost:3003 web@ip

完整代码

## .wxconfig.jsconst appid = "";                  // 公众账号id
const appsecret = "";                // 公众账号密钥
const mchid = "";                    // 商户号
const mchKey = "";                   // 商户密钥
const notifyUrl = "";                // 支付成功的回调地址module.exports = {appid,appsecret,mchid,mchKey,notifyUrl
};
## .aliconfig.jsconst aliAppId = "";              // 商户号
const aliNotifyUrl = "";             // 异步通知地址
const aliReturnUrl = "";             // 同步跳转地址
const alipayPublicKey = "";      // 支付宝公钥module.exports = {aliAppId,aliReturnUrl,aliNotifyUrl,alipayPublicKey
};
## app.jsconst express = require("express");
const mongoose = require("mongoose");
const xml2js = require("xml2js");
const fs = require("fs");
const cookieParser = require("cookie-parser");
const AliPaySDK = require("alipay-sdk").default;
const AliPayFormData = require("alipay-sdk/lib/form").default;
const {signPayParams,getJsAPITicket,signParams,appid,fullUrl,getOauthUrl,getOpenId
} = require("./utils/wx");
const {aliAppId,aliNotifyUrl,aliReturnUrl,alipayPublicKey
} = require("./.aliconfig.js");
const Order = require("./models/order");
const app = express();const alipaySdk = new AliPaySDK({appId: aliAppId,privateKey: fs.readFileSync("./.alipay_private_key.pem", "ascii"),alipayPublicKey
});app.use("/", express.static("./public"));app.use(cookieParser());
app.use(express.json());
app.use(express.urlencoded({extended: false})
);app.set("views", "./views");                              // 设置模版文件的路径
app.set("view engine", "ejs");                              // 设置使用的模版引擎app.get("/", (req, res) => {res.send("hello world! 这是一个微信支付的页面");
});app.get("/wx_pay", async(req, res) => {if (req.cookies.openid) {const nonceStr = Date.now().toString();const timestamp = Math.floor(Date.now() / 1000);const jsTicket = await getJsAPITicket();const signResult = signParams({jsapi_ticket: jsTicket,noncestr: nonceStr,timestamp,url: fullUrl(req)});res.render("wx_pay.ejs", {appid,timestamp,nonceStr,signResult,openid: req.cookies.openid});} else {// 重定向到微信授权页面if (req.query.code) {// 此处有code 获取openidconst { openid } = await getOpenId(req.query.code);res.cookie("openid", openid, {path: "/"});res.redirect("/wx_pay");} else {// 重定向到微信授权页面const redirectUrl = fullUrl(req);res.redirect(getOauthUrl(redirectUrl));}}
});app.use("/api/v1/wechats", require("./api/v1/wechats"));// 支付结果通知
app.post("/pay/notify_wx", async(req, res) => {var buf = "";req.on("data", chunk => {buf += chunk;});req.on("end", async() => {const payResult = await xml2js.parseStringPromise(buf, {explicitArray: false});try {if (payResult.xml.return_code == "SUCCESS") {const paramsNeedSign = {};for (let k in payResult.xml) {if (k != "sign") {paramsNeedSign[k] = payResult.xml[k];}}const sign = signPayParams(paramsNeedSign);if (sign == payResult.xml.sign) {const orderNo = payResult.xml.out_trade_no;const order = await Order.findOne({order_no: orderNo});if (order) {order.payed = true;order.payed_time = Date.now();await order.save();}}}res.send(`<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>`);} catch (err) {res.json(err);}});
});app.use("/api/v1/orders", require("./api/v1/orders"));app.get("/ali_pay_m", async(req, res) => {const order = await Order.findOne({order_no: req.query.order_no});if (order) {const formData = new AliPayFormData();formData.addField("notifyUrl", aliNotifyUrl);formData.addField("returnUrl", aliReturnUrl);formData.addField("bizContent", {productCode: "QUICK_WAP_WAY",subject: "node下单",totalAmount: order.fee,outTradeNo: order.order_no,quit_url: "https://" + req.get("host")});let result = "";try {result = await alipaySdk.exec("alipay.trade.wap.pay", {}, {formData});} catch (err) {console.log(err);}res.render("ali_pay_pc.ejs", {result});} else {res.send("订单不存在");}
});app.get("/ali_pay_pc", async(req, res) => {const order = await Order.findOne({order_no: req.query.order_no});if (order) {const formData = new AliPayFormData();formData.addField("notifyUrl", aliNotifyUrl);formData.addField("returnUrl", aliReturnUrl);formData.addField("bizContent", {productCode: "FAST_INSTANT_TRADE_PAY",subject: "node下单",totalAmount: order.fee,outTradeNo: order.order_no});let result = "";try {result = await alipaySdk.exec("alipay.trade.page.pay", {}, {formData});} catch (err) {console.log(err);}res.render("ali_pay_pc.ejs", {result});} else {res.send("订单不存在");}
});// 支付宝同步通知
app.get("/pay_success", async(req, res) => {let order = {};if (alipaySdk.checkNotifySign(req.query)) {const orderNo = req.query.out_trade_no;if (orderNo) {order = await Order.findOne({order_no: orderNo});if (order) {if (!order.payed) {order.payed = true;order.payed_time = Date.now();await order.save();}}}}res.render("pay_success.ejs", {order});
});// 支付宝异步通知
app.post("/pay/notify_ali", async(req, res) => {const isValid = alipaySdk.checkNotifySign(req.body);if (isValid) {const order = await Order.findOne({order_no: req.body.out_trade_no,payed: false});if (order) {order.payed_time = Date.now();order.payed = true;await order.save();}res.send("success");} else {res.send("fail");}
});app.listen(3003, () => {console.log("server is running on 3003");
});mongoose.connect("mongodb://localhost:27017/cat-shop", {useNewUrlParser: true}).then(res => {console.log("数据库连接成功");}).catch(err => {console.log(err);});
## wx.js 对接微信开发平台的代码封装const axios = require("axios").default;
const crypto = require("crypto");
const qs = require("qs");
const xml2js = require("xml2js");
const moment = require("moment");
const sha1 = require("sha1");
const url = require("url");
const WechatToken = require("../models/wechat_token");const {appid,                                      // 公众账号idmchid,                                     // 商户号mchKey,                                       // 商户密钥notifyUrl,                                   // 回调地址appsecret
} = require("../.wxconfig.js");function getOpenId(code) {return axios.get(`https://api.weixin.qq.com/sns/oauth2/access_token?appid=${appid}&secret=${appsecret}&code=${code}&grant_type=authorization_code`).then(result => {return result.data;});
}function getOauthUrl(redirectUrl, scope = "snsapi_base") {redirectUrl = encodeURIComponent(redirectUrl);return `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${redirectUrl}&response_type=code&scope=${scope}&state=123#wechat_redirect`;
}/*** 获取access_token*/
async function getAccessToken() {const accessToken = await WechatToken.findOne({name: "access_token"});let timeDiff = 0;if (accessToken) {timeDiff = moment(Date.now()).diff(moment(accessToken.createdAt),"seconds");if (timeDiff < 6000) {return accessToken.value;} else {await WechatToken.deleteOne({name: "access_token"});}}const result = await axios.get("https://api.weixin.qq.com/cgi-bin/token", {params: {grant_type: "client_credential",appid,secret: appsecret}});const t = new WechatToken({name: "access_token",value: result.data.access_token});await t.save();return result.data.access_token;
}/*** 获取jsApi ticket*/
async function getJsAPITicket() {const jsApiTicket = await WechatToken.findOne({name: "jsapi_ticket"});let timeDiff = 0;if (jsApiTicket) {timeDiff = moment(Date.now()).diff(moment(jsApiTicket.createdAt),"seconds");if (timeDiff < 6000) {return jsApiTicket.value;} else {await WechatToken.deleteOne({name: "jsapi_ticket"});}}const accessToken = await getAccessToken();const result = await axios.get("https://api.weixin.qq.com/cgi-bin/ticket/getticket", {params: {access_token: accessToken,type: "jsapi"}});const t = new WechatToken({name: "jsapi_ticket",value: result.data.ticket});await t.save();return result.data.ticket;
}function fullUrl(req) {// return url.format({//   protocol: req.protocol,//   host: req.get("host"),//   pathname: req.originalUrl// });// 因为开启https的nginx 代理之后,无法获取真实的协议return `https://${req.get("host") + req.originalUrl}`;      // 生成完整的url地址
}/*** jsapi 签名方法* @param {*} params*/
function signParams(params) {const sortedParams = Object.keys(params).sort().reduce((pre, cur) => ({...pre, [cur]: params[cur] }), {});const signResult = sha1(qs.stringify(sortedParams, { encode: false }));return signResult;
}/*** 支付签名* @param {*} params*/
function signPayParams(params) {const sortedParams = Object.keys(params).sort().reduce((pre, cur) => ({...pre, [cur]: params[cur] }), {});sortedParams.key = mchKey;const signResult = crypto.createHash("MD5").update(qs.stringify(sortedParams, { encode: false })).digest("hex").toUpperCase();return signResult;
}async function wxPay(payload, tradeType = "NATIVE") {const { body, orderNo, ip, totalFee, nonceStr, openid } = payload;const paramsNeedSign = {appid,mch_id: mchid,body,nonce_str: nonceStr,spbill_create_ip: ip,notify_url: notifyUrl,total_fee: totalFee,trade_type: tradeType,out_trade_no: orderNo};let strOpendId = "";if (openid) {paramsNeedSign.openid = openid;strOpendId = `<openid>${openid}</openid>`;}const sign = signPayParams(paramsNeedSign);const sendXml = `<xml><appid>${appid}</appid><body>${body}</body><mch_id>${mchid}</mch_id><nonce_str>${nonceStr}</nonce_str><notify_url>${notifyUrl}</notify_url><out_trade_no>${orderNo}</out_trade_no>${strOpendId}<spbill_create_ip>${ip}</spbill_create_ip><total_fee>${totalFee}</total_fee><trade_type>NATIVE</trade_type><sign>${sign}</sign></xml>`;const result = await axios.post("https://api.mch.weixin.qq.com/pay/unifiedorder",sendXml, {headers: {"conten-type": "application/xml"}});// console.log(result.data);return await xml2js.parseStringPromise(result.data, {cdata: true,explicitArray: false});
}// wxPay({//   body: "测试下单",
//   orderNo: "D" + Date.now(),
//   ip: "14.90.188.21",
//   totalFee: 1,
//   nonceStr: Date.now()
// });module.exports = {wxPay,signPayParams,getJsAPITicket,getAccessToken,fullUrl,signParams,appid,getOauthUrl,getOpenId
};
接口调用:
url:/api/v1/wechats/pay
method:post
params:fee               订单金额(必填)openid            用户id(tradeType为JSAPI时必填)tradeType         交易形式(选填,默认为NATIVE扫码支付,当使用公众号支付时传递JSAPI)
return:noncestr: '',       随机字符串prepay_id: '',       支付信息paySign: '',          支付签名codeUrl: '',          支付链接网址,NATIVE支付时用于生成二维码orderNo: '',        订单号
## wechats.js  微信支付api接口的实现const router = require("express").Router();
const Order = require("../../models/order");
const { wxPay } = require("../../utils/wx");router.post("/pay", async (req, res) => {const openid = req.body.openid;const tradeType = req.body.tradeType || "NATIVE";const order = new Order();order.order_no = "D" + Date.now();order.fee = req.body.fee;await order.save();const nonceStr = Date.now();const sign = await wxPay({body: "测试下单",orderNo: order.order_no,ip: "14.90.188.21",totalFee: order.fee * 100,openid,nonceStr},tradeType);if (sign.xml.return_code == "SUCCESS" && sign.xml.result_code == "SUCCESS") {res.json({code: 1,info: {noncestr: nonceStr,prepay_id: sign.xml.prepay_id,paySign: sign.xml.sign,codeUrl: sign.xml.code_url,orderNo: order.order_no}});} else {res.json({code: 0,err: sign});}
});module.exports = router;
## orders.jsconst router = require("express").Router();
const Order = require("../../models/order");router.post("/sub", async (req, res) => {const fee = req.body.fee || 1;const order = new Order();order.order_no = "D" + Date.now();order.fee = fee;await order.save();res.json({code: 1,info: {orderNo: order.order_no}});
});module.exports = router;
## order.jsconst mongoose = require("mongoose");
const Schema = mongoose.Schema;const orderSchema = new Schema({order_no: {type: String,required: true},fee: {type: Number,default: 0},address: {type: String,default: ""},payed: {type: Boolean,default: false},payed_time: {type: Date}},{timestamps: true}
);module.exports = mongoose.model("order", orderSchema);
## wechat_token.jsconst mongoose = require("mongoose");
const Schema = mongoose.Schema;const accessTokenSchema = new Schema({name: {type: String,required: true},value: {type: String}},{timestamps: true}
);module.exports = mongoose.model("wechat_access_token", accessTokenSchema);
## wx_pay.ejs<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>微信支付</title><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.4.1/dist/css/bootstrap.css"/><script src="https://cdn.jsdelivr.net/npm/axios@0.19.2/dist/axios.js"></script><script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script></head><body class="container"><h1 class="text-center">微信支付</h1><div class="form-group"><label>价格</label><input type="text" class="form-control" id="txtPrice" placeholder="请输入价格" /></div><button class="btn btn-primary" onclick="subOrder(event)">下单</button><script>wx.config({debug: true,                           // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。appId: "<%= appid %>",                   // 必填,公众号的唯一标识timestamp: "<%= timestamp %>",            // 必填,生成签名的时间戳nonceStr: "<%= nonceStr %>",          // 必填,生成签名的随机串signature: "<%= signResult %>",       // 必填,签名jsApiList: ["chooseWXPay"]                 // 必填,需要使用的JS接口列表});wx.ready(function() {// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。});wx.error(function(res) {console.log(res);// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。});function subOrder(event) {event.target.disabled = true;const fee = document.getElementById("txtPrice").value;axios.post("/api/v1/wechats/pay", {fee,openid: "<%= openid %>",tradeType: "JSAPI"}).then(res => {//wx.chooseWXPay({timestamp: 0,                  // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符nonceStr: res.data.noncestr,      // 支付签名随机串,不长于 32 位package: res.data.prepay_id,      // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*)signType: "MD5",                    // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'paySign: res.data.paySign,        // 支付签名success: function(res) {// 支付成功后的回调函数alert("此处可以做后续操作!");}});}).catch(err => {console.log(err);});}</script></body>
</html>
## ali_pay_pc.ejs<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>支付宝网页支付</title>
</head><body style="display: none;"><h1>支付宝网页支付</h1><%= result %>
</body></html>
## pay_success.ejs<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>支付宝网页支付</title><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.4.1/dist/css/bootstrap.css" /></head><body class="container"><% if(order.order_no) { %><h1>支付成功</h1><h3>订单号:<%= order.order_no %></h3><p>金额<%= order.fee %></p><p>支付时间<%= order.payed_time %></p><% }else {%><h5>订单不存在</h5><%} %></body>
</html>
## scan_pay.html<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>扫码支付</title><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.4.1/dist/css/bootstrap.css" /><script src="https://cdn.jsdelivr.net/npm/axios@0.19.2/dist/axios.js"></script><style>#qrcode img {display: block;margin: 1rem auto;}</style></head><body class="container"><h1 class="text-center">微信支付</h1><div class="form-group"><label>价格</label><input type="text" class="form-control" id="txtPrice" placeholder="请输入价格" /></div><button class="btn btn-primary" onclick="subOrder(event)">下单</button><hr /><div style="display: none;" id="order"><h3 class="text-center" id="orderPrice"></h3><p>订单号<span id="orderNo"></span></p><div id="qrcode"></div></div><script src="https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.js"></script><script>function subOrder(event) {event.target.disabled = true;const fee = document.getElementById("txtPrice").value;axios.post("/api/v1/wechats/pay", {fee}).then(res => {document.getElementById("order").style.display = "block";document.getElementById("orderPrice").innerText = fee;document.getElementById("orderNo").innerText =res.data.info.orderNo;var qrcode = new QRCode(document.getElementById("qrcode"), {text: res.data.info.codeUrl,width: 128,height: 128,colorDark: "#000000",colorLight: "#ffffff",correctLevel: QRCode.CorrectLevel.H});console.log(res.data);}).catch(err => {console.log(err);});}</script></body>
</html>
## ali_pay.html<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>扫码支付</title><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.4.1/dist/css/bootstrap.css" /><script src="https://cdn.jsdelivr.net/npm/axios@0.19.2/dist/axios.js"></script><style>#qrcode img {display: block;margin: 1rem auto;}</style></head><body class="container"><h1 class="text-center">支付宝支付</h1><div class="form-group"><label>价格</label><input type="text" class="form-control" id="txtPrice" placeholder="请输入价格" /></div><button class="btn btn-primary" onclick="subOrder(event)">下单</button><hr /><div style="display: none;" id="order"><h3 class="text-center" id="orderPrice"></h3><p>订单号<span id="orderNo"></span></p><a class="btn btn-danger btn-block" id="payLink">直接支付</a><div id="qrcode"></div></div><script src="https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.js"></script><script>function subOrder(event) {event.target.disabled = true;const fee = document.getElementById("txtPrice").value;axios.post("/api/v1/orders/sub", {fee}).then(res => {document.getElementById("order").style.display = "block";document.getElementById("orderPrice").innerText = fee;document.getElementById("orderNo").innerText =res.data.info.orderNo;document.querySelector("#payLink").href ="/ali_pay_pc?order_no=" + res.data.info.orderNo;var qrcode = new QRCode(document.getElementById("qrcode"), {text:location.origin +"/ali_pay_m?order_no=" +res.data.info.orderNo,width: 128,height: 128,colorDark: "#000000",colorLight: "#ffffff",correctLevel: QRCode.CorrectLevel.H});// console.log(res.data);}).catch(err => {console.log(err);});}</script></body>
</html>
## .gitignorenode_modules/
.wxconfig.js
.aliconfig.js
.alipay_private_key.pem

项目运行

nodemon app.js
  • 微信公众账号支付:https://xxx.com/wx_pay
  • 微信扫码支付:https://xxx.com/scan_pay.html
  • 支付宝支付:https://xxx.com/ali_pay.html

Node.js 整合 Express 框架实现微信支付和支付宝支付相关推荐

  1. 使用Node.js的Express框架搭建和开发项目

    在搭建项目前,需要安装node.js,npm node.js安装方法 ,npm安装方法 接下来我们来创建express项目 1.选择项目安装目录 假如选择D:\projects\node\blog作为 ...

  2. node.js的express框架用法(一)

    1.通过搜索express 应用生成器工具可以快速创建一个应用的骨架. 通过如下命令在cmd进行全局安装: $ npm install express-generator -g 2.cmd命令输入 e ...

  3. Node.js结合Express框架项目搭建

    一.简述 本次项目是用node.js写后台接口,前端使用vue.js分离的方法实现一个在线点咖啡的项目.本节教程只是一个简单的入门,关于实际用法后期继续更新. 二.搭建应用 1. 通过应用生成器工具 ...

  4. node.js Web应用框架Express入门指南

    node.js Web应用框架Express入门指南 作者: 字体:[增加 减小] 类型:转载 时间:2014-05-28 我要评论 这篇文章主要介绍了node.js Web应用框架Express入门 ...

  5. 关于node.js的web框架的应用及并发性能测试

    "Node.js 是服务器端的 JavaScript 运行环境,它具有无阻塞(non-blocking)和事件驱动(event-driven)等的特色,Node.js 采用 V8 引擎,同样 ...

  6. 使用 Node.js、Express、AngularJS 和 MongoDB 构建一个Web程序

    为什么80%的码农都做不了架构师?>>>    使用 Node.js.Express.AngularJS 和 MongoDB 构建一个实时问卷调查应用程序 2014 年 3 月 20 ...

  7. 转 10 个最佳的 Node.js 的 MVC 框架

    10 个最佳的 Node.js 的 MVC 框架 oschina 发布于: 2014年02月24日 (33评) 分享到:  收藏 +322 Node.js 是一个基于Chrome JavaScript ...

  8. node.js中的框架

    node.js中的框架 载自: http://nodeframework.com/ MVC frameworks Sinatra-like These frameworks offer rich co ...

  9. 10 个最佳的 Node.js 的 MVC 框架

    Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台, 用来方便地搭建快速的, 易于扩展的网络应用· Node.js 借助事件驱动, 非阻塞 I/O 模型变得轻量和高效, ...

  10. Node.js 国产 MVC 框架 ThinkJS 开发 config 篇

    原创:荆秀网 网页即时推送 https://xxuyou.com | 转载请注明出处 链接:https://blog.xxuyou.com/nodejs-thinkjs-study-config/ 本 ...

最新文章

  1. 限制IP 访问 Oracle 的方法
  2. 深入理解signed、unsigned 关键字
  3. 2.10 词嵌入除偏-深度学习第五课《序列模型》-Stanford吴恩达教授
  4. 功能性农业实用技术 谋定·农业大健康-李喜贵:粤黔东西协作
  5. 数据可视化(BI报表的开发)第三天
  6. C++从0到1的入门级教学(十三)——继承
  7. 解密 云HBase 冷热分离技术原理
  8. pycharm在创建py文件时如何自动注释
  9. docker用gpu的参数_ZStack实践汇 | ZStack+Docker支撑GPU业务实践
  10. 如何正确在IDEA 里maven构建的项目中引入lib的jar包(图文详解)
  11. 【搜狗拼音输入法 3.2 论坛版】
  12. mac 批量更改文件后缀名
  13. 打印机出现另存为xps_win10系统打印文件弹出另存为xps/pdf的处理方法
  14. linpack性能测试记录
  15. SSM 前台AJax传递参数和controller后台接收的方法
  16. TDA4 IPC 原理
  17. [No000019A]【波浪理论精典教学课程】
  18. VS2017,MFC对WPS下Excel表格的操作
  19. 关键链项目管理方法的缓冲区管理
  20. ASP.NET项目创建

热门文章

  1. cad计算机绘图基础知识,CAD 计算机绘图基础课件.ppt
  2. 微信公众号迁移公证书办理流程与条件
  3. 主动降噪技术matlab,主动降噪技术(ANC)的前生今世--原理仿真
  4. 后缀–ize_常见词性后缀
  5. Java实现鉴权失败达到一定次数锁定IP并释放到期IP
  6. 微信小程序-腾讯地图报错:鉴权失败,请传入正确的key
  7. VMware workstation环境下opnsense的安装
  8. 最全的搜索引擎入口和分类目录入口
  9. 【Excel】数据透视表—按年、季度、月份汇总报表
  10. c语言的数据类型说明保留字,第三章 数据类型、运算符与表达式