微信公众号借助小程序云函数实现支付功能

本文涉及的产品
.cn 域名,1个 12个月
简介: 微信公众号借助小程序云函数实现支付功能

一、公众号联系云函数

微信开发文档上有实例,可直接复制粘贴,根据内容需要改改就可以了。

这里是实例文档地址:实例传送门

首先需要讲的是,实例流程:

1.微信开发文档前面有说如果公众号联系小程序云函数的话,首先需要配置公众号的后台,需要的配置有:微信公众号后台的js安全域名和网页授权地址,还要在微信开发者工具上配置云函数,小程序的云函数要环境共享给公众号。

文档上都有说到,再次不过多讲述,附上地址:

微信公众号地址:微信公众平台传送门

小程序的配置附上图片引导:

打开微信开发者工具,打开项目,点击云开发:

网络异常,图片无法展示
|

然后点击更多——环境共享:

网络异常,图片无法展示
|

第一次进来的话,是没有关联的公众号的,需要点击添加共享,输入相应的公众号信息,才能关联成功,成功之后就会像我这样的显示出关联的公众号信息:

网络异常,图片无法展示
|

到这里就算是配置完了,其他地方没想到的文档上会有。

2.复制实例后,前面的部分都是引入一些必要的js文件以及初始化HTML的结构。

3.接下来是需要改的地方,实例也标出来了,公众号的appid以及云函数资源方小程序的appid和云函数的云环境id,还有什么样的授权方式,两个选一个,还有回调地址,这个回调地址一定要填上传的服务器地址全称,不是和公众号后台配置的js安全域名和网页授权地址一样的,公众号后台配置的那是域名,这个回调是地址全称。

下面是我的项目信息实例:

网络异常,图片无法展示
|

4.接下来就可以直接打开了,这要注意,如果在电脑上的浏览器打开,显示界面会是这样的:

网络异常,图片无法展示
|

点击登录后就会提示:

网络异常,图片无法展示
|

因为这是PC端,而公众号是微信端的,也就是需要用微信内部浏览器才可以打开,我们可以把VScode文件上传到服务器,然后在把地址发送到手机微信上,直接点击链接在微信内打开,也可以用微信开发者工具的网页调试打开:

网络异常,图片无法展示
|
网络异常,图片无法展示
|

这里要注意,如果公众号后台没有设置你是这个公众号的开发者,你是不可以调试的,有提示,一看就知道去那里配置了,打开之后把上传的服务器地址复制到红框里,刷新就可以了。

5.以第二种授权方式为例,也就是上面第3我改的那里信息登录用snsapi_userinfo,点击登录之后,点击右下角的小齿轮(那是控制台),我们可以看到第一次登录会在10s后跳转申请信息授权页面。等待10s后,页面会跳转到信息授权页面,我们点击同意授权,就登陆了,会跳回到原来这个首页,再点击一下登录,其他的选项就会显示出来了,就像这样:

网络异常,图片无法展示
|

注意:这里一定要注意!!!如果登录报错了,不管报的什么错误,基本只有一种可能,那就是信息填错了,也可能是配置错了,但我感觉不至于,就那两步;一定要注意信息是不是对的,就是改的那里,我写这个项目的时候,因为资源方小程序appid是公司的,而我一直以为用的是我的小程序appid,结果我在这卡了一天,找了n个CSDN文章、看了n遍文档、头发掉了n根、抽了三颗烟,最后才发现是appid填错了,改了之后直接就跑通了。

6.下面的选项就要看公司需要了,就比如我的项目是需要获取用户信息,实现微信支付,还要分享。

实例上有获取用户信息,其中包括用户头像和昵称,还有用户的openid,如下:

网络异常,图片无法展示
|

7.接下来是分享,看了看文档,是有介绍的,和微信小程序的分享差不多。

第二个按钮接口里还有保存图片和获取地理位置两个实例,不需要的话可以return掉。

网络异常,图片无法展示
|

这里是用的第二个按钮接口做的分享,文档上也说了,写在ready里,是用的接口列表也需要在上面填上,必填,否则会报错。分享的正确用法是登陆之后,点一下第二个按钮接口,没有报错就可以,点击右上角的三个点,分享之后就会有自定义的内容了:

网络异常,图片无法展示
|

再此附上地址:微信开放文档 公众号分享传送门

网络异常,图片无法展示
|

