1.微信外部浏览器h5支付
H5支付是一种移动支付方式,具体指商户在微信客户端外的移动端网页展示商品或服务,用户在前述页面确认使用微信支付时,商户发起服务呼起微信客户端进行支付。
h5支付文档
1. h5支付简介
- 应用场景
- H5支付主要用于触屏版的手机浏览器请求微信支付的场景,方便从外部浏览器唤起微信支付。
使用条件 - 商户需已有H5商城网站,并且该网站已经过ICP备案,才可申请接入H5支付
- H5支付主要用于触屏版的手机浏览器请求微信支付的场景,方便从外部浏览器唤起微信支付。
- 使用条件
- 用户从非微信浏览器的站点导航进入商户H5网页,挑选商品并选择微信支付发起购买。
- 进入微信客户端确认交易并输入支付密码。
- 支付成功后,用户收到支付凭证,商户后台同时收到支付成功的通知。
- 注意事项
- H5支付不建议在APP端使用。如需在APP中使用微信支付,建议接入APP支付。
- 使用H5支付时,需确保网络环境稳定,避免因网络变动导致支付失败
- 时序图
2. h5支付步骤
- 订单生成逻辑:前端根据商品信息配合后端调用接口,后端返回跳转URL。前端判断浏览器类型,如果是微信外浏览器则直接跳转打开微信支付页面。
- 开发前准备:需要开通微信支付商户功能,并在微信支付平台配置相关信息。订单提交后,系统会返回一个跳转URL,前端根据这个URL进行跳转,从而拉起微信支付页面。
- 支付链接的使用:H5支付链接支持多种远程收款方式,包括聚合收款码识别、JS浏览器付款和Nat跳转,用户通过点击链接、输入金额和选择支付方式来完成在线收款
3. h5支付实现
- 判断支付环境
function isWeixin() { var ua = window.navigator.userAgent.toLowerCase(); if (ua.match(/MicroMessenger/i) == "micromessenger") { return true; } else { return false; } }
- 写一个点击事件,调起后来的请求方法。
- 由商户后台向微信支付发起下单请求(调用统一下单接口)注:交易类型trade_type=MWEB
const express = require('express') const tenpay = require('tenpay'); const siteConfig = require('./payConfig.js'); const path = require('path'); const md5 = require('md5'); const app = express() const port = 8001 console.log(md5('template1')) app.use(express.static('public')) app.engine('html', require('express-art-template')); app.set('view options', { debug: process.env.NODE_ENV !== 'production' }); app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'html'); // 引用商户信息 const config = { appid: siteConfig.appid, mchid: siteConfig.mchid, partnerKey: siteConfig.partnerKey, notify_url: 'https://baidu.com/notify', // 买家成功支付后,微信系统通知商户后台系统的url地址 一般在这个里面去更改用户的订单的状态为 已支付 此处是随便写的 spbill_create_ip: '106.52.75.114', // 设备ip }; // 调试模式(传入第二个参数为true, 可在控制台输出数据) const api = new tenpay(config, true); app.get('/h5/buy', async (req, res) => { /* * 后端统一下单需要调用微信支付的 统一下单的api,然后返回一个 mweb_url * 如支付跳转url(参数名“mweb_url”),商户通过mweb_url调起微信支付中间页 * */ let goodsId = Date.now().toString(); let trade_no =md5(Math.random() + goodsId); let result = await api.unifiedOrder({ out_trade_no: trade_no, body: '微信H5端支付测试', total_fee: '1', // 单位是分 1代表1分钱 // openid: '用户openid' // 微信小程序支付或者是微信公众号网页支付的时候要 openid 相当于是买家用户的微信号 // https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_20&index=1 trade_type: 'MWEB', // 使用统一下单API可直接获取code_url, 需自行生成二维码图片 product_id: goodsId, }); console.log(result); res.render('h5', {mweb_url: result.mweb_url}); }) app.listen(port, () => { console.log(`pay app listening on port ${port}`) })
<!DOCTYPE html> <html lang="zh_CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>H5 端扫码微信支付</title> </head> <body> <h2>H5 端微信支付</h2> <p>对于H5支付,调用统一下单接口后,返回的是一个 url地址,只需要买家通过点击这个 a 链接,即可唤起微信支付</p> <form action="http://127.0.0.1:8001/h5/buy" method="get"> <input type="submit" value="微信h5支付"> </form> <a href="{{mweb_url}}">微信支付</a> </body> </html>
点击微信支付
点击立即支付,输入付款密码,支付成功,用户收到支付凭证,同时商户后台收到支付成功的通知。
2. 微信 JS-SDK 调用微信支付
如果要在微信的内部打开网页做支付,则需要使用 js-sdk 进行开发。
js-sdk 可以使得我们开发的网页具有原生 app 调用底层 api 的能力,例如 扫二维码。
- 微信 JS-SDK 是微信官方提供的一个 JavaScript 软件开发工具包,它允许开发者在微信内置浏览器中调用微信的各种原生功能,
- 微信支付。通过微信 JS-SDK 调用微信支付,可以实现用户在微信内完成支付流程,无需跳转到其他应用或页面,从而大大提升用户体验。
- 通过使用微信JS-SDK,网页开发者可借助微信高效地使用拍照、选图、语音、位置等手机系统的能力,同时可以直接使用微信分享、扫一扫、卡券、支付等微信特有的能力,为微信用户提供更优质的网页体验。
时序图
1.前端步骤
-
引入微信 JS-SDK
在 HTML 页面中引入微信 JS-SDK 的脚本文件。通常,你需要从微信的官方服务器下载这个文件,或者通过 CDN 链接引入。
-
配置微信 JS-SDK
在引入 JS-SDK 后,你需要使用 wx.config 方法来配置 SDK。通常包括设置 appId(公众号的唯一标识)、timestamp(生成签名的时间戳)、nonceStr(生成签名的随机串)、signature(签名)等参数。这些参数通常由后端服务器生成,并传递给前端页面。
-
调用微信支付接口
配置完成后,可以使用 wx.chooseWXPay 方法来调用微信支付。wx.chooseWXPay 方法需要传入一个包含支付相关信息的对象,如 appId、timestamp、nonceStr、package(订单详情扩展字符串)、signType(签名方式)和 paySign(签名)等。这些信息同样由后端服务器生成,并基于微信的支付接口进行签名和加密。
-
处理支付结果
支付完成后,微信会返回一个支付结果给前端页面。可以通过监听 wx.chooseWXPay 方法的回调函数来处理这个结果,比如显示支付成功或失败的提示信息,或者根据支付结果进行相应的页面跳转。
-
注意
- 使用微信 JS-SDK 调用微信支付需要你的公众号或小程序已经通过了微信支付的认证,并且你已经获得了相应的支付权限和密钥。此外,为了保障支付安全,所有的支付信息都应该通过后端服务器进行加密和签名,而不是直接在前端页面中进行处理。
- 微信 JS-SDK 的具体使用方法和参数可能会随着微信版本的更新而发生变化
<!DOCTYPE html> <html lang="zh_CN"> <head> <meta charset="UTF-8"> <title>JSSDK开发</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <h2>JSSDK开发</h2> <button id="btn">扫一扫</button> <button id="payBtn">微信内部网页支付</button> <div id="cnt"></div> <div id="payCnt"></div> </body> <script src="https://res2.wx.qq.com/open/js/jweixin-1.6.0.js"></script> <script type="text/javascript"> wx.config({ debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId: '{{sign.appId}}', // 必填,公众号的唯一标识 timestamp: {{sign.timestamp}}, // 必填,生成签名的时间戳 nonceStr: '{{sign.nonceStr}}', // 必填,生成签名的随机串 signature: '{{sign.signature}}',// 必填,签名 jsApiList: ['scanQRCode', 'chooseWXPay'] // 必填,需要使用的JS接口列表 }); wx.ready(function(){ // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。 document.getElementById('btn').onclick = function () { wx.scanQRCode({ needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果, scanType: ["qrCode","barCode"], // 可以指定扫二维码还是一维码,默认二者都有 success: function (res) { var result = res.resultStr; // 当needResult 为 1 时,扫码返回的结果 document.getElementById('cnt').innerText = JSON.stringify(res); } }) } document.getElementById('payBtn').onclick = function () { wx.chooseWXPay({ timestamp: {{result.timeStamp}}, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符 nonceStr: '{{result.nonceStr}}', // 支付签名随机串,不长于 32 位 package: '{{result.package}}', // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*) signType: '{{result.signType}}', // 微信支付V3的传入RSA,微信支付V2的传入格式与V2统一下单的签名格式保持一致 paySign: '{{result.paySign}}', // 支付签名 success: function (res) { // 支付成功后的回调函数 document.getElementById('payCnt').innerText = JSON.stringify(res); } }); } }) </script> </html>
2.后端实现步骤
-
获取微信JSSDK支付参数
获取微信JSSDK支付参数(自动下单, 兼容小程序)let result = await api.getPayParams({ out_trade_no: '商户内部订单号', body: '商品简单描述', total_fee: '订单金额(分)', openid: '付款用户的openid' });
const express = require('express') const tenpay = require('tenpay'); const siteConfig = require('./payConfig.js'); const path = require('path'); const md5 = require('md5'); const axios = require('axios'); const wxJssdkConfigSign = require('wx-jssdk-configsign') const app = express() const port = 8001 console.log(md5('template1')) app.use(express.static('public')) app.engine('html', require('express-art-template')); app.set('view options', { debug: process.env.NODE_ENV !== 'production' }); app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'html'); const config = { appid: siteConfig.appid, mchid: siteConfig.mchid, partnerKey: siteConfig.partnerKey, notify_url: 'https://api.52kfw.cn/notify', // 买家成功支付后,微信系统通知商户后台系统的url地址 一般在这个里面去更改用户的订单的状态为 已支付 spbill_create_ip: '106.52.75.114' }; // 调试模式(传入第二个参数为true, 可在控制台输出数据) const api = new tenpay(config, true); app.get('/jssdk', async (req, res)=>{ /* * https://cli.im/text * 1. jssdk 开发必须先生成前端的 jssdk 的签名信息 * https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#62 * 2. 先要拿到 ticket * 3。 如果要拿到ticket,则需要先拿到 access_token * https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html * * */ let appid = siteConfig.appid; let secret = siteConfig.appsecret; let access_token_url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${secret}`; let response = await axios.get(access_token_url); // console.log(response); let {access_token} = response.data; let ticket_url = `https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=${access_token}&type=jsapi`; let response1 = await axios.get(ticket_url); // console.log(response1.data); let { ticket }= response1.data; let url = 'https://baidu.com/jssdk'; // 回调url, 此处是假数据 let sign = wxJssdkConfigSign({ticket, url, appid}); console.log(sign); let openid = 'o2-ssssssssssssssssssssssssss'; // 购买者的微信openId, 此处是假数据 let goodsId = Date.now().toString(); // 此处是假数据 let trade_no =md5(Math.random() + goodsId); let result = await api.getPayParams({ out_trade_no: trade_no, body: '微信内部的网页支付', total_fee: '1', openid: openid, }); console.log(result, 'result'); res.render('jssdk', {sign, result}); }) /* * 获取用户的openid * */ app.get('/openid', async (req, res)=>{ let appid = siteConfig.appid; let redirect_url = `https://api.52kfw.cn/getopenid`; // 用户点击 获取openid 之后,然后会重定向到我们的 redirect_url 然后携带一个 code let state = md5(Math.random()); res.render('openid', {appid, redirect_url, state}); }) app.listen(port, () => { console.log(`pay app listening on port ${port}`) })