- 公众号运营新功能——官方AI配图,发表内容更方便了
[图片] 公众号发表内容过程中,有运营朋友遇到过图难找,图侵权等问题。 最近微信公众号后台最近推出了一项全新功能——“AI 配图”,为文章编辑过程带来了更多便捷选择。 除了传统的从图片库选择和本地上传图片的方式外,创作者现在可以直接跳转到AI图像生成页面。 只需输入一段文字描述,系统便会在十几秒内生成四张候选图片。 用户可以根据需求调整图片的风格和比例,甚至对生成的图片进行进一步优化,直到达到理想效果后,将其插入文章中。 这项功能为创作者提供了更加灵活、高效的图片编辑方式,极大提升了内容创作的效率与视觉表现力。 个人觉得这一新功能为提高编辑的效率,降低了创作成本与时间,也为以后更多的创作者带来新的灵感与创作思维。 随着版权意识的增强,越来越多的平台与运营者必须要重视这点。 更多公众号运营内容点击下列标题即可观看: 公众号运营:如何使用电子发票结算流量主收益 公众号运营:划线阅读功能到底是什么功能,具体有什么作用 公众号运营:互选广告授权MCN机构一定要多注意。 公众号运营:公众号菜单如何跳转历史文章? 公众号运营:如何文章内跳转公众号主页(不是历史文章页面) 更多公众号运营问题与小技巧可点击我的头像到我社区主页观看。 我是立十,非官方人员💍公众号💍运营资深忠实粉丝,专注回答社区中关于公众号的问题。
12-02 - 电脑版微信打不开所有小程序,怎么解决?
电脑版本微信打不开所有的小程序,这个要怎么处理? 电脑微信已经安装了最新版本的了; 网络也绝对是没有问题的,相同网络手机都是可以打开的; 也按照这里面(https://docs.qq.com/doc/DRmROZXpBVmNxYURY)的操作进行操作,所有都操作了一遍,但还是不行
2023-03-14 - 微信开放平台新服务器证书兼容性验证指引
1. 背景介绍 微信开放平台使用 HTTPS 协议来确保通信的安全性。在开发者调用微信开放平台 API 的过程中,需要在其操作系统或执行环境(如 JRE 等)中部署权威机构的根证书,以验证微信开放平台服务器证书的真实性,进而验证微信服务器及其域名的真实性。 受 Mozilla 信任库的最新根证书信任策略影响(全球所有 CA 的可信根证书需每15年更换一次,超过此时间的可信根将逐渐失去 Mozilla 的信任),DigiCert 将逐步停止使用旧的根证书体系(G1)来颁发 TLS/SSL 证书,转而采用新的根证书体系(G2)。 当前微信开放平台服务器证书的颁发根 CA 是 Digicert Global Root CA (G1),微信开放平台计划于 2024年10月22日 0:00 开始,逐步启用采用 DigiCert Global Root G2 根 CA 颁发的新服务器证书,开发者应仔细阅读本指引,并完成相关验证和修正,以免影响正常服务。 注意: 1)所有通过 api.weixin.qq.com 调用的 API 都可能受影响,开发者需要在 2024年10月22日 0:00 之前参考指引完成相关验证和修正。 2)大多数操作系统和执行环境都默认内置了 DigiCert Global Root G2 根证书,因此大多数系统无需改动即可兼容新服务器证书。但如果开发者的服务器未内置 G2 根证书,或程序代码指定使用旧的 G1 根证书,则会受到影响,将出现服务中断的问题。我们建议及时移除指定旧根证书代码,采用系统自带的信任库进行验证,同时确保系统内置有 DigiCert Global Root G2 根证书。 3)新的 G2 根体系采用更高安全性的 SHA256 签名算法,且依然兼容当前主流的操作系统和移动设备,主流环境兼容情况如下: [图片] 对应的根证书文件请参考: Baltimore CyberTrust Root:Download PEM、Download DER/CET Digicert Global Root CA:Download PEM、 Download DER/CRT Digicert Global Root G2:Download PEM、 Download DER/CRT 2. 开发须知 开发者需确认与微信开放平台对接的服务器系统可兼容新的服务器证书,并参考“验证指引”章节的方法,在 2024年10月22日 0:00 之前完成验证: • 如果验证结果正常,开发者不需要做其它任何事情 • 如果验证结果异常,开发者需要修改相关的系统实现或配置,且需最晚于 2024年10月22日 0:00 之前前完成修正,否则开发者的业务系统与微信开放平台系统之间将不能正常进行 HTTPS 通讯,将影响业务正常运行。 开发者在验证和修正过程中遇到问题,可在小程序专区发帖咨询,我们会有专人进行解答回复。 3. 验证指引 验证方式:绑定 host,请求已部署新证书的微信开放平台服务器 开发者可通过修改操作系统的 hosts 文件来绑定 IP,例如,可在 hosts 文件中新增一行如下内容,实现将域名"api.weixin.qq.com"绑定到新证书环境: 81.69.216.137 api.weixin.qq.com 在 Linux 系统下,hosts文件的完整路径为"/etc/hosts",在Windows系统下,hosts 文件的完整路径为"C:\Windows\System32\drivers\etc\hosts"。 注意: 1、host 环境可以访问的接口与正式环境完全一致,且真实生效,被视为生产正式业务。在验证之前,请充分评估对贵司业务系统以及业务的影响 2、验证完成后,请及时恢复服务器上的 host 配置,微信开放平台服务器证书更新完成后,此处使用的 IP 会被关停。 3、需要用和生产环境相同的操作系统、执行环境、开发语言及程序逻辑进行验证。使用 curl 等命令行工具验证成功,并不代表你的系统支持了新的服务器证书。 4、逐一验证所有正在调用的微信开放平台接口,如果均能正常使用,说明系统支持微信开放平台新的服务器证书。反之,则需要根据修正指引排查问题并修正。 4. 修正指引 大部分情况下,验证失败是以下两种情况导致的: 情况一:程序中指定了根证书,但是指定的根证书中仅包含了旧的 G1 根证书。 情况二:服务器上没有部署新的 G2 根证书。 可通过以下两个方案修正: 方案一:删除指定根证书的代码。当程序中不指定根证书时,会使用系统自带的根证书。绝大部分系统中已内置了新的 G2 根证书,所以删除指定根证书的代码,不会影响到你的现有业务。 方案二:更新根证书。往 truststore 或者根证书信任文件中追加新的 G2 根证书(注意:追加新的根证书时,需要保留老的G1根证书)。 推荐使用“方案一”来修正,该方案的兼容性更好。原因是:新的 G2 根证书将于 2029年4月15日不再被 Mozilla 信任,到时候同样要更换新的根证书。使用方案一修复,你的系统中很可能已内置了后续需更换使用的根证书,从而不受影响。如果使用方案二进行修复,在 2029年4月15日仍可能需安装新的根证书。 下面介绍各种开发语言可能碰到的问题及解决方法: JAVA常见错误 javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target 可能原因: 代码中设置了 javax.net.ssl.trustStore 。 可以在代码中搜索关键字“trustStore” 或者 “TrustManager” 来确认 解决方法: 方法一、删除掉指定[代码]javax.net.ssl.trustStore[代码]的代码 方法二、安装新的根证书, 往指定的trustStore 中添加新的根证书。操作命令:keytool -importcert -keystore cacerts -storepass changeit -noprompt -file ./DigiCertGlobalRootG2.crt -alias digicertglobalrootg2 C++常见错误 cURL error 60: SSL certificate: unable to get local issuer certificate. CURLE_SSL_CACERT (60) peer certificate cannot be authenticated with known CA certificates. 可能原因: 代码中设置了[代码]CURLOPT_CAINFO[代码]。 可以在代码中搜索关键字“CURLOPT_CAINFO”来确认 解决方法: 方法一、删除掉[代码]curl_easy_setopt(pCurl,CURLOPT_CAINFO,"./rootca.pem")[代码]相关的代码 方法二、更新rootca.pem。用libcurl官网最新的 https://curl.haxx.se/ca/cacert.pem 替换即可 PHP常见错误 cURL error 60: SSL certificate: unable to get local issuer certificate. CURLE_SSL_CACERT (60) peer certificate cannot be authenticated with known CA certificates. 可能原因: 使用libcurl时设置了[代码]CURLOPT_CAINFO[代码]。可以在代码中搜索关键字“CURLOPT_CAINFO”来确认。 解决方法: 方法一、删除掉类似[代码]curl_setopt(pCurl, CURLOPT_CAINFO, "./rootca.pem");[代码]的代码 方法二、更新rootca.pem。用libcurl官网最新的https://curl.haxx.se/ca/cacert.pem 替换即可 C#常见错误 RemoteCertificateValidationCallback 设置的回调函数返回 false 可能原因: 操作系统中没有新的根CA 解决方法: 安装新的根证书 Python常见错误 SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:581) 可能原因: 代码中用 verify 指定了根证书文件。 可以在代码中搜索关键字“verify”来确认。类似的代码如: [代码]response = requests.post( "https://api.mch.weixin.qq.com/secapi/pay/refund", verify="./rootca.pem", …… ); [代码] 解决方法: 方法一、删除verify参数 方法二、更新[代码]rootca.pem[代码]。用libcurl官网最新的https://curl.haxx.se/ca/cacert.pem 替换即可 go常见错误 x509: certificate signed by unknown authority . 可能原因: 发起https时, 用 RootCAs指定了根证书文件。 可以在代码中搜索关键字 “RootCAs” 来确认。类似的代码如: [代码]roots.AppendCertsFromPEM([]byte(rootPEM)) tr := &http.Transport{ TLSClientConfig: &tls.Config{RootCAs: roots}, } client := &http.Client{Transport: tr} [代码] 解决方法: 方法一、删除配置项RootCAs 方法二、更新[代码]rootca.pem[代码]。用libcurl官网最新的https://curl.haxx.se/ca/cacert.pem 替换即可 Ruby常见错误 SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed 可能原因: 发起https时, 用[代码]ssl_ca_file[代码]或者[代码]cert_store[代码]指定了根证书文件。 可以在代码中搜索关键字“ssl_ca_file” 或者 “cert_store” 来确认。类似的代码如: [代码] "https://api.mch.weixin.qq.com/secapi/pay/refund", :ssl_client_cert => OpenSSL::X509::Certificate.new(File.read("./apiclient_cert.pem ")), :ssl_client_key => OpenSSL::PKey::RSA.new(File.read("./apiclient_key.pem ")), :ssl_ca_file => "./rootca.pem", :verify_ssl=>OpenSSL::SSL::VERIFY_PEER).post(data); [代码] 或者 [代码]sock.cert_store.add_file('/usr/lib/ssl/certs/weixin/rootca.pem') [代码] 解决方法: 方法一、删除 [代码]ssl_ca_file[代码]及 cert_store 指定的参数 方法二、更新rootca.pem。用libcurl官网最新的https://curl.haxx.se/ca/cacert.pem 替换即可 NodeJs常见错误 Caught exception: Error: CERT_UNTRUSTED 可能原因: 代码中用 ca 指定了根证书文件。 可以在代码中搜索关键字“ca” 或者 “rootca” 来确认。类似的代码如: [代码] hostname: api.mch.weixin.qq.com ', pfx: await readFile('./apiclient_cert.p12'), ca: await readFile('./rootca.pem'), }; const req = https.request(options, (res) => { …… [代码] 解决方法: 方法一、删除ca指定的参数 方法二、更新[代码]rootca.pem[代码]。用libcurl官网最新的https://curl.haxx.se/ca/cacert.pem 替换即可 其它语言请参考对应的TLS/SSL库相关手册。 5. 常见问题 Q1:什么是服务器证书? A:服务器证书通常又叫“SSL 证书”、“HTTPS 证书”,它由权威 CA 机构颁发,用于对网站进行身份鉴定,并使客户端与网站之间通过 TLS/SSL 协议建立起安全传输通道。微信开放平台的服务器证书是由权威机构 DigiCert 颁发,开发者调用微信开放平台 API 时,能通过该证书来认证微信服务器身份。 Q2:什么是根证书? A:根证书是权威 CA 机构给自己颁发的证书,是信任链的起始点,是证书颁发机构(CA)与用户建立信任关系的基础。操作系统、浏览器、TLS/SSL 开发库通常随软件发行包预置其信任的权威机构的根证书。 Q3:微信开放平台为什么要更换服务器证书? A:根证书存在有效期,根证书到期后无法继续被用来校验服务器证书。 因此,微信开放平台向权威机构(CA)申请了新的服务器证书。避免在老的根证书过期后,开发者调用微信开放平台 API 时出现问题。 Q4:微信开放平台更换服务器证书,需要开发者配合做哪些事情? 一、验证你的系统是否兼容微信开放平台新的服务器证书。 二、若支持的话,不需要做任何操作。若不支持,需要参考修正指引排查原因并修正。 注意: a)开发者按指引进行验证或安装新的根证书, 不会产生任何费用。 b)开发者正在使用的其它 HTTPS 证书不需要更换或者升级。 Q5:开发者如何核实服务器上是否已内置了G2根证书? A: 以 Linux 系统为例。Linux 系统信任根证书库的保存位置因发行版本不同而有所差异:Debian/Ubuntu:/etc/ssl/certs/ca-certificates.crt CentOS/RHEL/Fedora/TencentOS:/etc/pki/tls/certs/ca-bundle.crt 开发者可以使用以下命令来核实系统是否已内置了G2根证书 [代码]grep "DigiCert Global Root" /etc/pki/tls/certs/ca-bundle.crt # DigiCert Global Root CA # DigiCert Global Root G2 [代码] 备注:示例显示系统已内置了G1和G2根证书 Q6:微信开放平台更换证书前,可以提前安装根 CA 证书吗? A: 必须提前安装,且建议在 2024年10月22日 0:00 之前提前安装。因为从 2024年10月22日 0:00开始,微信开放平台会在生产环境逐步灰度启用新服务器证书,以校验开发者的系统是否兼容新证书。微信开放平台在更换新证书的过程中,会同时使用新老两个服务器证书,开发者要能正常处理这种情况。因此,开发者的服务器上需要包含新老两个根证书。 Q7:开发者需在什么时候完成验证和修正?不验证会有什么影响? A: 开发者应在 2024年10月22日 0:00 之前完成验证和修正。如果开发者侧系统的实现方式已能兼容新的服务器证书,那么不会影响业务;如果开发者侧系统的实现方式不能兼容新的服务器证书,那么会导致开发者的系统与微信开放平台的系统不能正常交互,影响业务。 Q8:如果开发者无法按时完成兼容性修正,在微信开放平台灰度新证书时遇到异常应如何处理? A: 如果开发者的系统经验证是不能兼容新证书的,开发者应在 2024年10月22日 0:00 之前完成修正。如果无法按时完成修正,我们届时可提供仍使用旧证书的临时环境,开发者可通过 host 绑定 IP 的方式使用,以临时规避证书更换产生的影响。请务必在2024年10月22日 0:00 之前完成系统修正,并取消绑定临时环境IP,恢复以正常的方式对接微信开放平台的生产正式环境。 6. 特别提醒 1)为提前发现开发者“是否会受到证书更换的影响?”“有没有升级完根证书?”,微信开放平台将于 2024年10月22日 0:00 开始,把开发者的部分请求转发到部署了新证书的服务器上,并逐步提升灰度比例。当开发者不支持新的服务器证书时,会出现 SSL 相关的错误,通常情况下重试即可恢复。 2)根证书升级仅跟服务器和应用程序相关,与 APPID 没关系。只要在同一台服务器升级完成后,该服务器上使用的所有 APPID 都可以正常使用的。 3)如果你是通过代理服务器向微信开放平台发起请求,那么需要修改代理服务器的配置。 7. 联系我们 如果开发者在验证或者修正问题的过程中遇到困难,可在小程序专区发帖咨询,我们会有专人进行解答回复。 8. 附录 DigiCert 原文公告 :DigiCert root and intermediate CA certificate updates 2023
08-06 - crypto-js npm 包的使用问题?
npm install crypto-js --production 构建npm [图片] 使用: import hmacSHA512 from 'crypto-js/hmac-sha512';但是crypto-js目录只有一个index文件,所以只能用 var CryptoJS = require("crypto-js"); console.log(CryptoJS.HmacSHA512("Message", "Key")); 结果: [图片] [图片] 请告诉我解决办法
2019-09-26 - 小程序请求数据双向混合加密和防篡改+防重放攻击的实现
前言 大家好,借着中秋放假明日又要上班的这个晚上,平常又没空,趁这个时间点就决定来一篇。 [图片] 我们都知道微信小程序的服务端API上,官方可谓是做足了心思,对用户的数据进行加密,虽然对我们开发者来说似乎是一种麻烦,但是从长远角度来看,是十分有必要的,用户隐私高于一切。 那么在小程序的开发过程中与后端对接接口时是否有想过这样的问题呢? HTTPS真的百分百安全吗? 数据被成功抓包后安全吗? 数据被篡改后还有效吗? 请求被重放后安全吗? 世界上没有绝对安全的系统,但我们可以让它被破解的成本变高。 本篇文章专业性并不高,如果存在错误请大家为我指出来,以免误导别人,谢谢! 以下我们将小程序端称为C端,服务端称为S端,服务端代码是Node.js,仅供参考,但原理都一样,后端可以是其它语言。 思考 围绕着以上的问题,探究一下问题的答案? 本部分只对问题做思考,具体实现请参考下面的实现部分。 HTTPS真的百分百安全吗?只能说是相对安全,当然,在微信小程序的沙箱环境里,HTTPS通信会更加安全,否则官方可能会要求我们对请求加密了对吧。但百密一疏,C端是否存在漏洞,假设C端安全了难道S端就安全嘛?细思恐极,退一万步讲,百分百安全是不存在的,由于篇幅问题相关漏洞大家可以搜索探究。 假设数据被中间人成功抓包,如果数据是明文传输,那么将导致数据泄露,因此对数据进行加密是必要的,但应该如何加密呢?如何做到密钥安全?C端和S端如何进行数据的加解密?RSA和AES加密应该使用哪种加密明文?如何充分发挥RSA和AES两个加密算法的特长? 假设数据被篡改,如果因为请求数据被篡改而导致严重后果,那么很大程度上其实是代码设计有问题,正常设计中安全应被摆在重要的位置,至少不应该出现购买某样商品时是通过C端发送商品价格给S端调起支付那样(只需篡改商品价格即可支付极少的钱购买商品),如果代码设计并不存在严重问题,那么数据被篡改也是不可忽视的,我们需要进行数据签名,让不同的数据拥有唯一的MD5哈希值,如果数据被篡改,通过哈希值即可判断数据是否被篡改,当然也可能会有人问,既然数据被篡改,那攻击者会不会重新生成一个哈希值代替?是的。但是我们有接下来会说到的key,key会作为签名的数据变量之一,由于攻击者并不知道key值因此无法重新签名,key值建议不是固定值而是周期性更换的随机值,例如随着用户的登录态而产生并随之抹除。 假设请求被重放,大部分时候由于数据被加密和防篡改处理过,攻击者并无法直接获得数据或篡改,但如果通过劫持到的登录认证请求的原始数据并重新发起该请求,则攻击者将可能获得重要的认证数据,使得系统将攻击者作为正常用户处理,后续的请求攻击者仍能伪装成正常的用户进行后续攻击。 期望效果 在开始实现之前先看一下完成后的效果,以下是截取了Network中其中一个GET请求发送的数据: [图片] 实际上发送的数据是如下图所示: [图片] 然后是该请求返回的数据: [图片] 实际上返回的数据是如下图所示: [图片] 整个流程: [图片] 实现 由于整个流程在C端的实现上顺序反过来的,因此下面的步骤也将是反向而行。 工具库下载 工欲善其事,必先利其器!这三个库是经过修改压缩的,支持在小程序上使用并且体积可观(总共69.1KB,如果不涉及密码哈希处理只需要前两个库,体积只有65.3KB),接下来的实现操作将会使用到,建议大家可以根据实际情况对功能进行二次封装: CryptoJS.js:点击下载 RSA.js:点击下载 SHA256.js(可选):点击下载 在线的各类加解密工具(可选,可以收藏起来,平时测试挺有用):点击访问 请求数据防重放 要防范请求重放攻击,首先需要了解Unix时间戳 timestamp概念,和时间戳不一样的是它的单位是秒,事实上这个需求也只需要秒级即可。除此之外还将用到另一个值:nonce,它是一个随机产生并只能被使用一次的值,长度自定,请求越频繁长度需要越长(降低同一时间产生相同nonce的几率),C端发送请求时需要将timestamp和生成的nonce加入发送的参数中。那么,如何将两者结合呢?我这为防重放的目标下了一个简单的定义。 同样的请求只能发生一次,且请求必须在规定时间内发出。 何为同样的请求?不是指两次发送的参数一致就是一样的,而是连timestamp和nonce也一样才算是同样的请求。 那么S端如何确认其是同样的请求呢? S端每次接收到一个请求,都会将该请求的nonce存入缓存并保持60秒(这个阈值不一定是60秒,可以根据实际需要定义),时间过后该值将被移除,建议S端采用Redis存储nonce,这样可省去检测和移除nonce的代码。如果S端发现当前请求的nonce存在于已存储的nonce之中,则此请求发生重复,那么timestamp有何用? 如果只使用nonce我们只能保证该请求60秒内不会重复,但60秒后依然任人宰割,这不是要的结果。所以timestamp将用来限制时间,S端时间戳减去C端发送请求的时间戳,得到的差值为N秒,如果N秒大于60秒则此请求过期,那么则可以保证,60秒内因为nonce相同而被判为请求重放,60秒后因为时间差超过而被判为请求已过期,因此确保了请求不会被重放。。 以下展示三种情况: [代码]C端时间戳:1568487720 //2019/9/15 03:02:00 C端NONCE:5rKbMs2Fm3 C端发送请求 -> S端接收请求 S端时间戳:1568487722 //2019/9/15 03:02:02 CS端时间差:1568487722 - 1568487720 = 2 C端NONCE是否存在于缓存:false 【重放校验通过】 [代码] [代码]C端时间戳:1568487722 //2019/9/15 03:05:22 C端NONCE:IzFEs52bAC C端发送请求 -> S端接收请求 S端时间戳:1568487922 //2019/9/15 03:02:00 CS端时间差:1568487922 - 1568487722 = 200 C端NONCE是否存在于缓存:false 【重放校验不通过,请求已过期,因为时间差超过60秒】 [代码] [代码]C端时间戳:1568487720 //2019/9/15 03:02:00 C端NONCE:IxwPHQU0nA C端发送请求 -> S端接收请求 S端时间戳:1568487722 //2019/9/15 03:02:02 CS端时间差:1568487722 - 1568487720 = 2 C端NONCE是否存在于缓存:true 【重放校验不通过,此请求为重放请求,因为nonce已经存在,此请求已经完成,不可重复】 [代码] timestamp和nonce将作为参数参与下面部分的签名。 请求数据防篡改 C端数据签名 首先通过对参数按照参数名进行字典排序(调过一些第三方API的朋友应该明白),假设当前需要传输的参数如下: [代码]{ "c": 123, "b": 456, "a": 789, "timestamp": 1568487720, "nonce": "5rKbMs2Fm3" } [代码] 进行字典排序,参数名顺序应为: [代码]const keys = Object.keys(data); //获得参数名数组 keys.sort(); //字典排序 console.log(key); //["a", "b", "c", "nonce", "timestamp"]; [代码] 参数字典排序后应和参数一起拼接为字符串,至于使用什么拼接符就要与S端商量了,如果参数值是一个数组或一个对象(如c为[1,2,3])那么可以将数据值转为JSON字符串再拼接。以上参数拼接后字符串如下: [代码]a=789&b=456&c=123&nonce=5rKbMs2Fm3×tamp=1568487720 [代码] 下一步是计算拼接字符串的MD5哈希值了嘛? 不是。因为这样拼接的字符串很容易被攻击者伪造签名并篡改数据,这样就失去了签名的意义了。也就是说缺了一个key值。key值又从何而来?上面思考部分有提到建议从登录后发放,并且这个key与该用户的登录态绑定,登录态有效期间,将使用这个key进行请求的签名与验签,至于如何鉴别用户,我们会在最终发送给S端的参数加入一个sessionId作为登录态唯一标识,这里不加是因为这部分数据是需要参与后续的加密的,而sessionId不参与加密。 但是可能又会有一个问题,登录前没有key怎么实现的登录请求?事实上,登录请求并不怕篡改,因为攻击者自己也不知道账号密码,所以无需提供key用于登录请求的签名。 提到登录密码这个需要注意一点,密码不能明文传输,请计算哈希值后传输,S端比对账户密码哈希值即可确认是否正确,同样S端非特殊情况也不能明文存储密码,建议SHA-1或更高级的SHA-256计算后的值,MD5值可能被使用彩虹表(一种为各种常见密码建立的MD5映射表)破解。SHA256计算的库已在上面工具库下载提供。 拼接上我们登录时随机生成的key: [代码]a=789&b=456&c=123&nonce=5rKbMs2Fm3×tamp=1568487720&key=gUelv79KTcFaCkVB [代码] 接下来计算32位MD5哈希值 为什么上面说MD5会被破解而这里却用MD5计算?因为此处计算MD5的目的并不是为了隐藏明文数据,而只是用于数据校验 此处引入了CryptoJS.js [代码]const CryptoJS = require('./CryptoJS'); const signStr = "a=789&b=456&c=123&nonce=5rKbMs2Fm3×tamp=1568487720&key=gUelv79KTcFaCkVB"; const sign = CryptoJS.MD5(signStr).toString(); //a42af0962de99e698d27030c5c9d3b0e [代码] 这么一来我们的数据签名阶段就完成了,然后需要把签名加入参数之中,将和参数一起传输,需要注意的是传输参数无需在意参数名排序。以下是当前的参数处理结果: [代码]{ "c": 123, "b": 456, "a": 789, "timestamp": 1568487720, "nonce": "5rKbMs2Fm3", "sign": "a42af0962de99e698d27030c5c9d3b0e" } [代码] 其实从上面这两部分内容来看,会发现防重放和防篡改是相辅相成的,就像两兄弟一样,少了谁都干不好这件事。 S端验证签名 既然有签名那必定也有验签,验签流程其实就是重复C端的前面流程并比对CS两端得出的签名值是否一致。S端取得请求数据后(假设数据未加密,暂时不讨论解密),将除了sign之外的参数名进行字典排序,sign用一个临时变量存下,然后排好序的参数和C端一样拼接得到字符串。 接下来根据上面部分提到的未加密参数sessionId获得用户登录态并获取到该状态的临时key拼接到字符串末尾,接下来进行MD5计算即可得到S端方获得的签名,此时与请求中携带的sign比较是否一致则可确定签名是否有效,如果不一致返回签名错误。具体流程请参考以下: [代码]C端发送请求 -> S端接收请求 //请求参数为data const _sign = data.sign; //排序并拼接除sign外的参数 let signStr = a=789&b=456&c=123&nonce=5rKbMs2Fm3×tamp=1568487720 const sessionId = ...; //用户的sessionId const sessionData = getUserSessionData(sessionId);//根据sessionId查询用户登录态sessionData并取出key,并拼接到字符串尾部 const key = sessionData.key signStr += key; //MD5计算并比对,代码仅供参考 const sign = crypto.md5(signStr); if(sign !== _sign) { //签名错误! } [代码] 请求数据加解密 有了上面部分的参数处理铺垫后,接下来就该开始本文章最核心的加解密,在这之前我们先了解AES和RSA两种加密算法。了解概念后我们再来思考一下两者的一些特性。 AES加解密需要密钥,除某些模式外还需要提供初始化向量,可使用密钥解出明文,是对称加密算法。 RSA加解密需要一对密钥,分别为公钥和私钥,公钥加密,私钥解密,是非对称加密算法。 两者在加密长文本性能上AES占优势。 根据这些让我们发现,他们可以形成互补关系,RSA加解密安全性高但长文本处理性能不及AES。AES加解密长文本性能优于RSA但需要明文密钥和向量加解密,密钥的安全性成问题。 那么在C端生成随机的AES密钥和向量,使用密钥和向量使用aes-128-cbc加密模式(也可以根据实际需要采用其它的模式)加密真正需要传输的参数(参数则是经过防重放+防篡改处理的参数)得到encryptedData,然后将该密钥使用RSA公钥加密得到encryptedKey,下面我顺便把AES加密的向量也一起加密了得到encryptedIV,这样就完美的互补了对方的缺点,既能够较快的完成数据加密又能保证密钥安全性,两全其美。 整个个加解密流程如下图所示: [图片] 下面的四个小章节将逐一描述流程实现: C端加密数据 C端加密后的数据应如下(并非固定格式,根据自己需要定制): [代码]{ "sessionId": "xxxxxxxx", "encryptedData": "xxxxxx", "encryptedKey": "xxxxxx", "encryptedIV": "xxxxxxx" } [代码] 但RSA公钥又是怎么发放到C端的呢?答案是在登录认证的时候服务器下发的,登录成功时服务器会创建RSA密钥对并把公钥发放给C端,私钥存在服务器上该用户的登录态数据中。流程如下所示: [代码]C端发起登录请求 -> S端接收登录请求 S端登录认证是否通过 true S端生成RSA密钥对 - publicKey , privateKey S端查询相关用户信息 S端生成登录态信息 -> 向登录态信息存入privateKey私钥,登录态信息类似如下 "xxxxxx": { id: "xxxxx", authData: { key: "xxxxxx", privateKey: "xxxxxx" } } S端返回登录态唯一标识sessionId和publicKey公钥以及相关用户信息 -> C端 C端存储登录态信息于本地,后续请求将使用服务器提供的公钥进行加密 [代码] 其中privateKey就是该用户当前登录态所使用的解密私钥,C端通过公钥加密后的AES密钥数据只能用该私钥解密。 如果希望登录阶段的请求也加密,那么可以手动生成一个RSA密钥对,然后客户端放置一个固定的公钥,服务器也使用一个固定的私钥进行登录阶段的加解密。 具体实现如下: 此处引入了CryptoJS.js和RSA.js [代码]const CryptoJS = require('./CryptoJS'); const RSA = require('./RSA.js'); //假设当前已登录成功并获得S端下发的RSA公钥且已存入本地存储 //createRandomStr为生成随机大小写英文和数字的字符串 const aesKey = createRandomStr(16); //生成AES128位密钥 16字节=128位 const aesIV = createRandomStr(16); //生成初始化向量IV const raw = JSON.stringfly({ "c": 123, "b": 456, "a": 789, "timestamp": 1568487720, "nonce": "5rKbMs2Fm3", "sign": "a42af0962de99e698d27030c5c9d3b0e" }); const encryptedData = CryptoJS.AES.encrypt(data: raw, key: aesKey, { iv: aesIV, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); //使用CBC模式和Pkcs7填充加密 const authData = wx.getStorageSync("authData"); //读取本地存的RSA公钥 RSA.setPublicKey(authData.publicKey); //设置RSA公钥 const encryptedKey = RSA.encrypt(aesKey); //RSA加密AES加密密钥 const encryptedIV = RSA.encrypt(aesIV); //RSA加密AES加密初始化向量,是否加密向量可由自己决定 //最后的处理结果 const result = { sessionId: authData.sessionId, encryptedData, encryptedKey, encryptedIV }; [代码] S端解密数据 [代码]const crypto = require('crypto'); const cryptojs = require('crypto-js'); const { sessionId, encryptedData, encryptedKey, encryptedIV } = requestData; const authData = getUserSessionData(sessionId); //获取用户登录态数据 const privateKey = authData.privateKey; const aesKey = crypto.privateDecrypt({ key: privateKey, padding: crypto.constants.RSA_PKCS1_PADDING }, encryptedKey); //使用私钥解密得到AES密钥 const aesIV = crypto.privateDecrypt({ key: privateKey, padding: crypto.constants.RSA_PKCS1_PADDING }, encryptedIV); //使用私钥解密得到AES iv向量 const key = cryptojs.enc.Base64.parse(aesKey); const iv = cryptojs.enc.Utf8.parse(aesIV); const decryptedData = cryptojs.AES.decrypt(encryptedData, key, { iv, mode: cryptojs.mode.CBC, padding: cryptojs.pad.Pkcs7 }); //采用与加密统一的模式和填充进行解密 const { "c": 123, "b": 456, "a": 789, "timestamp": 1568487720, "nonce": "5rKbMs2Fm3", "sign": "a42af0962de99e698d27030c5c9d3b0e" } = decryptedData; //至此解密得到C端传来的数据 [代码] S端加密数据 当S端处理完C端的请求后应加密响应数据,那么加密响应数据应该使用什么密钥呢?既然C端已经将加密的密钥发送过来了,那么干脆将C端使用的AES密钥拿来加密响应数据就可以了。加密的数据传回C端后,C端只需使用该请求所使用的AES加密密钥进行解密即可。响应的加密数据如下: [代码]const cryptojs = require('crypto-js'); const responseData = JSON.stringify(...); //此为S端需要返回给C端的数据 const aesKey = ...; //此为之前C端用来加密数据的AES密钥 const aesIV = createRandomStr(16); //生成初始化向量IV const encryptedData = cryptojs.AES.encrypt(data, aesKey, { iv: aesIV, mode: cryptojs.mode.CBC, padding: cryptojs.pad.Pkcs7 }); //加密响应数据 const encryptedResponse = { "encryptedData": encryptedData, "iv": aesIV }; //得到加密后的响应数据并返回给C端 [代码] C端解密数据 C端接收到S端的响应数据后应对加密的数据进行解密,此次解密就是单纯的AES解密了,使用发起请求时用于加密数据的AES密钥配合响应数据的iv向量对encryptedData进行解密,得到解密后的数据即为S端真正的响应数据。实现过程如下: 此处引入了CryptoJS.js [代码]const CryptoJS = require('./CryptoJS'); const key = ...; //之前用于加密请求参数的AES加密密钥 const { encryptedData, iv } = responseData; const aesIV = CryptoJS.enc.Utf8.parse(iv); const aesKey = CryptoJS.enc.Utf8.parse(key); const decryptedData = CryptoJS.AES.decrypt(encryptedData, aesKey, { iv: aesIV, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); //AES解密响应数据 const { ... } = decryptedData; //得到解密后的响应数据 [代码] 结语 第一次写这么长的文章,可能存在大量纰漏,如果大佬发现问题欢迎指出(`・ω・´)我会马上修改。 也许小程序的运行环境会比我想象中的更安全 也许HTTPS也会比我想象中的更安全 也许Web服务器引擎也比我想象中的更安全 但,安全不总是先行一步的吗?
2019-10-24 - wx.openlocation调用微信内置地图,然后点右下角使用高德地图导航,起始位置要怎么修改?
使用wx.openlocation调用微信内置地图,然后点右下角使用高德地图导航,在高德app里显示的是图上红框位置的地址名称,这个起始位置名称要怎么修改[图片]
2020-06-26 - MapContext.setLocMarkerIcon(Object object)怎么用?
this.mapCtx = wx.createMapContext('myMap') this.mapCtx.moveToLocation(); this.mapCtx.setLocMarkerIcon({ iconPath:"https://res.wx.qq.com/wxdoc/dist/assets/img/map.dd86f5e2.jpg", complete:function(res){ console.log('setLocMarkerIcon') console.log(res) } }) 用了,图片没有改成功[图片]
2021-04-09 - 抽奖活动小程序运营合规记录
本文背景是这样的,我的抽奖小程序今天被封了,不管什么原因被封的,虽然让我很被动,也带来很大的损失,但是我本身是有预期和能够接受的, [图片] 因为抽奖的属性就决定了,我不能让所有人都满意,因为我的奖项设置是每个奖项只能有一人中奖,几百人参与,只有一个会中奖,所以其他参与的用户心里有怨言,我是承认的,换位思考下,我懂 当有人因为屡次抽奖不中的情况下,通过方便的投诉入口,对小程序进行投诉,当这种投诉进入官方审核人员视线,官方说你不行你就不行,行也不行,同样的抽奖场景,我对比过累计用户过亿的抽奖助手小程序,存在同样的交互,即看激励式视频广告,然后再抽奖 但是我不能理解的是,小程序官方的审核人员、以及广告的审核是怎么让我的审核通过的,让我的小程序处于封禁的处境,说审核通过的是你们,说违规的还是你们,当然可能你们是两拨人,要不这不是啪啪打自己脸嘛 上面这一行可以忽略,我真想一个大耳刮子扇向自己,为了流量主那点收益,冲昏了头脑,抽奖还让用户看多达30秒的激励式视频广告,这太狠了,能活6天,我是不是应该高兴才对~~ 下图为被封当天早上的截图,可见每天UV破5000那是很轻松的 [图片] 上线第一天的流量主收益,每天达到了150元 [图片] 哦,写了这么多,感觉自己变成了一个怨妇了,回归本文,今天我不是来抱怨,更不是来发牢骚的 本文内容具体被封的原因如下所示 通过下图我们不难看出,是由于转发的原因(除了转发,小程序不提供其他截图里面的功能),说是利诱那没有错,我抽奖,有利诱,目前我定位到问题所在就是因为抽奖之后有个转发的功能 f [图片] f [图片] f 这是审核人员给出的被封理由截图, [图片] f [图片] f 但是我想说的是,激励式视频广告本身的场景就是用于这种情况的,抽奖之前,让用户看广告,这个路线,难道存在违规风险, 我对这一点始终不能理解 本文总结我总结了下被封的原因是因为:小程序里面有以下两个功能 (1)抽奖之前会提示用户看激励式视频 (2)抽奖之后,提供用户分享的入口,没有强制分享,分享也不会增加中奖机会,仅仅做为一个普通的分享入口存在 所以对于我们以后在类似场景下,在小程序运营红线面前,要始终保持清醒,不要为金钱冲昏了头脑。 我对小程序做了以下两个改造重新换了小程序上线,目前已发布 (1)去掉抽奖之前的激励式广告 (2)去掉小程序里面的分享入口,不提供任何分享操作 f 2020-07-28补充 那些说,抽奖送皮肤属于网赚的, 我只能用的小程序以身试法了,目前第二个小程序已上线运营,如果再被封掉,就可以断定是抽奖送皮肤是被封的源头了 关于网赚的定义,群里小伙伴提供了官方的定义 “网赚”小程序,你只了解1%? - 微信开放社区 https://developers.weixin.qq.com/community/develop/doc/000a0646f480f013c179a01f951409 申诉结果出来了,是激励式视频惹的祸 [图片] 再次重申下,我不是来寻求说法的,官方永久封禁,我会通过正常渠道申诉,至于申诉是否通过,我相信官方有定论,即使不通过,我也不委屈= 2020-07-30 抽奖助手小程序截图,具体的抽奖交互就是首先要看激励是视频广告,这一步是必要条件,绕不过去的,当然并不是所有抽奖都是这种交互,但是肯定是存在的,截图为证 f [图片] [图片] f
2021-01-11 - 小程序服务内容不符合个人性质?小程序名称不符合个人备案要求?
1.小程序服务内容不符合个人性质,请修改服务内容符合个人性质,或者使用企业证件进行备案。 2.小程序名称不符合个人备案要求小程序名称涉及企业/单位/商城等非个人性质,请修改为与实际小程序业务有关的名称;如果你是企业小程序,请使用企业证件进行备案。 被退回两次了,都是这两个原因,真不知道怎么改才能符合个人性质,符合个人备案要求。 [图片]
2023-10-24 - 小程序备案写不涉及文化情况说明书不规范?既然没有模板,那求教下什么样的才叫规范?
不涉及文化情况说明书 日期:XXXX年XX月XX日 致:相关单位或个人 尊敬的XXX: 我司/我方注意到,在某些情况下,某些事项可能被解读为与特定文化背景或文化相关。但是,我们郑重声明,本次涉及的事项或情况与任何特定文化无关。 我们明确表示,本次所涉及的事项或情况不涉及任何与特定文化背景或文化相关的内容。我们对此进行明确的说明和澄清,以避免任何可能的误解。 我们对本次所涉及的事项或情况的说明如下: 本次事项/情况的背景和原因:请在此处详细说明本次事项或情况的背景和原因。请注意,所有涉及的事项或情况均与任何特定文化无关。 本次事项/情况的详细情况:请在此处详细说明本次事项或情况的详细情况。请注意,所有涉及的事项或情况均与任何特定文化无关。 可能产生的社会影响:请在此处说明本次事项或情况可能产生的社会影响。请注意,所有涉及的事项或情况均与任何特定文化无关。 我们再次强调,本次涉及的事项或情况不涉及任何与特定文化背景或文化相关的事项或因素。我们希望以上说明能够充分传达这一信息。如有任何疑问,请随时与我们联系。 谢谢! XXX公司/单位/个人 代表:XXX
2023-10-12 - 微信小程序客服消息回复开发
概述 微信小程序为了提高小程序的服务质量,提供了客服消息能力,目的是为了让用户快捷地与小程序服务提供方进行沟通。小程序的客服消息回复有两种方式:一种是接入用户消息到微信公众平台网页版客服工具和客服小助手小程序进行客服消息回复,接入后客服可以看到用户留言,根据用户问题进行专门解答。一种是开启消息推送,当客服无法及时回复的时候能够指导用户联系客服人员或者解决问题。 如果需要接入微信公众平台网页版客服工具和客服小助手,只需要在小程序后台->客服里头添加客服人员就可以,客服人员就可以实时接收到用户消息并且与用户沟通。 如果需要开启消息推送可以参考下文的接入过程。 消息推送开发准备条件 在小程序中设置button组件并且把open-type属性设置为contact 前往小程序后台开发->开发设置->消息推送配置相关信息,可在此指定消息加密方式和数据格式。注意当开启了消息推送,普通微信用户向小程序客服发消息时,微信服务器会先将消息 POST 到开发者填写的 URL 上。在此处的填写我选择的是明文模式和JSON数据格式。 开发过程 处理初次验证 填写完消息推送的配置并且提交后,微信服务器将发送GET请求到填写的URL地址进行校验。因此首先要进行的就是针对微信服务器的初次校验做处理。微信官方在消息推送章节已经提出了校验代码此处便不再重复。 [代码]//微信服务器验证处理 if (isset($_GET['echostr'])) { //调用微信提供的校验代码 if ($this->checkSignature() == false) { exit(); } $echoStr = $_GET['echostr']; echo $echoStr; exit; } [代码] 处理消息 校验成功后,微信服务器会将用户在客服会话中的消息转发到开发者的服务器上,针对微信服务器传入的消息的类型,开发者们可以编写不同的业务逻辑处理。以用户在客服会话中写入文本为例:根据选择的数据格式JSON或者XML,微信服务器会传入相应格式的数据包。根据"MsgType"可以分辨微信服务器转发的是何种类型消息,并编写不同的业务逻辑。 [代码]//1接受微信推送消息 $message = $GLOBALS["HTTP_RAW_POST_DATA"]; $message = json_decode($message, true); //2判定用户发送消息的类型 if (!empty($message['MsgType']) && $message['MsgType'] == 'text') { //do something } [代码] 在处理完微信转发的消息之后,开发者可以根需要调用服务端的客服消息发送接口发送消息给用户。 [代码]$fromUsername = $message['FromUserName']; //发送者openid $url = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=$access_token";//根据需要获取access_token $data = array( "touser" => $fromUsername, "msgtype" => "text", "text" => array( "content" => "客服消息推送测试" ) ); $data = json_encode($data, JSON_UNESCAPED_UNICODE); $result = httpRequest($url, "post", $data); $result = json_decode($result, true); if ($result['errcode'] == 0) { //当处理成功之后返回空字符串或者success都可以防止微信服务器重新发起请求 echo ""; exit; } [代码] 转发客服消息 小程序设置了推送消息之后,还可以接入到网页版客服工具中,只需要设置返回数据的MsgType为transfer_customer_service返回给微信服务器。 [代码]//设置转发数据 $transferData = array( "ToUserName" => $message['FromUserName'],//用户的OpenID "FromUserName" => $message['ToUserName'],//小程序原始id "CreateTime" => $message['CreateTime'],//创建时间 "MsgType" => "transfer_customer_service",//指定为transfer_customer_service 消息将会转发到客服工具中 ); $transferData = json_encode($transferData, JSON_UNESCAPED_UNICODE); [代码] 完整代码演示 [代码]//接受微信服务器转发的请求。 public function getMessage() { // 判断是否为微信验证消息 if (isset($_GET['echostr'])) { if ($this->checkSignature() == false) { exit(); } $echoStr = $_GET['echostr']; echo $echoStr; exit; } //接受微信推送消息 $message = $GLOBALS["HTTP_RAW_POST_DATA"]; if (!empty($message)) { $access_token = $this->getAccess();//根据需要获取小程序对应的 access_token //设置转发客服消息 $fromUsername = $message['FromUserName'];//消息发起用户的open_id $transferData = array( "ToUserName" => $fromUsername,//接收方帐号(用户的OpenID) "FromUserName" => $message['ToUserName'],//小程序原始id "CreateTime" => $message['CreateTime'],//创建时间 "MsgType" => "transfer_customer_service",//指定为transfer_customer_service 消息将会转发到客服工具中 ); $transferData = json_encode($transferData, JSON_UNESCAPED_UNICODE); $message = json_decode($message, true); //判定消息类型并处理 if (!empty($message['MsgType']) && $message['MsgType'] == 'text') { //调用send接口发送相对应的消息 $url = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=$access_token"; $data = array( "touser" => $fromUsername, "msgtype" => "text", "text" => array( "content" => "客服消息推送测试" ) ); $data = json_encode($data, JSON_UNESCAPED_UNICODE); $result = httpRequest($url, "post", $data); $result = json_decode($result, true); //回复消息之后 不转发消息到客服系统 返回success 或者空字符串 避免微信提示严重错误 if ($result['errcode'] == 0) { echo ""; exit; } //回复消息之后 转发客服消息到客服系统 将$transferData['MsgType']设置为transfer_customer_service //if ($result['errcode'] == 0) { //echo $transferData; //exit; //} } } } /** * 处理微信验证函数 */ public function checkSignature() { $signature = $_GET['signature']; $timestamp = $_GET['timestamp']; $nonce = $_GET['nonce']; $token = "customer12"; //填写在后台配置的Token(令牌) $tmpArr = array($token, $timestamp, $nonce); sort($tmpArr, SORT_STRING); $tmpStr = implode($tmpArr); $tmpStr = sha1($tmpStr); //加密 if ($tmpStr == $signature) { return true; } else { return false; } } /** * CURL请求 * @param $url 请求url地址 * @param $method 请求方法 get post * @param null $postfields post数据数组 * @param array $headers 请求header信息 * @param bool|false $debug 调试开启 默认false * @return mixed */ function httpRequest($url, $method, $postfields = null, $headers = array(), $debug = false) { $method = strtoupper($method); $ci = curl_init(); curl_setopt($ci, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); curl_setopt($ci, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0"); curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, 60); /* 在发起连接前等待的时间,如果设置为0,则无限等待 */ curl_setopt($ci, CURLOPT_TIMEOUT, 7); /* 设置cURL允许执行的最长秒数 */ curl_setopt($ci, CURLOPT_RETURNTRANSFER, true); switch ($method) { case "POST": curl_setopt($ci, CURLOPT_POST, true); if (!empty($postfields)) { $tmpdatastr = is_array($postfields) ? http_build_query($postfields) : $postfields; curl_setopt($ci, CURLOPT_POSTFIELDS, $tmpdatastr); } break; default: curl_setopt($ci, CURLOPT_CUSTOMREQUEST, $method); /* //设置请求方式 */ break; } $ssl = preg_match('/^https:\/\//i', $url) ? TRUE : FALSE; curl_setopt($ci, CURLOPT_URL, $url); if ($ssl) { curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, FALSE); // https请求 不验证证书和hosts curl_setopt($ci, CURLOPT_SSL_VERIFYHOST, FALSE); // 不从证书中检查SSL加密算法是否存在 } curl_setopt($ci, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ci, CURLOPT_MAXREDIRS, 2);/*指定最多的HTTP重定向的数量,这个选项是和CURLOPT_FOLLOWLOCATION一起使用的*/ curl_setopt($ci, CURLOPT_HTTPHEADER, $headers); curl_setopt($ci, CURLINFO_HEADER_OUT, true); $response = curl_exec($ci); $requestinfo = curl_getinfo($ci); $http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE); if ($debug) { echo "=====post data======\r\n"; var_dump($postfields); echo "=====info===== \r\n"; print_r($requestinfo); echo "=====response=====\r\n"; print_r($response); } curl_close($ci); return $response; } [代码]
2019-05-29 - 小程序备案指南(企业备案),持续更新
在mp后台: 1:未上架的小程 首页--小程序发布流程--小程序备案(查看能否备案)。 说明:此页面是未发布小程序前的首页页面,发布后的不一样,不要纠结找不找得到、没有这个页面。已经发布的看下方第二张图。 可以备案: [图片] 2:已上架的小程 可以备案: 小程序管理后台顶部会提示“小程序需补充备案信息”的提醒,点击【去备案】即可进入备案流程 或在设置--基本设置--小程序备案(去备案) 不能备案: 设置--基本设置--小程序备案(暂未对存量小程序开放) [图片] 企业小程序备案准备材料: 营业执照(副本扫描件或加盖公章的复印件,建议用副本扫描件,在上面加上**小程序备案所用,他用无效)。法人身份(最好是法人,负责人的也可以)证正反面照片,彩色的,拍照要拍全。管理员个人信息,姓名,身份证号,电话,备用电话,常用邮箱。(建议管理人员和负责人是同一个人)地址填写,最好是营业执照上地址,也可以是常用地址。前置审批/专项审批(具体可查看https://developers.weixin.qq.com/miniprogram/product/record_guidelines.html)。补充材料:根据规则提供包括但不限于授权书、党建证明、居住证、情况说明、承诺书等。互联网信息服务备案承诺书(单位)。资料提前准备好,需要法人扫码验证(和小程序认证差不多)。根据不同地区,准备资料可能有所差异,详细需要什么资料,审核备案时具体再做补充。如果上述没有看懂,转移这里,官方给出的流程https://developers.weixin.qq.com/miniprogram/product/record_guidelines.html[图片] 备案流程 [图片] 。。。。。。(持续更新详细步骤) 九月八号上午填写备案信息,九月十三号成功备案(本来九月八号当天发验证短信的,用户这边没及时验证,耽搁一天) [图片] 常见信息填写问题 1、备案流程中的主办单位、主体负责人具体指的是谁?主办单位 又称互联网信息服务主办者,主要指内容服务提供者,包括单位(如企业、政府机关等)和个人两类。主体负责人 个人:主体负责人应为主办单位本人。非个人:通常由单位法定代表人担任,如有特殊情况(如法代身份涉密、长期不在国内等)可授权单位高管担任。2、个体户没有公章怎么办?若个体工商户无公章,需要主体负责人手写日期+签名+盖手印+身份证号码,同时请在主体备注处备注“个体工商户无公章”。 3、填写小程序主体信息的通讯地址是指的什么地址?可填写主体证件上的地址,也可填写你实际的办公或住所地址。 若你是个人开发者:需精确到门牌号码,若已是最详细的地址或无门牌号的,在主体备注中说明“通信地址已为最详细”。若你是单位开发者需精确到门牌号码,且至少和主体证件所省份保持一致(如证件住所和通讯地址都是广东省),不能使用特殊符号(如:2#楼2-3-301);若已是最详细的地址,无门牌号的,在主体备注中说明“通信地址已为最详细”。备注:若你是北京地区,通讯地址填写时不能使用特殊符号(如:2#楼2-3-301)。4、什么情况下需要上传居住/暂住证明?当个人主体小程序备案申请人的身份证证件地址与申请小程序备案的省份不一致时,需要提供暂住证或居住证等证明材料。 涉及省份包括:吉林、上海、江苏、浙江、安徽、山东、湖北、广东、四川、贵州、云南。 5、小程序备案主体负责人必须填写法定代表人吗?每个省份管局的要求不一致,请按照备案小程序所属省管局要求进行填写,具体请参考: 类型地区主体负责人不是法定代表人需提供小程序主体负责人授权书吉林、山西、甘肃、江苏、安徽、四川 主体负责人必须是法定代表人:天津、内蒙古、陕西、宁夏、新疆、湖北、湖南、河南、上海、浙江、江西、贵州、重庆、云南、西藏、广西、广东、福建、黑龙江、河北、山东、青海 主体负责人可以不是法定代表人吉林、山西、甘肃、江苏、安徽、四川、海南、北京、辽宁 6、提示:主体负责人与法定代表人不一致,且备案所在地不支持法定代表人授权?你填写的【主体负责人】姓名与营业执照证件上的【法定代表人】姓名不一致,请重新填写,并保持一致。你在小程序备案 -【验证备案类型】页面中 - 主办人信息 - 选择地区中选择的省份,不支持法定代表人授权,【主体负责人】需填写【法定代表人】姓名。备案省份需填写小程序备案主体实际所在地,系统会根据你选择的区域自动匹配当地管局规则。7、在填写负责人手机号、应急手机号、邮箱时,提示:不允许被多人使用?在填写负责人手机号、应急手机号、邮箱时,提示“不允许被多人使用”,一般是出现了个人信息混用的情况,即手机号/应急手机号/邮箱填写的是其他人的信息。 在平台备案系统中,人,手机号,应急手机号,邮箱均一一绑定,同一个人允许为多个小程序备案(同一主体下),可以提交一致的手机号、应急手机号及邮箱,但不能出现不同人共用手机号/邮箱的情况。 小程序负责人授权书、小程序主体负责人授权、互联网信息服务承诺书怎么填写?小程序备案材料示例及填写指引:小程序备案材料示例小程序信息填写相关1、什么是服务内容标识?怎么选?服务内容标识是通信管局对各个行业的分类,平台部分行业类目与管局行业类目名称不完全不一致,建议根据备案小程序实际运营内容尽可能选择对应的服务内容标识。 若你是个人主体,请勿选择经营性质、企业/单位性质、涉及有关主管部门审批等的内容,如不可选择“批发和零售业-零售批发”。若你是单位主体,应选择与主体经营范围、资质相符合的内容,如你是医药公司,可选择“医疗服务-医药”,并上传《互联网药品信息服务许可证》。非政府单位不得选择“政务民生”内容。2、小程序负责人具体是指谁?是小程序管理员吗?个人主体:小程序负责人应为主办人本人。 非个人主体:小程序负责人应为本单位/公司具体负责小程序管理、小程序维护的相关人员。 3、怎么判断备案小程序是否要选择前置审批项?可参考:前置审批类别及审批部门 4、小程序管理员信息填写时,负责人姓名已填写为小程序管理员的姓名,为什么还是提示:负责人与小程序管理员不一致?出现这种提示一般都是第三方服务商协助创建的小程序未完善管理员实名信息,需补充管理员实名信息后才能进行备案,补充指引参考: 小程序MP后台-成员管理-管理员-修改。验证原管理员-填写原管理员身份证信息-扫码验证。绑定新管理员-填写【原管理员的信息】并提交,即完成管理员实名信息补充。相关文档可参考:如何完善小程序实名信息 小程序备案常见问题:https://developers.weixin.qq.com/community/develop/article/doc/000ac251a9c340df3e6073ee566c13 最后祝大家,一次备案成功
2023-10-08 - 【微信小程序备案】9月1日起,微信小程序需完成备案才可上架!已上架用户,查看这份备案手册,快人一步,抢先备案!
自2023年9月1日起,微信小程序必须完成备案后才可上架;已经上架的小程序,要于2024年3月31日前完成备案,若逾期未完成备案,微信官方平台会按照相关规定于2024年4月1日起进行清退处理,目前备案通道暂未开启,9月1号正式开启备案通道,来一间商户,可提前准备资料,熟悉流程! 备案需要准备哪些资料 主办单位证件(营业执照) 主体负责人证件(身份证) 小程序负责人证件(身份证) 需要小程序负责人扫脸验证 其他材料:根据各省通信管理局要求,上传对应补充材料。 备案流程 ➤ 登录小程序管理后台([https://mp.weixin.qq.com/])首页顶部会提示“小程序需补充备案信息”,点击“去备案”;也可以在小程序管理后台设置模块-小程序备案-点击去备案,填写备案信息,并上传材料 ➤ 1-2个工作日内完成初审并告知结果 ➤ 提交通管局后,24小时内完成短信核验 ➤ 通管局在1-20个工作日内完成审核并告知结果 ➤ 审核通过后,下发小程序备案号 备案信息填写 1、主体信息填写 分个人主体和非个人主体 [图片] (非个人主体) [图片] (个人主体) 提示:选择地区、详细地址,请填写实际通讯地址。 上传有效证件原件的彩色扫描件或拍照件,请保持图片清晰完整、不遮挡关键信息/图像、边角齐全,证件有效期大于1个月。 请对照证件信息如实选择、填写备案信息。 2、主体负责人信息填写 提示:手机号码请填写主体负责人、应急联系人本人使用的有效手机号码,需保持电话畅通,不同人不能使用同一个号码 [图片] 3、小程序信息填写 [图片] 4、小程序负责人信息填写及人脸核身 如果小程序负责人和主体负责人一致,可直接复用主体人信息,如不一致,需手动填写小程序负责人信息。 [图片] 平台初审 开发者在微信公众平台提交小程序备案信息后,平台将会在1-2个工作日内完成初审(具体请以实际审核时间为准),审核结果将以站内信息、模板消息等形式通知管理员。在平台初审的过程中,请备案相关人员保持电话畅通,以便平台对你提交的备案信息进行核验。 工信部短信核验 根据工信部的要求,小程序备案需完成短信核验。你的备案初审通过后,会收到工信部发送的核验短信(发送号码:12381),请在收到短信的24小时内登录工信部备案官网首页完成短信核验,短信核验成功后备案申请进入通管局审核流程。 若核验时提示验证码错误或无效,你可触发一次短信重发;24小时内未核验成功,备案将被工信部系统自动驳回,你需到微信公众平台重新提交备案申请 [图片] [图片] 通管局审核 各省通信管理局将在1-20个工作日内(具体以实际审核时间为准)完成审核,审核结果以站内信、模板消息、短信等形式通知。 备案成功 管局审核通过后,将下发的小程序备案号,代表你的小程序已完成备案,可以进入下一步版本发布等流程。 [图片] 此备案流程仅适用酒旅商家操作,如需查看更多,请查看微信小程序官方备案操作指引
2023-08-28 - 关于小程序隐私指引无内容弹窗问题?
小程序获取定位之前先使用wx.getPrivacySetting判断是否需要签署隐私协议,跳转协议签署页面之后又出现了官方弹窗且没有内容,3.1.1版本出现的,3.1.1以下版本无此问题,麻烦各位大佬帮忙分析一下吧[图片]
2023-09-27 - 我是公司负责小程序开发的,小程序负责人必须是小程序管理员吗,那我这怎么搞?
[图片]小程序负责人授权书我也签字盖章了,还没看见有地方上传,那如果小程序负责人必须是小程序管理员,我也绑不过来啊,最多绑5个,公司十个几十个,怎么解决啊;谁是管理员谁就是小程序负责人了???
2023-09-15 - wx.getPrivacySetting相关的问题?
有几个问题: 1、wx.getPrivacySetting 官方写的基础库 2.32.3 开始支持 但是实际上一直返回的是false,改为3.0.0就正确了。 2、wx.getPrivacySetting需要做低版本兼容处理 我现在的做法是基础库直接最低版本3.0.0(也就是安卓8.0.39 ios8.0.38)这样就会导致很多微信不更新的用户 打开小程序就提示更新 体验不友好。 我的问题是 1、wx.getPrivacySetting 现在基础库3.0.0(也就是安卓8.0.39 ios8.0.38)是微信的最低版本 才能使用吗?如果微信用户的版本低于这两种是不是就用不了这个接口 2、有些手机的微信版本最新才8.0.38 没办法升级到8.0.39 [图片]
2023-08-25 - 关于新版隐私协议接口wx.onNeedPrivacyAuthorization的解读以及实现代码(二)
官方公告地址: https://developers.weixin.qq.com/community/develop/doc/00042e3ef54940ce8520e38db61801 目前,开发工具或者体验版的小程序,调试基础库改成3.0.0可以适配测试,线上版本9月15日之后生效,所以这之前需要尽快改完,发布一版,否则到了9月15号之后 线上就会生效报错了。 这是官方的Demo地址,也是每个页引用一个全局的组件,个人感觉太过于繁琐了,可以参考以下我整理的代码。 https://developers.weixin.qq.com/miniprogram/dev/framework/user-privacy/PrivacyAuthorize.html 其实改起来也很简单,以下是实现步骤和代码: 1、首先看一下这个网址,里边包含涉及到的隐私的接口,这些接口都要适配一下 https://developers.weixin.qq.com/miniprogram/dev/framework/user-privacy/miniprogram-intro.html [图片] 在以上接口用到的页面,需要画一下类似上边的弹窗(这个弹窗可以全局定义个组件,方便多个页面共用),然后里边蓝字可以点击后调用wx.openPrivacyContract(Object object)接口即可,会自动跳转打开隐私协议页面。 拒绝按钮可以加一个点击事件,然后在事件里这样写,官方demo里也没有加id。 <button class="btn-refuse" catch:tap="clickRefuse">拒绝</button> refuse() { this.resolvePrivacyAuthorization({ event: 'disagree' }) }, 同意按钮比较特殊,布局需要用button这样写,记得给button加一个Id <button id="agree-btn" class="btn-agree" open-type="agreePrivacyAuthorization" bindagreeprivacyauthorization="handleAgreePrivacyAuthorization">同意</button> 然后在handleAgreePrivacyAuthorization里就可以获取到点击事件,这样写 handleAgreePrivacyAuthorization() { this.resolvePrivacyAuthorization({ buttonId: 'agree-btn', event: 'agree' }) }, 2、然后每个有隐私接口的页面引用自己的组件,代码如下 { "usingComponents": { "agreement": "/components/agreement/agreement" } } 布局引用 <!-- 隐私授权弹窗 --> <agreement id="agreement" frameTitle="温馨提示" bind:refuse="refuse" bind:agree="agree"></agreement> 让组件弹窗显示,这样写: this.selectComponent('#agreement').show(); 3、最后需要在用到隐私接口的页面的onShow里加上以下监听代码,在这里边让自定义的隐私弹窗显示出来即可。 onShow: function () { let that = this; if (wx.onNeedPrivacyAuthorization) { wx.onNeedPrivacyAuthorization(resolve => { this.selectComponent('#agreement').show();//这里是让组件弹窗显示 this.resolvePrivacyAuthorization = resolve }) } } 以上代码加上就可以了,如果业务逻辑用到了需要判断是否授权过,可以加上 wx.getPrivacySetting(Object object)去获取是否授权过,用不到可以不加这个判断。 //=======================以下是针对昵称输入框input的type="nickname"的代码=========================== 注意:以下代码只是针对input另外加的,上边提到的代码也都要加上才能正常弹出弹窗 布局,在input的外层View加touch事件,然后加一个focus动态控制焦点。 <view catch:touchstart="handleTouchInput"> <input type="nickname" class="text" bindinput="listenerContent" placeholder="输入名称" maxlength="10" value='{{name}}' focus="{{focus}}" /> </view> 然后,touch事件: handleTouchInput() { let that = this if (wx.getPrivacySetting) { wx.getPrivacySetting({//获取是否需要弹出 success: res => { if (res.needAuthorization) { wx.requirePrivacyAuthorize({//该接口请求隐私协议,会自动触发上边的wx.onNeedPrivacyAuthorization success: () => { that.setData({ focus: true, }) }, fail: () => {}, complete: () => {} }) } else { that.setData({ focus: true, }) } }, fail: () => {}, complete: () => {} }) } else { this.setData({ focus: true }) } }, 这样就可以了,记得把上边提到的监听代码都加上。
2023-08-23 - 提交代码审核之前必须等调用设置小程序用户隐私保护指引接口审核了才能调用是吗?
我现在是提交代码审核之前调用了设置小程序用户隐私保护指引接口,反馈:{"errcode":61039,"errmsg":"隐私接口检查任务未完成,请稍等一分钟再重试, hint: [b2491477-57b2-498d-90ac-e6811d72b85c] rid: 64dd987d-2059bade-5c02196f"} 然后紧接着调用提交代码审核,得到反馈:{"errcode":61039,"errmsg":"隐私接口检查任务未完成,请稍等一分钟再重试 rid: 64dd987d-3a13cb97-5ab5c447"} 请问隐私保护指引接口提交完了后,如何拿到结果反馈,有没有走消息推送?查了文档没有说明。
2023-08-17 - 关于新版隐私协议接口wx.onNeedPrivacyAuthorization的适配解读以及实现代码
官方公告地址: https://developers.weixin.qq.com/community/develop/doc/00042e3ef54940ce8520e38db61801 目前,开发工具或者体验版的小程序,调试基础库如果是2.33.0及以上就得适配了,线上版本9月15日之后生效,所以这之前需要尽快改完,发布一版,否则到了9月15号之后 线上就会生效报错了。 其实改起来也很简单,以下是实现步骤和代码: 1、首先看一下这个网址,里边包含涉及到的隐私的接口,这些接口都要适配一下 https://developers.weixin.qq.com/miniprogram/dev/framework/user-privacy/miniprogram-intro.html [图片] 在以上接口用到的页面,需要画一下类似上边的弹窗(这个弹窗可以全局定义个组件,方便多个页面共用),然后里边蓝字可以点击后调用wx.openPrivacyContract(Object object)接口即可,会自动跳转打开隐私协议页面。 拒绝按钮可以加一个点击事件,然后在事件里这样写 [图片] 同意按钮比较特殊,布局需要用button这样写,记得给button加一个Id [图片] 然后在handleAgreePrivacyAuthorization里就可以获取到点击事件,这样写 [图片] 2、最后需要在onLoad或者onShow里加上以下监听代码,在这里边让自定义的隐私弹窗显示出来即可。 [图片] 以上代码加上就可以了,如果业务逻辑用到了需要判断是否授权过,可以加上 wx.getPrivacySetting(Object object)去获取是否授权过,用不到可以不加这个判断。
2023-08-16 - 对应的服务器证书无效,怎么检查?
[图片] 提示服务器证书无效,是申请的免费证书,没过期,在哪个网址或者平台可以检查证书有啥问题啊?
2023-06-12 - 使用wx.request发送multipart/form-data请求的方法
社区里有不少关于wx.request发送multipart/form-data请求的问题,我看了几个,基本都说wx.request不支持,得用wx.uploadFile来“曲线救国”。我花了些时间研究了一下,找到了如下方法,分享出来,希望能帮助到有需要的人。 首先,服务端,我用的是async-busboy来解析multipart/form-data请求,测试代码直接使用它提供的就可以:https://github.com/m4nuC/async-busboy/blob/master/examples/index.js 小程序端: 如果wx.request中仅指定'content-type'为'multipart/form-data',服务端会报"no multipart boundary was found"的错误,就像这个帖子里描述的:https://developers.weixin.qq.com/community/develop/doc/000e64476f8d80fd7b9765d5655c00 尽管指定了content-type为'mutipart/form-data',但是wx.request并不会为我们自动添加boundary,既然如此,我们自己加不就可以了吗?经过测试,确认了这个方法可行,前端示例代码如下: [代码]wx.request({[代码][代码] [代码][代码]url: [代码][代码]'http://localhost:8080/test/multipart-form'[代码][代码],[代码][代码] [代码][代码]method: [代码][代码]'POST'[代码][代码],[代码][代码] [代码][代码]header: {[代码][代码] [代码][代码]'content-type'[代码][代码]: [代码][代码]'multipart/form-data; boundary=XXX'[代码][代码] [代码][代码]},[代码][代码] [代码][代码]data: [代码][代码]'\r\n--XXX'[代码] [代码]+[代码][代码] [代码][代码]'\r\nContent-Disposition: form-data; name="field1"'[代码] [代码]+[代码][代码] [代码][代码]'\r\n'[代码] [代码]+[代码][代码] [代码][代码]'\r\nvalue1'[代码] [代码]+[代码][代码] [代码][代码]'\r\n--XXX'[代码] [代码]+[代码][代码] [代码][代码]'\r\nContent-Disposition: form-data; name="field2"'[代码] [代码]+[代码][代码] [代码][代码]'\r\n'[代码] [代码]+[代码][代码] [代码][代码]'\r\nvalue2'[代码] [代码]+[代码][代码] [代码][代码]'\r\n--XXX--'[代码][代码] [代码][代码]})[代码]这里,我向服务端传递了两个参数:field1=value1, field2=value2,服务端得到的结果如下: [图片] 如果对multipart/form-data的请求体格式不了解,可以参考这篇文章:https://ec.haxx.se/http-multipart.html wx.request中的data结构看起来有些复杂,有需要的可以封装一下,封装好的API欢迎跟帖分享出来。
2019-08-10 - wx.uploadFile上传多张图片
- 需求的场景描述(希望解决的问题) - 希望提供的能力 wx.uploadFile上传多张图片
2018-11-07 - 内容安全:msgSecCheck违规内容也返回无问题(PHP)
同样的内容, 用小程序发送提示有违规用PHP发送就返回无问题. 代码如下: private function httpPost($url, $data) { $curl = curl_init(); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_POST, 1); curl_setopt($curl, CURLOPT_POSTFIELDS, $data); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2); curl_setopt($curl, CURLOPT_HEADER, 0); //设置header curl_setopt($curl, CURLOPT_URL, $url); $res = curl_exec($curl); curl_close($curl); return $res; } $ak = 'xxxxxxxxxxx'; $url = 'https://api.weixin.qq.com/wxa/msg_sec_check?access_token='.$ak; $data = ['content'=>'xxxxxx]; $res = $this->httpPost($url, json_encode($data)); 另外: imgSecCheck的mediaForm-Data 这个Form-Data 是什么样的格式和msgSecCheck的参数有什么差别, 可以像msgSecCheck接口这样来吗?
2018-07-22 - PHP开发调用msgSecCheck,违规内容也返回无问题
通过PHP调用msgSecCheck,无论什么内容均返回OK,无问题,即使是很明显的违规词也返回无问题。 而且看过相关的问题处理,已经是post提交,且验证的内容也是utf-8。 复现demo: <?php $checkContent = '要检测的内容'; $url = 'https://api.weixin.qq.com/wxa/msg_sec_check?access_token=ACCESS_TOKEN'; $data = json_encode(array('content'=>$checkContent)); $ch = curl_init(); curl_setopt($ch, CURLOPT_HEADER, FALSE); curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_URL,$url); // url curl_setopt($ch, CURLOPT_POST, TRUE); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); // json数据 $res = curl_exec($ch); // 返回值 curl_close($ch); $result = json_decode($res,true); echo "<pre>"; var_dump($result); echo "</pre>"; ?>
2018-08-28 - 如何实现动态生成好友分享图
本文章采用官方最新的 Canvas.createImage() 来实现下动态生成好友分享图,可以拿来即用。展示效果如下(其中蓝框中文案和红框头像为插入的文本、图像。背景图也支持动态更换): [图片] 小程序demo案例:https://developers.weixin.qq.com/s/nJtr4QmL7RD3 一、市面案例缺陷:翻阅了目前市面上的小程序动态生成好友分享图,大部分还是使用已废弃的『wx.createSelectorQuer』接口来实现。目前小程序已无法很好支持。 二、主要有以下几个关键点需要注意下: 做好友分享图要考虑5:4的比例使用 wx.getImageInfo 一定要考虑图片失败的场景,然后采用兜底图片。相同的逻辑在complete中执行。要区分 wx.createImage ,这个是小游戏用来创建图片对象的。小程序要用Canvas.createImage()。也不是使用 new Image!!!使用的时候一定要在 canvas 类型中注明 type 是 2d 的 canvas[图片] 三、优化知识点: 如何用 async in image loading:https://stackoverflow.com/questions/46399223/async-await-in-image-loading ----- 采用await img.decode() 或者 img.onload = () => resolve() 如何隐藏Canvas:https://developers.weixin.qq.com/community/develop/doc/1aadfacdd9f38584881e0c50db2bcda1 ----- position:fixed;left:100%;
2023-06-19 - 分享小程序给好友(群),在标题下方还能设置一行小字?
如图箭头所指,该行字还是动态的,分享后还会自动更改,这个功能如何实现? 文档(地址)上也并没有此功能描述 [图片]
2022-11-18 - 获取用户信息,修改用户信息?
用getUserInfo获取用户信息,显示的是匿名用户信息,只要授权了,就不会再次要求授权,用wx.getUserProfile获取用户信息,每次进入页面就需要进行一次授权。在修改用户资料页面,console.log出来是信息没有id,那么要修改用户信息,怎么知道修改的是个用户的信息?要怎么获取?在网上查找资料,要么用的是插件,要么就是不全,查官方文档,又要改接口,对于我们初学者来说,头痛。 [图片][图片] 代码片段 https://developers.weixin.qq.com/s/nmxH4smH7pCS
2022-09-22 - 小程序性能优化实践
小程序性能优化课程基于实际开发场景,由资深开发者分享小程序性能优化的各项能力及应用实践,提升小程序性能表现,满足用户体验。
10-09 - 小程序登录、用户信息相关接口调整说明
公告更新时间:2021年04月15日考虑到近期开发者对小程序登录、用户信息相关接口调整的相关反馈,为优化开发者调整接口的体验,回收wx.getUserInfo接口可获取用户授权的个人信息能力的截止时间由2021年4月13日调整至2021年4月28日24时。为优化用户的使用体验,平台将进行以下调整: 2021年2月23日起,若小程序已在微信开放平台进行绑定,则通过wx.login接口获取的登录凭证可直接换取unionID2021年4月28日24时后发布的小程序新版本,无法通过wx.getUserInfo与<button open-type="getUserInfo"/>获取用户个人信息(头像、昵称、性别与地区),将直接获取匿名数据(包括userInfo与encryptedData中的用户个人信息),获取加密后的openID与unionID数据的能力不做调整。此前发布的小程序版本不受影响,但如果要进行版本更新则需要进行适配。新增getUserProfile接口(基础库2.10.4版本开始支持),可获取用户头像、昵称、性别及地区信息,开发者每次通过该接口获取用户个人信息均需用户确认。具体接口文档:《getUserProfile接口文档》由于getUserProfile接口从2.10.4版本基础库开始支持(覆盖微信7.0.9以上版本),考虑到开发者在低版本中有获取用户头像昵称的诉求,对于未支持getUserProfile的情况下,开发者可继续使用getUserInfo能力。开发者可参考getUserProfile接口文档中的示例代码进行适配。请使用了wx.getUserInfo接口或<button open-type="getUserInfo"/>的开发者尽快适配。开发者工具1.05.2103022版本开始支持getUserProfile接口调试,开发者可下载该版本进行改造。 小游戏不受本次调整影响。 一、调整背景很多开发者在打开小程序时就通过组件方式唤起getUserInfo弹窗,如果用户点击拒绝,无法使用小程序,这种做法打断了用户正常使用小程序的流程,同时也不利于小程序获取新用户。 二、调整说明通过wx.login接口获取的登录凭证可直接换取unionID 若小程序已在微信开放平台进行绑定,原wx.login接口获取的登录凭证若需换取unionID需满足以下条件: 如果开发者帐号下存在同主体的公众号,并且该用户已经关注了该公众号如果开发者帐号下存在同主体的公众号或移动应用,并且该用户已经授权登录过该公众号或移动应用2月23日后,开发者调用wx.login获取的登录凭证可以直接换取unionID,无需满足以上条件。 回收wx.getUserInfo接口可获取用户个人信息能力 4月28日24时后发布的新版本小程序,开发者调用wx.getUserInfo或<button open-type="getUserInfo"/>将不再弹出弹窗,直接返回匿名的用户个人信息,获取加密后的openID、unionID数据的能力不做调整。 具体变化如下表: [图片] 即wx.getUserInfo接口的返回参数不变,但开发者获取的userInfo为匿名信息。 [图片] 此外,针对scope.userInfo将做如下调整: 若开发者调用wx.authorize接口请求scope.userInfo授权,用户侧不会触发授权弹框,直接返回授权成功若开发者调用wx.getSetting接口请求用户的授权状态,会直接读取到scope.userInfo为true新增getUserProfile接口 若开发者需要获取用户的个人信息(头像、昵称、性别与地区),可以通过wx.getUserProfile接口进行获取,该接口从基础库2.10.4版本开始支持,该接口只返回用户个人信息,不包含用户身份标识符。该接口中desc属性(声明获取用户个人信息后的用途)后续会展示在弹窗中,请开发者谨慎填写。开发者每次通过该接口获取用户个人信息均需用户确认,请开发者妥善保管用户快速填写的头像昵称,避免重复弹窗。 插件用户信息功能页 插件申请获取用户头像昵称与用户身份标识符仍保留功能页的形式,不作调整。用户在用户信息功能页中授权之后,插件就可以直接调用 wx.login 和 wx.getUserInfo 。 三、最佳实践调整后,开发者如需获取用户身份标识符只需要调用wx.login接口即可。 开发者若需要在界面中展示用户的头像昵称信息,可以通过<open-data>组件进行渲染,该组件无需用户确认,可以在界面中直接展示。 在部分场景(如社交类小程序)中,开发者需要在获取用户的头像昵称信息,可调用wx.getUserProfile接口,开发者每次通过该接口均需用户确认,请开发者妥善处理调用接口的时机,避免过度弹出弹窗骚扰用户。 微信团队 2021年4月15日
2021-04-15 - 小程序将小程序码与图片结合生成海报分享朋友圈
样例参考(瑞幸咖啡小程序) [图片][图片][图片] 需求分析 服务器端会返回不确认的图片资源到前端 前端将返回的每张图片都要贴上小程序码 将贴上小程序码的图片使用 swiper 组件轮播 用户点击保存时,将图片保存至相册。 至于点击保存如何保存至相册(wx.saveImageToPhotosAlbuml 了解一下,注意一下授权问题即可) 碰到的问题 canvas为原生组件, 而原生组件的层级是最高的,所以页面中的其他组件无论设置 z-index 为多少,都无法盖在原生组件上。 [代码]采用方法: 通过定位,将其移除到不在可视范围,如iphone6 .canvas { position: relative; left: -375px; } [代码] 绘制多张图片时, 一个canvas 标签只能对应画一张图片 (目前我测试的是这样,如有其它方法,欢迎评论交流) [代码]采用方法: 1. html端循环要生成的图片张数,对应循环出多个 canvas 组件,分别设置不同的 canvasId 区分 2. 封装一个绘制海报的函数,返回一个Promise对象,用于后面绘制完所有的图片,统一赋值渲染,避免多次触发数据更新 3. js端 循环执行一次 绘制海豹函数,存于一个数组列表 4. 使用 Promise.all 函数,统一绘制完毕将生产图片路径赋值 html: <canvas v-for="u in m.urlList" :key="u" :canvas-id="'poster'+index" class="canvas" style="width:100%; height:100%;"> </canvas> <swiper :indicator-dots="true" :circular="true" :autoplay="true" indicator-color="rgba(255,255,255,.2)" indicator-active-color="#fff" class="swiper"> <swiper-item v-for="f in m.filePaths" :key="index"> <image :src="f"> </swiper-item> </swiper> js: // 数据 (mpvue 开发) function data(){ return { m : { urlLIst: [ // 图片资源 '/static/poster0.jpg', '/static/poster1.jpg' ], filePaths: [], // 生成图片(贴上小程序码) } } } try { let res = wx.getSystemInfoSync(); // 同步获取系统信息 let w = res.windowWidth; // 手机可用区域宽度 let h = res.windowHeight; // 手机可用区域高度 let codeUrl = '/static/code.jpg'; // 小程序码 let drawList = []; // 用于保存绘制海报图Promise对象 m.urlList.forEach( (u, i)=> { // 传入canvas组件ID,图片路径(测试使用的是本地路径) drawList.push(drawPoster('poster'+i, u, codeUrl)); }) // 统一更新数据 Promise.all(drawList).then((valuse)=>{ m.filePaths = valuse; }); // 封装绘制图片函数 function drawPoster(canvasId, bgUrl, codeUrl){ return new Promise( (resolve, reject) => { // 创建画布实例 let ctx = wx.createCanvasContext(canvasId); // 绘制背景图: 图片路径,x坐标,y坐标,宽,高 ctx.drawImage(bgUrl, 0, 0, w, h); // 绘制小程序码 ctx.drawImage(codeUrl, w-120, h-120, 100, 100); // 绘制 ctx.draw(false, ()=>{ // 该通过函数将canvas绘制导出为图片 wx.canvasToTempFilePath({ x: 0, y: 0, width: w, height: h, canvasId: canvasId, success(res){ resolve(res.tempFilePath); } }); }); } }catch(e){ // 自己封装了一成 wx.$toast(e); } [代码] 最终demo效果图 [图片][图片] 在社区中暂未看到多张海报实现的方案,如果有更好的实现方案,欢迎交流
2019-07-30 - 小程序海报生成工具,可视化编辑直接生成代码使用,你的海报你自己做主
开门见山 工具地址 点我直达>>painter-custom-poster 由于挂载在github page上,打开速度会慢一些,请耐心等待或自行解决git网速问题 背景 在做小程序时候,我们经常会有一个需求,需要将小程序分享到朋友圈,但是朋友圈是不允许直接分享小程序,那我们还有其他的办法解决吗?答案肯定是有的,即 canvas 生成个性化海报分享图片到朋友圈 分析 小程序中有大量的生成图片需求,但是使用过 canvas 的人,都会发现一些难以预料的问题>>有关小程序的坑 直接在 canvas 上绘制图形,对于普通开发者来说代码会特别凌乱并且难以维护,经常会花费很久的时间去优化代码 不同的环境渲染问题,例如在开发者工具看起来好好的,一到 Android 真机,就出现图片不显示,位置不对应等等问题 解决 那可不可以开发一款生成海报的插件库呢? 首先,只需要提供一份简单的参数配置文件即可 解决掉小程序Canvas遇到的一些大大小小的坑 有严苛的测试环节,解决各种环境和各种机型遇到的问题,并提供稳定的线上版本 长期维护,并有专人更新迭代更新颖的功能 以上的要求当然是可以的,曾经的我也想尝试开发一款出来,但是后来尝试了几款现成的工具之后就放弃了,毕竟轮子这个东西,是需要不断维护更新的,另外已经有这么多优秀现成的插件了,我为何还要费力去写呢,贡献代码岂不更美哉,以下是我收集的几款 小程序生成图片库,轻松通过 json 方式绘制一张可以发到朋友圈的图片>>Painter 小程序组件-小程序海报组件>>wxa-plugin-canvas 微信小程序:一个 json 帮你完成分享朋友圈图片>>mp_canvas_drawer 我想干什么 唠了这么多,好像提供给大家插件就没我什么事情了…想走是不可能的 为了能够制作出更酷炫的海报,我思考了许久 虽然有了插件后,只需要提供配置代码就能够制作出一款海报来,但是我发现还是有些许问题 制作海报效率还是不够高,微调一个元素的大小和位置,就需要不断的修改保存代码,等待片刻,查看效果,真的烦 一个小小的位置调整可能就需要来回调整无数次,这种最简单的机械化劳动,这辈子是不可能的 拿着完美的稿子,递给设计师看,这个位置不对,这个线太粗,这个颜色太重…你信不信我打死你 对于一些精美复杂的海报,实现起来真的不太现实 那我需要怎么做呢,请点击这个链接体验>>painter-custom-poster 点击左侧例子展示中的任意一个例子,然后导入代码就能看到效果图,这下你应该能猜到了我的想法了 如何实现 刚开始我想用简单的html和css加拖动功能实现,通过简单尝试之后就放弃了,因为这个功能真的太复杂了,简单的工具肯定是不行的 中间这个计划停滞了很长时间,一度已经放弃 直到发现了这个库fabric.js,真的太太优秀了,赞美之词无以言表,唯一的缺点就是中文教程太少,必须生啃英文加谷歌翻译 fabric介绍,你可以很容易地创建任何一个简单的形状,复杂的形状,图像;将它们添加到画布中,并以任何你想要的方式进行修改:位置、尺寸、角度、颜色、笔画、不透明度等 How To Use 目前工具一共分成4部分 例子展示 用来将一些用户设计的精美海报显示出来,通过点击对应的例子并将代码导入画布中 画布区 显示真实的海报效果,画布里添加的元素,都可以直接用鼠标进行拖动,旋转,缩放操作 操作区 第一排四个按钮 复制代码 将画布的展示效果转化成小程序海报插件库所需要的json配置代码,目前我使用的是Painter库,默认会转化成这个插件的配置代码,将代码直接复制到card.js即可 查看代码 这个功能用不用无所谓,可以直观的看到生成的代码 导出json 将画布转化成fabric所需要的json代码,方便将自己设计的海报代码保存下来 导入json 将第3步导出的json代码导入,会在画布上显示已设计的海报样式 第二排五个按钮 画布 画布的属性参数 详解见下方 文字 添加文字的属性参数 详解见下方 矩形 添加矩形的属性参数 详解见下方 图片 添加图片的属性参数 详解见下方 二维码 添加二维码的属性参数 详解见下方 第三排 各种元素的详细设置参数 激活区 激活对象是指鼠标点击画布上的元素,该对象会被蓝色的边框覆盖,此时该对象被激活,可以执行拖动 旋转 缩放等操作 激活区只有对象被激活才会出来,用来设置激活对象的各种配置参数,修改value值后,实时更新当前激活对象的对应状态,点击其他区域,此模块将隐藏 快捷键 ‘←’ 左移一像素 ‘→’ 右移一像素 ‘↑’ 上移一像素 ‘↓’ 下移一像素 ‘ctrl + z’ 撤销 ‘ctrl + y’ 恢复 ‘delete’ 删除 ‘[’ 提高元素的层级 ‘]’ 降低元素的层级 布局属性 通用布局属性 属性 说明 默认 rotate 旋转,按照顺时针旋转的度数 0 width、height view 的宽度和高度 top、left 如 css 中为 absolute 布局时的作用 0 background 背景颜色 rgba(0,0,0,0) borderRadius 边框圆角 0 borderWidth 边框宽 0 borderColor 边框颜色 #000000 shadow 阴影 ‘’ shadow 可以同时修饰 image、rect、text 等 。在修饰 text 时则相当于 text-shadow;修饰 image 和 rect 时相当于 box-shadow 使用方法: [代码]shadow: 'h-shadow v-shadow blur color'; h-shadow: 必需。水平阴影的位置。允许负值。 v-shadow: 必需。垂直阴影的位置。允许负值。 blur: 必需。模糊的距离。 color: 必需。阴影的颜色。 举例: shadow:10 10 5 #888888 [代码] 渐变色支持 你可以在画布的 background 属性中使用以下方式实现 css 3 的渐变色,其中 radial-gradient 渐变的圆心为 中点,半径为最长边,目前不支持自己设置。 [代码]linear-gradient(-135deg, blue 0%, rgba(18, 52, 86, 1) 20%, #987 80%) radial-gradient(rgba(0, 0, 0, 0) 5%, #0ff 15%, #f0f 60%) [代码] !!!注意:颜色后面的百分比一定得写。 画布属性 属性 说明 默认 times 控制生成插件代码的宽度大小,比如画布宽100,times为2,生成的值为200 1 文字属性 属性名称 说明 默认值 text 字体内容 别跟我谈感情,谈感情伤钱 maxLines 最大行数 不限,根据 width 来 lineHeight 行高(上下两行文字baseline的距离) 1.3 fontSize 字体大小 30 color 字体颜色 #000000 fontWeight 字体粗细。仅支持 normal, bold normal textDecoration 文本修饰,支持none underline、 overline、 linethrough none textStyle fill: 填充样式,stroke:镂空样式 fill fontFamily 字体 sans-serif textAlign 文字的对齐方式,分为 left, center, right left 备注: fontFamily,工具中的第一个例子支持文字字体,但是导入小程序为什么看不到呢,小程序官网加载网络字体方法>> 加载字体教程>> 文字高度 是maxLines lineHeight2个字段一起计算出来的 图片属性 属性 说明 默认 url 图片路径 mode 图片裁剪、缩放的模式 aspectFill mode参数详解 scaleToFill 缩放图片到固定的宽高 aspectFill 图片裁剪显示对应的宽高 auto 自动填充 宽度全显示 高度自适应居中显示 Tips(一定要看哦~) 本工具不考虑兼容性,如发现不兼容请使用google浏览器 painter现在只支持这几种图形,所以暂不支持圆,线等 如果编辑过程,一个元素被挡住了,无法操作,请选择对象并通过[ ]快捷键提高降低元素的层级 文字暂不支持直接缩放操作,因为文字大小和元素高度不容易计算,可以通过修改激活栏目maxLines lineHeight fontSize值来动态改变元素 如发现导出的代码一个元素被另一个元素挡住了,请手动调整元素的位置,json数组中元素越往后层级显示就越高,由于painter没有提供层级参数,所以目前只能这样做 本工具导出代码全是以px为单位,为什么不支持rpx, 因为painter在rpx单位下,阴影和边框宽会出现大小计算问题,由于原例子没有提供px生成图片方案,可以下载我这里修改过的demo>>Painter即可解决 文本宽度随着字数不同而动态变化,想在文本后面加个图标根据文本区域长度布局, 请参考Painter文档这块教程直接修改源码 由于本工具开发有些许难度,如出现bug,建议或者使用上的问题,请提issue,源码地址>>painter-custom-poster 海报贡献 如果你设计的海报很好看,并且愿意开源贡献,可以贡献你的海报代码和缩略图,例子代码文件在example中,按顺序排列,例如现在库里例子是example2.js,那你添加example3.js和example3.jpg图片,事例可以参考一下文件夹中源码,然后在index.js中导出一下 导出代码 代码不要格式化,会报错,请原模原样复制到json字段里 生成缩略图 刚开始我想在此工具中直接生成图片,但是由于浏览器图片跨域问题导致报错失败 所以请去小程序中生成保存图片,图片质量设置0.2,并去tinypng压缩一下图片 找到painter.js,替换下边这个方法,可以生成0.2质量的图片,代码如下 [代码] saveImgToLocal() { const that = this; setTimeout(() => { wx.canvasToTempFilePath( { canvasId: 'k-canvas', fileType: 'jpg', quality: 0.2, success: function(res) { that.getImageInfo(res.tempFilePath); }, fail: function(error) { console.error(`canvasToTempFilePath failed, ${JSON.stringify(error)}`); that.triggerEvent('imgErr', { error: error }); } }, this ); }, 300); } [代码] TODO 颜色值选择支持调色板工具 文字padding支持 缩放位置弹跳问题优化 假如需求大的话,支持其他几款插件库代码的生成 ~ 创作不易,如果对你有帮助,请给个星星 star✨✨ 谢谢 ~
2019-09-27 - 使用按钮获取用户头像的临时路径,为什么存储之后无法二次渲染展示头像?
基础库2.24.2,我在使用<button open-type="chooseAvatar" bind:chooseavatar="onChooseAvatar"></button> 获取用户头像后,返回的是一个临时路径,我把该头像的url路径渲染到了<image src="{{url}}"></image>标签,这个时候是可以展示头像的。 于是我尝试将该临时路径存到了云开发的数据库中,紧接着从云开发的数据库中将该头像的url路径获取到并渲染到另外一个<image src="{{url}}"></image>标签时却无法展示头像,标签都是一样的,路径url也是一样的,为什么存储之后再次渲染就无法展示头像了。
2022-07-23 - 如何实现快速生成朋友圈海报分享图
由于我们无法将小程序直接分享到朋友圈,但分享到朋友圈的需求又很多,业界目前的做法是利用小程序的 Canvas 功能生成一张带有小程序码的图片,然后引导用户下载图片到本地后再分享到朋友圈。相信大家在绘制分享图中应该踩到 Canvas 的各种(坑)彩dan了吧~ 这里首先推荐一个开源的组件:painter(通过该组件目前我们已经成功在支付宝小程序上也应用上了分享图功能) 咱们不多说,直接上手就是干。 [图片] 首先我们新增一个自定义组件,在该组件的json中引入painter [代码]{ "component": true, "usingComponents": { "painter": "/painter/painter" } } [代码] 然后组件的WXML (代码片段在最后) [代码]// 将该组件定位在屏幕之外,用户查看不到。 <painter style="position: absolute; top: -9999rpx;" palette="{{imgDraw}}" bind:imgOK="onImgOK" /> [代码] 重点来了 JS (代码片段在最后) [代码]Component({ properties: { // 是否开始绘图 isCanDraw: { type: Boolean, value: false, observer(newVal) { newVal && this.handleStartDrawImg() } }, // 用户头像昵称信息 userInfo: { type: Object, value: { avatarUrl: '', nickName: '' } } }, data: { imgDraw: {}, // 绘制图片的大对象 sharePath: '' // 生成的分享图 }, methods: { handleStartDrawImg() { wx.showLoading({ title: '生成中' }) this.setData({ imgDraw: { width: '750rpx', height: '1334rpx', background: 'https://qiniu-image.qtshe.com/20190506share-bg.png', views: [ { type: 'image', url: 'https://qiniu-image.qtshe.com/1560248372315_467.jpg', css: { top: '32rpx', left: '30rpx', right: '32rpx', width: '688rpx', height: '420rpx', borderRadius: '16rpx' }, }, { type: 'image', url: this.data.userInfo.avatarUrl || 'https://qiniu-image.qtshe.com/default-avatar20170707.png', css: { top: '404rpx', left: '328rpx', width: '96rpx', height: '96rpx', borderWidth: '6rpx', borderColor: '#FFF', borderRadius: '96rpx' } }, { type: 'text', text: this.data.userInfo.nickName || '青团子', css: { top: '532rpx', fontSize: '28rpx', left: '375rpx', align: 'center', color: '#3c3c3c' } }, { type: 'text', text: `邀请您参与助力活动`, css: { top: '576rpx', left: '375rpx', align: 'center', fontSize: '28rpx', color: '#3c3c3c' } }, { type: 'text', text: `宇宙最萌蓝牙耳机测评员`, css: { top: '644rpx', left: '375rpx', maxLines: 1, align: 'center', fontWeight: 'bold', fontSize: '44rpx', color: '#3c3c3c' } }, { type: 'image', url: 'https://qiniu-image.qtshe.com/20190605index.jpg', css: { top: '834rpx', left: '470rpx', width: '200rpx', height: '200rpx' } } ] } }) }, onImgErr(e) { wx.hideLoading() wx.showToast({ title: '生成分享图失败,请刷新页面重试' }) //通知外部绘制完成,重置isCanDraw为false this.triggerEvent('initData') }, onImgOK(e) { wx.hideLoading() // 展示分享图 wx.showShareImageMenu({ path: e.detail.path, fail: err => { console.log(err) } }) //通知外部绘制完成,重置isCanDraw为false this.triggerEvent('initData') } } }) [代码] 那么我们该如何引用呢? 首先json里引用我们封装好的组件share-box [代码]{ "usingComponents": { "share-box": "/components/shareBox/index" } } [代码] 以下示例为获取用户头像昵称后再生成图。 [代码]<button class="intro" bindtap="getUserInfo">点我生成分享图</button> <share-box isCanDraw="{{isCanDraw}}" userInfo="{{userInfo}}" bind:initData="handleClose" /> [代码] 调用的地方: [代码]const app = getApp() Page({ data: { isCanDraw: false }, // 组件内部关掉或者绘制完成需重置状态 handleClose() { this.setData({ isCanDraw: !this.data.isCanDraw }) }, getUserInfo(e) { wx.getUserProfile({ desc: "获取您的头像昵称信息", success: res => { const { userInfo = {} } = res this.setData({ userInfo, isCanDraw: true // 开始绘制海报图 }) }, fail: err => { console.log(err) } }) } }) [代码] 最后绘制分享图的自定义组件就完成啦~效果图如下: [图片] tips: 文字居中实现可以看下代码片段 文字换行实现(maxLines)只需要设置宽度,maxLines如果设置为1,那么超出一行将会展示为省略号 代码片段:https://developers.weixin.qq.com/s/J38pKsmK7Qw5 附上painter可视化编辑代码工具:点我直达,因为涉及网络图片,代码片段设置不了downloadFile合法域名,建议真机开启调试模式,开发者工具 详情里开启不校验合法域名进行代码片段的运行查看。 最后看下面大家评论问的较多的问题:downLoadFile合法域名在小程序后台 开发>开发设置里配置,域名为你图片的域名前缀 比如我文章里的图https://qiniu-image.qtshe.com/20190605index.jpg。配置域名时填写https://qiniu-image.qtshe.com即可。如果你图片cdn地址为https://aaa.com/xxx.png, 那你就配置https://aaa.com即可。
2022-01-20 - 获取小程序右上角菜单按钮信息错误?
获取小程序右上角菜单按钮信息偶尔结果为0,导致无法正确的设置自定义导航栏的高度 [图片]
2021-07-02 - 请问小程序的分享朋友圈,打开后页面正常显示,但是下面有提示一个前往小程序,不是很友好,不能直接操作?
请问小程序的分享朋友圈,打开后页面正常显示,但是下面有提示一个前往小程序,不是很友好,不能直接操作?
2022-05-30 - 30分钟实现小程序打卡签到送积分功能,提升用户留存利器!
前言在小程序运营中,为了提升用户留存,通常会自建打卡签到模块送积分功能。用户可以通过打卡签到获取到积分,然后积分可以兑换礼物,从而实现用户留存的效果。 效果在我的页面加入了一个签到入口,点击进入打卡签到页面。进入页面后会自动签到,引导用户进行二次提醒推送。可在后台灵活配置每天签到的积分及抽奖、奖品配置。 [图片] 今天我来带大家看看如果快速实现这个功能,在这里需要用到小程序的「单页模版」功能。来看看官方文档介绍: 目前「单页模版」处于内测阶段,我这个是内测版本,所以大家在开发工具中没有也是正常的。 小程序开发过程中,有很多通用的业务模块,例如:打卡签到、邀请有礼、趣味小游戏、运营 banner 配置等。这些模块业务模型具备通用性,但是前端每个小程序都有自己的样式设计。因此,每个小程序都需要重复性的进行开发。单页模板致力于帮助小程序开发者聚焦前端交互展示,无需关注于实现接口以及管理端。开通单页模板后,运营人员可直接在管理端配置新功能,小程序前端源码组件可导入到小程序内快速接入,也可以对前端组件进行二开以满足业务需求。 简单来说就是把整套打卡签到的前端代码和后端业务代码插入到你的小程序中,自己可以对前端组件做二次开发。 接下来我就带你来看看如何使用: 目前该功能还在内测阶段,你的微信开发功能没有是正常的。在这里我只是演示内测版本,后续云开发团队会持续更新,正式上线。第一步:开通单页模版开发设置面板点击扩展设置找到其他组件安装单页模版[图片] 当你开通完成后来,选中miniprogram文件夹右键呼出菜单,选择「配置单页模版」点击免费使用 [图片] 则会进入腾讯云登录界面,扫码验证选择自己的小程序即可。验证成功后则会进入单页模板控制台的小程序组件页面。 [图片] 第二步:后台配置规则点击「前往配置」则会进入签到配置页面,分别可以对:奖品管理、签到记录、签到规则、签到配置。 奖品管理奖品列表【新增、删除、导出】[图片] 奖品新增,在这里要注意填写奖品总数直接和剩余数量填写相等即可,当用户中奖后会自动将剩余数量-1[图片] 签到记录显示所有的签到记录、中奖记录,可以通过搜索条件对openid、连续天数、奖品描述进行过滤,而从显示对具体信息的快速查找。 [图片] 签到规则这里编辑的内容会在小程序页面用户点击签到规则时进行弹出显示。 [图片] 签到配置在这里可以配置签到活动的开关,签到的积分奖励,目前支持积分和虚拟物品,下周会上线实体物品。还有就是抽奖设置,包括概率和触发条件。 [图片] 当配置一切搞定之后,接下来我们来看下如何导入前端的签到打卡组件。 第三步:导入代码我们回到刚才的控制面板,切换到第二个菜单导入小程序组件,点击导入组件到IDE。 [图片] [图片] 点击查看详情,会有这个组件的介绍。 [图片] 点击导入IDE会直接导入在小程序的miniprogram目录下 [图片] 在这里如果你想直接导入在pages下面在进入单页模版配置的时候直接选择pages文件夹即可,或者移动文件夹到pages下面也可以,因为通常来说我们的页面都是放在pages下面来进行管理的。 看下代码以及预览效果 [图片] 该组件代码的目录: . ├── README.md ├── cloudfunctions # 云开发云函数目录 ├── miniprogram # 小程序前端代码 │ ├── miniprogram_npm │ ├── node_modules # 如果希望在 miniprogram/pages 下调用 @cloudbase/saas-module,需要在 miniprogram 下安装依赖 │ ├── pages │ │ ├── index │ │ ├── page_module_sign_up # 导入目录规范 page_module_${模块名} │ │ │ ├── README.md │ │ │ ├── components # 模块内的组件 │ │ │ ├── config.js │ │ │ ├── images │ │ │ ├── miniprogram_npm # 构建后的npm包,包含 @cloudbase/page-module │ │ │ ├── package.json │ │ │ └── pages # 模块内示例页面 │ │ └── demo │ └── sitemap.json ├── project.config.json 从代码上来看,在这里使用 page_module_sign_up/pages/index/index.wxml 是直接使用的自定义组件如果想要在前端进行样式的调整可以在 page_module_sign_up/components进行相关组件的代码修改。 [图片]这就是在文档中提到的可以对前端组件进行二开以满足业务需求。除了前端之外我们来看看后端配置 第四步:接口配置回到单页模版页面,选择接口自定义接口。 设置消息提醒首先选择一个具体的云环境进行部署云函数 [图片] 部署成功后进行代码下载 [图片] [图片] 这样你就可以得到发送订阅消息的代码,这里要注意就是要修改成自己的订阅模版。登录当前小程序的 mp 管理系统,从菜单中找到订阅消息-公共模版库-搜索【签到模版】-选中第一个进行选用。 [图片] 选用后三个字段 活动名称签到奖励温馨提示[图片] 提交即可,模版申请成功后会在我的模版中显示 [图片] 复制模版ID去小程序前端代码中进行替换,找到page_module_sign_up/config.js [图片] 替换temId和你想要用户点击推送订阅消息卡片跳转的页面page [图片] 除了小程序前端之外还需要在云函数page_module_tcb_sign_up/api/sendmsg.js进行修改。 [图片][图片] 在这里需要和管理后台的模版详情的字段序号对应上,默认下载的代码是 温馨提示:{{thing2.DATA}} 签到奖励:{{thing1.DATA}} 活动名称:{{thing3.DATA}} 代码中的逻辑是第二天发送模版消息,但是实际上测试肯定不会等到第二天要不然效率就太慢了,可以在 page_module_tcb_sign_up/api/set_remind.js 的主题逻辑run方法中修改 addDelayedFunctionTask 的 delayTime 参数,比如:我想点击10秒后发就可以设置为10。 [图片] addDelayedFunctionTask:用于延时调用云函数 delayTime:延迟时间,单位:秒,合法范围:6s-30天 设置完成后,点击明天提醒就可以立即收到模版消息推送了。 [图片] 在这里点击卡片的时候如果你签到打卡是一个新的页面会提醒找不到这个页面,因为发送推送消息默认环境跳转到正式环境去了,所以需要在 page_module_tcb_sign_up/api/sendmsg.js 改动下跳转环境。 [图片] subscribeMessage:发送订阅消息 miniprogramState:developer为开发版;trial为体验版;formal为正式版;默认为正式版 这样调试起来就不会有问题了,但是一定要记得上线后记得把时间和环境都调整过来。 接下来再来看看监听积分数据和奖品数据。 监听积分与奖品这两个监听相对简单,而且方法都是一样的所以就一起讲解。 监听积分发放需要在云函数下新建 /api/send_face_value.js 文件并实现该接口。 [图片] 监听奖品发放需要在云函数下新建 /api/send_prize.js 文件并实现该接口。 [图片] 具体实现,两种只是入参不一样其他都是一样的,以下为官方给出的代码示例。 const objCloud = require('wx-server-sdk'); /** * 具体的业务函数,在这里实现您发奖,发积分的逻辑,data 入参是固定的,出参必须遵循规范 * @param { object } data - 业务入参 * @returns { object } - 返回参数 * @returns { number } code 返回的状态标记,成功返回0, 非0代表错误 * @returns { string } msg 如果成功,则可以不返回,如果失败把相应的错误原因中文描述放在这里 * @returns { object } result 接口调用返回的信息 * */ module.exports = async (data) => { console.log("参数:", data); // 这里实现您具体的业务逻辑,例如发积分,发奖 return { code:0, msg:'suc', // 出参需要遵守自定接口规范 result: { sendResult: true }}; }; 我基于这个案例写了一个实际的业务,当用户签到获取到积分的时候我就存在我的积分表里面。 // 云函数入口文件 const cloud = require('wx-server-sdk') cloud.init() const db = cloud.database() module.exports = async (data) => { console.log("参数:", data); // 具体业务 const integral = await db.collection('integral') await integral.add({ data: data }) return { code: 0, msg: 'suc', result: { sendResult: true } }; }; 数据结构: [图片] 当然你自己也可以根据自己的业务来新增相关字段,做到这一步基于「单页模版」的签到打卡送积分功能就全部完成了。 注意我已经上线使用了1周了,发现了一个问题。 [图片] 用了单页模式新增了一个lowcode环境,所以在实现礼物接口和积分接口的时候需要指定下环境,要不然会出现概率报错找不到数据库。 [图片] [图片] 最后其实我很早就想实现自己的积分体系,但是由于时间不够的原因,导致迟迟没有去迈出第一步,正好遇到的单页模版的签到打卡送积分这个功能,于是在wilsonsliu的高度配合下完成了积分体系v1.0上线了。同时也非常期待后续的单页模块的邀请有礼、趣味小游戏、运营 banner 配置等上线。 只要你的小程序想要提高用户留存都需要搭建自己的积分体系,而单页模版的签到打卡功能可以成为用户获取积分的途径之一,要有积分才能去消费,具体消费积分场景可以配合积分商城或特殊功能上去进行消费。
2022-01-14 - 小程序签到打卡逻辑分析
小程序签到打卡逻辑分析 ~ 最近在实现小程序的积分签到打卡的功能,连续签到5天以上下发的积分是不一样的,所以有几个难点就是 1、用户进来后如何判断今天是连续第几题签到; 2、如果超过7天,后面怎么展示; ~ [图片] ~ [图片] ~ [图片] ~ 小程序签到打卡逻辑分析~ 最下面的图是支付宝的 支付宝积分签到 [图片] ~ 它1到7天,分别1到7积分,然后后边签到都是7积分 ~ 后面我会联系签到7天,每天在评论处打卡,还望各位提醒监督
2021-12-27