8.接下来就是调起微信支付了。

我是又写了个按钮接口来展示微信支付的,接下来,看我的操作:

网络异常,图片无法展示
|
网络异常,图片无法展示
|

其实也就是比着葫芦画瓢,看实例其他的是怎么写的,自己就仿照着写一个就可以了。

我这里是写了按钮接口后,访问了以前写的一个调起微信支付的云函数,给我返回了支付统一下单需要的所有信息,然后紧接着用了公众号调起微信支付的方法。

这里是文档传送门:公众号调微信支付传送门

前端h5页面的源代码:

<html>
<head>
    <title>云开发 Web 能力极简示例</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
    <script>
        window.onerror = e => {
            alert('window error ' + e)
        }
    </script>
    <!-- 调试用的移动端 console -->
    <script src="https://cdn.jsdelivr.net/npm/eruda"></script>
    <script>eruda.init();</script>
    <!-- 公众号 JSSDK -->
    <script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
    <!-- 云开发 Web SDK -->
    <script src="https://res.wx.qq.com/open/js/cloudbase/1.1.0/cloud.js"></script>
    <script>
        // 公众号基本信息:请修改下列字段使用本示例
        const WX_RESOURCE_APPID = 'xxxx' /*         资源方 AppID */
        const WX_RESOURCE_ENVID = 'xxxxx' /*                     资源方云开发环境 ID */
        const WX_OFFICIAL_ACCOUNT_APPID = 'xxxxxxx'/*  公众号 AppID */
        const WX_AUTH_TYPE = 'snsapi_userinfo' /*                     选择需要的授权方式,snsapi_base 或 snsapi_userinfo */
        const WX_REDIRECT_URI = 'http://test1001.wlphp.com/demo04/a.html' /*         回调 URL */
        // 示例配置
        const isPersistLoginInfo = true // 是否需要保留登录态信息到 window 中,只有 snsapi_userinfo 方式需要
        if (window.wx) {
            window.cloud = wx.cloud
        }
        var urlSearch = new URLSearchParams(location.search)
        var accessToken = urlSearch.get('access_token')
        var refreshToken = urlSearch.get('refresh_token')
        /**
         * 检查/发起登录
         * 1. 函数会检查当前是否已登录(checkLogin)
         * 2. 如果未登录,会 10s 后自动发起登录(startLogin)
         * 3. 如果已登录,会初始化实例,使用指定的微信云开发资源
         */
        window.doLogin = async () => {
            try {
                const checkLoginOptions = {
                    provider: 'OfficialAccount',
                    appid: WX_OFFICIAL_ACCOUNT_APPID,
                }
                if (urlSearch.get('oauthredirect') === '1') {
                    checkLoginOptions.accessToken = accessToken
                    checkLoginOptions.refreshToken = refreshToken
                }
                console.log(`checkLogin options: `, checkLoginOptions)
                const result = await cloud.checkLogin(checkLoginOptions)
                console.log(`checkLogin result: `, result)
                if (isPersistLoginInfo) {
                    window.checkLoginRes = result
                }
                if (result.errCode === 0 && result.loggedIn) {
                    console.log(`checkLogin success`)
                    const instance = window.instance = new cloud.Cloud({
                        appid: WX_OFFICIAL_ACCOUNT_APPID,
                        resourceAppid: WX_RESOURCE_APPID,
                        resourceEnv: WX_RESOURCE_ENVID,
                    })
                    const initResult = await instance.init()
                    console.log(`instance inited`, initResult)
                    console.log(`can use cloud instance to access resource now !`)
                    const els = [...document.getElementsByClassName('display-none')]
                    els.forEach(el => el.classList.remove('display-none'))
                } else {
                    console.log(`checkLogin with sdk errCode ${result.errCode} errMsg ${result.errMsg}, will start oauth in 5s`)
                    setTimeout(() => {
                        try {
                            cloud.startLogin({
                                provider: 'OfficialAccount',
                                appid: WX_OFFICIAL_ACCOUNT_APPID,
                                scope: WX_AUTH_TYPE,
                                redirectUri: WX_REDIRECT_URI,
                            })
                        } catch (e) {
                            console.error(`startLogin fail: ${e}`)
                            console.warn(`will start OfficialAccount OAuth login in 5s.`)
                        }
                    }, 5000)
                }
            } catch (e) {
                console.error(e)
            }
        }
        /**
         * 获取用户信息的示例
         * 1. 需在登录后调用
         * 2. 网页授权的方式需为 snsapi_userinfo
         * 3. 从 checkLogin 结果中获取用户信息 cloudID(已在 doLogin 函数中先暂存到全局变量 checkLoginRes)
         * 4. 调用云函数换取 cloudID 信息
         */
        window.getUserInfo = async () => {
            if (!checkLoginRes) throw new Error('获取登录信息失败,请确认授权方式以及是否保存了登录信息')
            try {
                if (checkLoginRes.cloudID) {
                    const res = await instance.callFunction({
                        name: 'echo',
                        data: {
                            userInfoData: new instance.CloudID(checkLoginRes.cloudID),
                        },
                    })
                    const cloudData = res.result.userInfoData
                    if (cloudData.data) {
                        const userInfoImg = document.querySelector('#userinfo .avatar')
                        userInfoImg.src = cloudData.data.avatarUrl
                        const userInfoName = document.querySelector('#userinfo .name')
                        userInfoName.innerText = cloudData.data.nickName
                        const userInfoOpenID = document.querySelector('#userinfo .openid')
                        userInfoOpenID.innerText = cloudData.data.openId
                        const userInfoDiv = document.getElementById('userinfo')
                        userInfoDiv.style.cssText = ''
                    } else {
                        console.warn(`cloudID data error: `, cloudData)
                        alert(`cloudID 信息获取错误,请查看调试器报错信息`)
                    }
                } else {
                    alert(`找不到 cloudID,请确认网页授权方式为 snsnapi_userinfo`)
                }
            } catch (e) {
                console.error(`error: ${e} ${e.stack}`)
            }
        }
        /**
         * 使用 JSSDK 的示例
         * 1. 需在登录后调用
         * 2. 首先会使用云开发 web sdk 提供的 getJSSDKSignature 方法获取网页所需的 wx.config 的签名
         * 3. 调用 wx.config
         * 4. wx.config 成功之后尝试调用选择图片和获取地理位置作为示例
         */
        window.useJSSDK = async () => {
            try {
                const instance = window.instance
                console.log(`url: ${location.href}`)
                const res = await instance.getJSSDKSignature({
                    url: location.href,
                })
                console.log(`jssdk sign res: ${JSON.stringify(res)}`)
                const configOpt = {
                    debug: true, // 开启调试模式,调用的所有 api 的返回值会在客户端 alert 出来,若要查看传入的参数,可以在 pc 端打开,参数信息会通过 log 打出,仅在 pc 端时才会打印。
                    appId: WX_OFFICIAL_ACCOUNT_APPID, // 必填,公众号的唯一标识
                    timestamp: res.timestamp + '', // 必填,生成签名的时间戳
                    nonceStr: res.nonceStr, // 必填,生成签名的随机串
                    signature: res.signature,// 必填,签名
                    jsApiList: ['chooseImage', 'getLocation', 'updateTimelineShareData','updateAppMessageShareData'] // 必填,需要使用的 JS 接口列表
                }
                console.log(`wx.config opt ${JSON.stringify(configOpt)}`)
                wx.config(configOpt)
                console.log(`wx.config executed`)
                wx.ready(() => {
                    console.log(`wx.ready triggered`)
                    //分享朋友
                    wx.updateAppMessageShareData({
                        title: '测试标题', // 分享标题
                        desc: '测试内容', // 分享描述
                        link: 'http://test1001.wlphp.com/demo04/a.html?a=1&b=2', // 分享链接,该链接域名或路径必须与当前页面对应的公众号 JS 安全域名一致
                        imgUrl: 'https://636c-cloud1-8gsu5k8140fd2694-1310350518.tcb.qcloud.la/thanks.png?sign=1f8e7c81d5468f1164e1054654a9b0f0&t=1658483671', // 分享图标
                        success: function () {
                            // 设置成功
                        }
                    })
                    //分享朋友圈
                    wx.updateTimelineShareData({
                        title: '测试标题', // 分享标题
                        link: 'http://test1001.wlphp.com/demo04/a.html?a=1&b=2', // 分享链接,该链接域名或路径必须与当前页面对应的公众号 JS 安全域名一致
                        imgUrl: 'https://636c-cloud1-8gsu5k8140fd2694-1310350518.tcb.qcloud.la/thanks.png?sign=1f8e7c81d5468f1164e1054654a9b0f0&t=1658483671', // 分享图标
                        success: function () {
                            // 设置成功
                            console.log('分享成功')
                        }
                    })
                    return
                    setTimeout(() => {
                        wx.chooseImage({
                            count: 5,
                            sizeType: ['original', 'compressed'],
                            sourceType: ['album', 'camera'],
                            success: function (res) {
                                alert('已选择 ' + res.localIds.length + ' 张图片');
                            },
                            fail: function (err) {
                                console.error(`chooseImage fail ${JSON.stringify(err)}`)
                            },
                        })
                        wx.getLocation({
                            type: 'wgs84', // 默认为wgs84的 gps 坐标,如果要返回直接给 openLocation 用的火星坐标,可传入'gcj02'
                            success: function (res) {
                                var latitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90
                                var longitude = res.longitude; // 经度,浮点数,范围为180 ~ -180。
                                var speed = res.speed; // 速度,以米/每秒计
                                var accuracy = res.accuracy; // 位置精度
                                console.log(`getLocation ${JSON.stringify(res)}`)
                            },
                            fail: err => {
                                console.log(`getLocation fail ${JSON.stringify(err)}`)
                            }
                        });
                    }, 2000)
                })
                wx.error(err => {
                    console.error(`wx.error ${JSON.stringify(err)}`)
                })
            } catch (e) {
                console.error(`error: ${e} ${e.stack}`)
            }
        }
        /**
         * 带登录态访问资源方的云开发资源,调用方式见文档,与小程序一致
         */
        window.accessResource = async () => {
            try {
                const c = window.instance
                await runWithLogs(() => c.database().collection('test').where({}).get(), `start db`, `db res`)
            } catch (e) {
                console.error('logs', `error: ${e}`)
            }
        }
        /**
         * 未登录模式下访问微信云开发的资源示例
         */
        window.accessResourceWithoutAuth = async () => {
            var c = new cloud.Cloud({
                identityless: true, // 表示是未登录模式
                resourceAppid: WX_RESOURCE_APPID,
                resourceEnv: WX_RESOURCE_ENVID,
            })
            await c.init()
            await runWithLogs(() => c.database().collection('test').where({}).get(), `start db`, `db res`)
        }
        window.runWithLogs = async (fn, before, after) => {
            try {
                console.log(before)
                const res = await fn()
                console.log(`${after}: ${JSON.stringify(res)}`)
            } catch (e) {
                console.error(`error: ${e}`)
            }
        }
        window.payment = async () => {
            const orderid = document.querySelector('.input').value
            const money = document.querySelector('.input1').value
            const a = await instance.callFunction({
                name: 'Pay1',
                data: {
                    orderid: orderid,
                    money: money
                }
            })
            const payData = a.result.payment
            WeixinJSBridge.invoke(
                'getBrandWCPayRequest', payData,
                function (res) {
                    if (res.err_msg == "get_brand_wcpay_request:ok") {
                        // 使用以上方式判断前端返回,微信团队郑重提示:
                        //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
                    } else if (res.err_msg == "get_brand_wcpay_request:fail") {
                        console.log("支付失败")
                    } else {
                        console.log("取消支付")
                    }
                }
            )
        }
    </script>
    <style>
        .display-none {
            display: none;
        }
        .userinfo {
            display: flex;
            flex-direction: column;
            align-items: center;
        }
        .userinfo .avatar {
            width: max-content;
            box-shadow: inset 0 -3em 3em rgba(0, 0, 0, 0.1), 0 0 0 2px rgb(255, 255, 255), 0.3em 0.3em 1em rgba(0, 0, 0, 0.3);
        }
        .userinfo .name {
            border-bottom: 2px solid gray;
            padding: 0 5px 5px 5px;
        }
        a {
            list-style: none;
        }
    </style>
