在创建微搭 APIS 时,如果选择开放服务中的微信支付-小程序服务时,该服务会要求用户填写统一下单和申请退款的通知回调地址,这两个回调地址要求填入可在公网访问的 HTTP 请求地址,而在用户的业务中,这两个回调地址不一定都是现有的,可能还需要一定的开发工作,导致在小程序里接入微信支付时门槛较高。本篇文档主要介绍如何使用 云开发 HTTP 访问服务能力,快速生成通知回调地址。
什么是 HTTP 访问服务能力?HTTP 访问能力是旨在让用户可以通过简单的 HTTP 请求方式来访问用户部署在云开发环境里的云上资源,例如云函数、静态网站托管以及云托管服务等云上资源。可以通过资源绑定的方式,将云开发环境里的云上资源绑定到某个特定的 URL 上,之后只需向该 URL 发送简单的
HTTP
请求即可访问到云上资源,从而避免了使用各种语言的 SDK 带来的繁琐以及语言版本特性等诸多原因带来的问题。了解了以上内容后,接下来详细介绍通过 HTTP 访问服务和云函数这两个现有能力来快速生成所需要的通知回调地址。
1. 前期准备
步骤1:创建云函数
如上文所述,由于服务要求填入统一下单和申请退款两个通知回调地址,所以需要创建两个云函数。进入云开发环境后,在左侧导航栏选择云函数,在右侧云函数页面中单击新建云函数,开始新建云函数。
如下图所示,创建两个云函数,分别为:
order:用于处理统一下单通知回调。
refund:用于处理申请退款通知回调。
云函数代码先使用默认写好的示例代码即可,填写好云函数名称后直接单击下一步,确定后即可完成创建。
步骤2:绑定 HTTP 访问路径
创建好云函数后,接下来开始给云函数绑定 HTTP 访问路径。单击左侧导航栏,选择访问服务,并单击右侧页面中的新建,开始新建访问路径,如下:
首先,将处理统一下单通知回调的云函数 order 的 HTTP 触发路径设置为
/order
,并单击确定,如下:
同理,将处理申请退款通知回调的云函数 refund 的 HTTP 触发路径设置为
/refund
,并单击确定。这样就创建好了两个云函数对应的两条 HTTP 访问路径,如下:
步骤3:测试访问服务是否可用
服务绑定完成之后,需要简单测试下服务是否已经可用。单击 HTTP 访问服务中的触发路径,如下:
此时浏览器会打开一个这样的地址:
https://xxx.ap-shanghai.app.tcloudbase.com/order
和 https://xxx.ap-shanghai.app.tcloudbase.com/refund
。这两个地址即是所需要的统一下单和申请退款的通知回调地址。当页面显示响应内容时,表示服务已经可用,如果页面显示报错,则表示服务正在启动,大概 5 分钟后重试即可。2. 使用回调地址
如上文所述,准备好的两个用于处理统一下单和申请退款的通知回调地址,分别是:
统一下单:
https://xxx.ap-shanghai.app.tcloudbase.com/order
申请退款:
https://xxx.ap-shanghai.app.tcloudbase.com/refund
将其填入如下位置即可。
当在小程序里调用微信支付完成下单或申请退款操作后,微信支付会以请求回调通知地址的方式同步支付结果,接下来,看看如何在云函数里处理微信支付的请求。
步骤1:处理统一下单通知回调请求
当微信支付回调
https://xxx.ap-shanghai.app.tcloudbase.com/order
地址的时候,会将请求打返回给该地址绑定的云函数里面,所以需要在云函数里处理回调请求。在云开发控制台中打开 order 云函数,进入到云函数代码编辑页面,填入以下代码:
'use strict';const crypto = require("crypto")exports.main = async (event, context) => {/*** 从event.body里获取微信支付请求通知回调时携带的参数,参数说明详见* https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/payment-notice.html#%E9%80%9A%E7%9F%A5%E5%8F%82%E6%95%B0*/const {id, // 通知的唯一IDcreate_time, //通知创建的时间resource_type, // 通知的资源数据类型event_type, // 通知的类型summary, // 回调摘要resource, // 通知资源数据} = JSON.parse(event.body)const key = `xxxxxx`; // APIv3 密钥const nonce = resource.nonce; // 加密使用的随机串const associated_data = resource.associated_data; // 加密用的附加数据let ciphertext = resource.ciphertext; // 加密体ciphertext = Buffer.from(ciphertext, 'base64');const authTag = ciphertext.slice(ciphertext.length - 16);const data = ciphertext.slice(0, ciphertext.length - 16);const decipher = crypto.createDecipheriv('aes-256-gcm', key, nonce);decipher.setAuthTag(authTag);decipher.setAAD(Buffer.from(associated_data));const decoded = decipher.update(data, null, 'utf8');decipher.final();const decodedData = JSON.parse(decoded); //解密后的数据console.log('解密后的数据', decodedData)return null};
可以从 event.body 里获取微信支付请求通知回调时携带的参数,参数示例如下:
{id: "a3fc0826-1b9a-5f21-b918-4abb937468fc",create_time: "2023-10-18T20:46:21+08:00",resource_type: "encrypt-resource",event_type: "TRANSACTION.SUCCESS",summary: "支付成功",resource: {original_type: "transaction",algorithm: "AEAD_AES_256_GCM",ciphertext:"....",associated_data: "transaction",nonce: "9pZlUiqMsfi2",},}
{mchid: '161375xxxx',appid: 'wx480c825d7xxxxxx',out_trade_no: 'weda_2023-10-18-10117',transaction_id: '420000199120231018419562xxxx',trade_type: 'JSAPI',trade_state: 'SUCCESS',trade_state_desc: '支付成功',bank_type: 'OTHERS',attach: '',success_time: '2023-10-18T20:46:21+08:00',payer: { openid: '...' },amount: { total: 1, payer_total: 1, currency: 'CNY', payer_currency: 'CNY' }}
步骤2:处理申请退款通知回调请求
同理,在 refund 云函数里处理申请退款通知回调请求,在云开发控制台中打开 refund 云函数,进入到云函数代码编辑页面,仍旧填入以下代码:
'use strict';const crypto = require("crypto")exports.main = async (event, context) => {/*** 从event.body里获取微信支付请求通知回调时携带的参数,参数说明详见* https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/payment-notice.html#%E9%80%9A%E7%9F%A5%E5%8F%82%E6%95%B0*/const {id, // 通知的唯一IDcreate_time, //通知创建的时间resource_type, // 通知的资源数据类型event_type, // 通知的类型summary, // 回调摘要resource, // 通知资源数据} = JSON.parse(event.body)const key = `xxxxxx`; // APIv3 密钥const nonce = resource.nonce; // 加密使用的随机串const associated_data = resource.associated_data; // 加密用的附加数据let ciphertext = resource.ciphertext; // 加密体ciphertext = Buffer.from(ciphertext, 'base64');const authTag = ciphertext.slice(ciphertext.length - 16);const data = ciphertext.slice(0, ciphertext.length - 16);const decipher = crypto.createDecipheriv('aes-256-gcm', key, nonce);decipher.setAuthTag(authTag);decipher.setAAD(Buffer.from(associated_data));const decoded = decipher.update(data, null, 'utf8');decipher.final();const decodedData = JSON.parse(decoded); //解密后的数据console.log('解密后的数据', decodedData)return null};
从 event.body 里获取微信支付请求通知回调时携带的参数,参数示例如下:
{id: "e59bba96-1920-56f4-aac9-287eacde4510",create_time: "2023-10-18T21:12:51+08:00",resource_type: "encrypt-resource",event_type: "REFUND.SUCCESS",summary: "退款成功",resource: {original_type: "refund",algorithm: "AEAD_AES_256_GCM",ciphertext: "...",associated_data: "refund",nonce: "MHDDHcJ3FuvG",},}
{mchid: '161375xxxx',out_trade_no: 'weda_2023-10-18-10117',transaction_id: '42000019912023101841956xxxx',out_refund_no: 'weda_2023-10-18-10117',refund_id: '50302107462023101827007675043',refund_status: 'SUCCESS',success_time: '2023-10-18T21:12:51+08:00',amount: { total: 1, refund: 1, payer_total: 1, payer_refund: 1 },user_received_account: '支付用户零钱'}
总结
以上,就是使用 HTTP 访问服务能力快速生成通知回调地址的所有步骤,接下来,就可以在云函数里根据微信支付的处理状态来编写业务代码,对业务进行下一步处理,如需操作微搭数据源信息,请参见 管理端-数据源 开发文档。