一、需求背景
微信公众号缴费后会跳转到微信自带的订单详情页面,如图所示:
在此订单页面返回会跳到公众号的聊天页面,这不满足支付完成后返回上一页面的场景。调研产生问题的原因,得知是微信支付完成后的回调能力被陆续回收,由点金计划来替代
。
有以下两个场景:
- 服务商没有为特约商户开通点金计划:微信支付完成后,发起支付的H5页面将被直接关闭。
- 服务商为特约商户开通了点金计划:微信支付完成后,将会跳转到微信的点金计划页面,如果服务商没有为特约商户开通商家小票功能,则点金计划页面默认展示官方小票,反之则展示商家小票。
官方点金计划文档(包含商家小票):
https://wx.gtimg.com/pay/download/goldplan/goldplan_product_description_v2.pdf
商家小票是一个自定义的页面,我们可以从这个页面入手,添加一个跳转到目标页面的按钮实现需求。
二、微信支付具体配置
1、服务商需要先自行开通点金计划
操作路径:【登录微信支付服务商平台 → 服务商功能 → 点金计划】,点击申请开通即可
2、 为特约商户打开“点金计划”开关 。 服务商也可通过“默认开通”按钮为全量特约商户开通点金计划。
3、开通商家小票功能。
3.1 配置商家小票链接
服务商维度配置商家小票链接
1) 打开点金计划管理页面 操作路径:【登录微信支付服务商平台 → 服务商功能 → 点金计划】。在“商家小票链接配置” 模块中,点击“添加商家小票链接”:
2) 按照页面操作指引添加一个链接 商家小票链接仅支持 https 协议,同时需通过 ICP 备案的验证,且应保证该链接线上可访问。
3) 下载验证文件,验证链接所有权 将从添加商家小票链接页面下载的验证文件放置在要填写的链接目录下,以证明对该链接的所有 权。放置完毕并确保该路径可访问后,填写链接并点击“提交”,微信支付后台会进行验证文件的 查验,查验通过后即提交成功。 (注:将验证文件放置在所填链接的目录或根目录均可)
特约商户维度配置独立商家小票链接
部分特约商户(例如:医院、学校等类型)有特殊的域名要求,服务商可为这些特约商户配置独立的 商家小票链接。配置后,这些特约商户的点金计划页面将通过所特约商户维度的独立链接进行订单数 据交互。未配置特约商户独立商家小票链接的,将依然使用服务商维度的商家小票链接。
特约商户维度独立的商家小票链接入口如下,具体步骤与上述服务商维度配置相同:
3.2 为特约商户打开“商家小票”开关
3.3 为特约商户打开“点金计划”开关
详情见开发文档。
三、实现代码
根据官方文档的开发指引进行开发。仿写一个官方小票的样式,加一个返回首页按钮
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<meta name="referrer" content="origin">
<meta name="viewport" content="width=device-width, viewport-fit=cover, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<meta name="Cache-Control" content="no-cache, no-store, must-revalidate">
<script type="text/javascript" charset="UTF-8" src="https://wx.gtimg.com/pay_h5/goldplan/js/jgoldplan-1.0.0.js"></script>
<script type="text/javascript" src="https://cdn.bootcss.com/vConsole/3.3.0/vconsole.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.16"></script>
<script src="./js/flexible.js"></script>
<style>
.page-box {
box-sizing: border-box;
/* background-color: #f2f6ff; */
background-color: #fff;
padding: .36rem;
padding-top: 1.32rem;
}
.container {
display: flex;
font-size: .3733rem;
flex-flow: column nowrap;
justify-content: flex-start;
align-items: center;
}
.img-wrapper {
height: 1.7067rem;
width: 1.7067rem;
}
.img-inner {
width: 100%;
height: 100%;
object-fit: fill;
border-radius: 50%;
border: 1px solid #d1d1d1;
}
.list-style {
display: flex;
justify-content: space-between;
align-items: center;
line-height: .96rem;
width: 100%;
}
.title {
color: #666;
}
.detail {
color: #333;
}
.line-style {
height: 1px;
width: 100%;
margin: 5px;
background-color: #d1d1d1;
}
.button-style {
margin-top: .2667rem;
width: 3.2rem;
height: 28px;
border: 1px solid #189fff;
border-radius: 5px;
background-color: #fff;
color: #189fff;
text-align: center;
line-height: 28px;
}
.button-style:hover {
background-color: #189fff;
color: #fff;
}
</style>
</head>
<body>
<div id="app" class="page-box">
<div class="container">
<div class="img-wrapper">
<img class="img-inner" :src="logo">
</div>
<div style="padding: .4267rem 0;">xxxx有限公司</div>
<div class="list-style">
<div class="title">订单状态</div>
<div class="detail"><show-value>{{order.status}}</show-value></div>
</div>
<div class="list-style">
<div class="title">订单尾号</div>
<div class="detail"><show-value>{{order.no}}</show-value></div>
</div>
<div class="line-style"></div>
<div class="list-style">
<div class="title">支付总额</div>
<div class="detail"><show-value>{{order.amount}}</show-value></div>
</div>
<div class="list-style" style="justify-content: center;">
<div class="button-style" @click="goBack">
返回首页
</div>
</div>
</div>
</div>
<script>
Vue.component('show-value', {
template: `<div><slot><div> <div></slot></div>`
})
let vue = new Vue({
el: '#app',
data() {
return {
logo: './imgs/logo.png',
order: {},
url: 'https://payapp.weixin.qq.com'
}
},
created() {
this.getQueryVal()
},
mounted() {
this.showCustomPage()
if (!this.result.out_trade_no) {
parent.alert('没有订单号,请联系管理员!')
} else {
this.queryOrderInfo()
}
},
methods: {
// 获取地址栏字段
getQueryVal() {
// let astr = '?sub_mch_id=1234567&out_trade_no=ABCDEFG&check_code=39f0876dd58e4eb61b66250f69d02705'
// let queryStr = astr.substring(1)
let queryStr = window.location.search.substring(1) // 地址栏字符串
let strArr = queryStr.split('&')
this.result = {}
if (strArr.length) {
strArr.forEach(val => {
let subStrArr = val.split('=')
this.result[subStrArr[0]] = subStrArr[1]
})
}
},
// 用于展示商家小票
showCustomPage() {
let customData = JSON.stringify({ action: 'onIframeReady',displayStyle: 'SHOW_CUSTOM_PAGE', height: 900 })
parent.postMessage(customData, this.url)
},
// 请求订单信息(需要保存订单信息的后台系统提供)
queryOrderInfo() {
axios({
baseURL: 'https://www.xxxx.com/xxxxx/',
method: 'get',
url: 'xxx',
params: {
out_trade_no: this.result.out_trade_no
}
})
.then(res => {
if (res.success) {
this.order = res.result
} else {
parent.alert('出错了,请联系管理员!')
}
})
.catch(err => {
parent.alert('出错了,请联系管理员!')
})
},
// 返回首页
goBack() {
let mchData = { action:'jumpOut', jumpOutUrl: `${location.origin}/xxxxx/user` }
let postData = JSON.stringify(mchData)
parent.postMessage(postData, this.url)
}
}
})
</script>
</body>
</html>
页面效果:
四、注意事项
1、自定义小票页面url会自动携带 “特约商户号(sub_mch_id)”、“商户订单号(out_trade_no)”、“md5 校验码 (check_code)”三个字段的信息,根据这三个字段获取订单信息
2、在商家小票页面渲染完成后,调用父页面的‘’onIframeReady‘’事件的JSAPI,并且从加载商家小票页面开始到调用’onIframeReady‘’事件的JSAPI之间的用时不可超过3s,否则会提示无法获取订单信息。
3、 注意调整 Iframe 框架高度(onIframeReady 事件)。供服务商调整商家小票区域的整体高度(规 则:以宽度 640px 为基准,商家小票高度最小 600px,最大 960px。若服务商通过 jsapi 传入高度小 于 600px,页面会展示 600px,传入大于 960px,页面仅展示 960px)。
4、onIframeReady不能放在created里执行(dom未加载完成),放到mounted中执行。
5、通过系统后台查询订单的接口最后让后端提供一个不需要token等身份信息验证的接口,保证接口传入信息不正确时不报错即可。(微信访问的这个地址不是显式的)。
6、 外跳新页面(jumpOut 事件):支持从点金计划页面上方的商家小票区域,点击外跳到商家的完 整页面(此处不可模拟用户点击自动外跳,否则相关能力会被处罚)。
7、 所有嵌入点金计划页面的商家小票链接(包括页面内可点击跳转的链接),均需在 HTML 的 header 中引入:
<script type="text/javascript" charset="UTF-8" src="https://wx.gtimg.com/pay_h5/goldplan/js/jgoldplan-1.0.0.js"></script>
若未引用,平台会根据《点金计划须知和承诺函》中的相关规定对服务商进行功能处罚。( 平台会对嵌入点金计划页面的商家小票链接(含跳转链接)进 行记录,同时会对商家小票页面内容进行安全监测。)