</head>
<body>
    <h2>云开发 Web 能力极简示例</h2>
    <p>请注意按照文档说明配置好,主要包括:</p>
    <p>1. 配置好公众号的授权回调域名及 JS 安全域名</p>
    <p>2. 配置好云开发授权关系(小程序授权云开发资源给公众号)</p>
    <p>3. 将代码中相应需要填入小程序/公众号 AppID 的地方进行相应改动</p>
    <p>4. 编写部署好微信云开发对应云环境的 cloudbase_auth 云函数和 echo 云函数</p>
    <p>5. 准备好后,页面加载后先点击登录,登录后再执行访问资源等其他操作,日志可在调试器查看</p>
    <div id="userinfo" class="userinfo" style="display: none">
        <img class="avatar" />
        <p class="name"></p>
        <p class="openid"></p>
    </div>
    <p><a href="javascript:;" onclick="doLogin()">登录(云开发公众号网页授权)</a></p>
    <p><a href="javascript:;" class="display-none" onclick="getUserInfo()">获取用户信息</a></p>
    <p><a href="javascript:;" class="display-none" onclick="useJSSDK()">使用 JSSDK</a></p>
    <p><a href="javascript:;" class="display-none" onclick="accessResource()">访问云资源</a></p>
    <p><a href="javascript:;" class="display-none" onclick="accessResourceWithoutAuth()">未登录模式下访问云资源</a></p>
    <p><input type="text" placeholder="请输入订单号" class="display-none input" name="orderid" value='' /></p>
    <script>
        var date = new Date();
        // 通过以下三种方式获时间戳
        time = date.getTime();
        document.querySelector('.input').value = time
    </script>
    <p><input type="text" placeholder="请输入订单总价" class="display-none input1" name="money" value="1" /></p>
    <p><a href="javascript:;" class="display-none" onclick="payment()">微信支付</a></p>
</body>
</html>
目录
相关文章
|
2月前
|
存储 JSON 小程序
微信小程序入门之新建并认识小程序结构
微信小程序入门之新建并认识小程序结构
53 1
ly~
|
3月前
|
存储 供应链 小程序
除了微信小程序,PHP 还可以用于开发哪些类型的小程序?
除了微信小程序,PHP 还可用于开发多种类型的小程序,包括支付宝小程序、百度智能小程序、抖音小程序、企业内部小程序及行业特定小程序。在电商、生活服务、资讯、工具、娱乐、营销等领域,PHP 能有效管理商品信息、订单处理、支付接口、内容抓取、复杂计算、游戏数据、活动规则等多种业务。同时,在企业内部,PHP 可提升工作效率,实现审批流程、文件共享、生产计划等功能;在医疗和教育等行业,PHP 能管理患者信息、在线问诊、课程资源、成绩查询等重要数据。
ly~
83 6
|
2月前
|
小程序 JavaScript API
微信小程序开发之:保存图片到手机,使用uni-app 开发小程序;还有微信原生保存图片到手机
这篇文章介绍了如何在uni-app和微信小程序中实现将图片保存到用户手机相册的功能。
798 0
微信小程序开发之:保存图片到手机,使用uni-app 开发小程序;还有微信原生保存图片到手机
|
2月前
|
缓存 小程序 UED
如何利用小程序的生命周期函数实现数据的加载和更新?
如何利用小程序的生命周期函数实现数据的加载和更新?
74 4
|
2月前
|
小程序 UED 开发者
小程序的生命周期函数
小程序的生命周期函数
83 1
|
2月前
|
小程序 前端开发 数据可视化
微信商城小程序WeiMall
微信商城小程序WeiMall
37 0
|
3月前
|
小程序 前端开发 API
微信小程序 - 调用微信 API 回调函数内拿不到 this 问题(解决方案)
本文讨论了在微信小程序中调用API回调函数时无法获取到`this`上下文的问题,并提供了解决方案。在回调函数中,使用一个变量(如`that`)来保存当前的`this`引用,然后在回调内部使用这个变量来访问当前页面的数据和方法。
|
4月前
|
移动开发 小程序 前端开发
|
4月前
|
JSON 小程序 JavaScript
超详细微信小程序开发学习笔记,看完你也可以动手做微信小程序项目
这篇文章是一份全面的微信小程序开发学习笔记,涵盖了从小程序介绍、环境搭建、项目创建、开发者工具使用、文件结构、配置文件、模板语法、事件绑定、样式规范、组件使用、自定义组件开发到小程序生命周期管理等多个方面的详细教程和指南。
|
4月前
|
小程序 前端开发
微信小程序商城,微信小程序微店 【毕业设计参考项目】
文章推荐了一个微信小程序商城项目作为毕业设计参考,该项目在Github上获得18.2k星,提供了详细的使用教程和前端页面实现,适合学习微信小程序开发和作为毕业设计项目。
微信小程序商城,微信小程序微店 【毕业设计参考项目】