微信小程序支付
微信小程序支付
1、背景
因业务需要接入微信支付功能(客户端是微信小程序),因公司服务器版本较低,服务端采用.net framework 版本(并采用盛派微信sdk)
2、文档地址
1)小程序支付:
2)小程序调起支付api:
3)支付下单:
4)发起微信支付:https://developers.weixin.qq.com/miniprogram/dev/api/open-api/payment/wx.requestpayment.html
5)盛派sdk:
3、代码实现
小程序支付的交互图如下:
3.1 服务端实现
1)引入包:
2)注册:在 global.asmx 里进行注册
/* * co2net 全局注册开始 * 建议按照以下顺序进行注册 */ /* * co2net 是从 senparc.weixin 分离的底层公共基础模块,经过了长达 6 年的迭代优化。 * 关于 co2net 在所有项目中的通用设置可参考 co2net 的 sample: * https://github.com/senparc/senparc.co2net/blob/master/sample/senparc.co2net.sample.netcore/startup.cs */ //设置全局 debug 状态 var isglobaldebug = true; //全局设置参数,将被储存到 senparc.co2net.config.senparcsetting var senparcsetting = senparcsetting.buildfromwebconfig(isglobaldebug); //也可以通过这种方法在程序任意位置设置全局 debug 状态: //senparc.co2net.config.isdebug = isglobaldebug; //co2net 全局注册,必须!! iregisterservice register = registerservice.start(senparcsetting).usesenparcglobal(); #region 全局缓存配置(按需) #region 配置和使用 redis -- dpbmark redis //配置全局使用redis缓存(按需,独立) var redisconfigurationstr = senparcsetting.cache_redis_configuration; var useredis = !string.isnullorempty(redisconfigurationstr) && redisconfigurationstr != "redis配置"; if (useredis)//这里为了方便不同环境的开发者进行配置,做成了判断的方式,实际开发环境一般是确定的,这里的if条件可以忽略 { /* 说明: * 1、redis 的连接字符串信息会从 config.senparcsetting.cache_redis_configuration 自动获取并注册,如不需要修改,下方方法可以忽略 /* 2、如需手动修改,可以通过下方 setconfigurationoption 方法手动设置 redis 链接信息(仅修改配置,不立即启用) */ senparc.co2net.cache.redis.register.setconfigurationoption(redisconfigurationstr); //以下会立即将全局缓存设置为 redis senparc.co2net.cache.redis.register.usekeyvalueredisnow();//键值对缓存策略(推荐) //senparc.co2net.cache.redis.register.usehashredisnow();//hashset储存格式的缓存策略 //也可以通过以下方式自定义当前需要启用的缓存策略 //cachestrategyfactory.registerobjectcachestrategy(() => redisobjectcachestrategy.instance);//键值对 //cachestrategyfactory.registerobjectcachestrategy(() => redishashsetobjectcachestrategy.instance);//hashset } //如果这里不进行redis缓存启用,则目前还是默认使用内存缓存 #endregion // dpbmark_end #region 配置和使用 memcached -- dpbmark memcached //配置memcached缓存(按需,独立) var memcachedconfigurationstr = senparcsetting.cache_memcached_configuration; var usememcached = !string.isnullorempty(memcachedconfigurationstr) && memcachedconfigurationstr != "memcached配置"; if (usememcached) //这里为了方便不同环境的开发者进行配置,做成了判断的方式,实际开发环境一般是确定的,这里的if条件可以忽略 { /* 说明: * 1、memcached 的连接字符串信息会从 config.senparcsetting.cache_memcached_configuration 自动获取并注册,如不需要修改,下方方法可以忽略 /* 2、如需手动修改,可以通过下方 setconfigurationoption 方法手动设置 memcached 链接信息(仅修改配置,不立即启用) */ senparc.co2net.cache.memcached.register.setconfigurationoption(memcachedconfigurationstr); //以下会立即将全局缓存设置为 memcached senparc.co2net.cache.memcached.register.usememcachednow(); //也可以通过以下方式自定义当前需要启用的缓存策略 cachestrategyfactory.registerobjectcachestrategy(() => memcachedobjectcachestrategy.instance); } #endregion // dpbmark_end #endregion #region 注册日志(按需,建议) register.registertracelog(configweixintracelog);//配置tracelog #endregion /* 微信配置开始 * 建议按照以下顺序进行注册 */ //设置微信 debug 状态 var isweixindebug = true; //全局设置参数,将被储存到 senparc.weixin.config.senparcweixinsetting var senparcweixinsetting = senparcweixinsetting.buildfromwebconfig(isweixindebug); //也可以通过这种方法在程序任意位置设置微信的 debug 状态: //senparc.weixin.config.isdebug = isweixindebug; //微信全局注册,必须!! register.usesenparcweixin(senparcweixinsetting, senparcsetting) #region 注册公众号或小程序(按需) //注册公众号(可注册多个) -- dpbmark mp .registermpaccount(senparcweixinsetting, "【盛派网络小助手】公众号")// dpbmark_end //注册多个公众号或小程序(可注册多个) -- dpbmark miniprogram .registerwxopenaccount(senparcweixinsetting, "小程序名称")// dpbmark_end //除此以外,仍然可以在程序任意地方注册公众号或小程序: //accesstokencontainer.register(appid, appsecret, name);//命名空间:senparc.weixin.mp.containers #endregion // dpbmark_end #region 注册微信支付(按需) -- dpbmark tenpay //注册旧微信支付版本(v2)(可注册多个) .registertenpayold(senparcweixinsetting, "标记名称")//这里的 name 和第一个 registermpaccount() 中的一致,会被记录到同一个 senparcweixinsettingitem 对象中 //注册最新微信支付版本(v3)(可注册多个) .registertenpayv3(senparcweixinsetting, "标记名称")//记录到同一个 senparcweixinsettingitem 对象中 #endregion // dpbmark_end // dpbmark_end ; /* 微信配置结束 */
/// <summary> /// 配置微信跟踪日志 /// </summary> private void configweixintracelog() { //senparc.co2net.config.isdebug = false; //这里设为debug状态时,/app_data/weixintracelog/目录下会生成日志文件记录所有的api请求日志,正式发布版本建议关闭 senparc.weixin.weixintrace.sendcustomlog("系统日志", "系统启动");//只在senparc.weixin.config.isdebug = true的情况下生效 //自定义日志记录回调 senparc.weixin.weixintrace.onlogfunc = () => { //加入每次触发log后需要执行的代码 }; //当发生基于weixinexception的异常时触发 senparc.weixin.weixintrace.onweixinexceptionfunc = ex => { //加入每次触发weixinexceptionlog后需要执行的代码 //发送模板消息给管理员 var eventservice = new eventservice(); eventservice.configonweixinexceptionfunc(ex); }; }
3)统一下单:
*** 因实际代码设计隐私问题,因此剔除了,如有问题请联系我。
public async task<actionresult> getwxopenprepayid(string sessionid,string cost) { try { var sessionbag = sessioncontainer.getsession(sessionid); var openid = sessionbag.openid; //生成订单10位序列号,此处用时间和随机数生成,商户根据自己调整,保证唯一 var sp_billno = string.format("{0}{1}{2}", "商户号" /*10位*/, systemtime.now.tostring("yyyymmddhhmmss"), tenpayv3util.buildrandomstr(6)); var timestamp = tenpayv3util.gettimestamp(); var noncestr = tenpayv3util.getnoncestr(); var price = convert.toint32(convert.todecimal(cost) * 100);//单位:分 var xmldatainfo = new tenpayv3unifiedorderrequestdata( "小程序appid", "小程序商户号", body, sp_billno, price, "127.0.0.1", "回调地址", tenpayv3type.jsapi, openid, "小程序商户key", noncestr, attach: "附加数据"); var result = tenpayv3.unifiedorder(xmldatainfo);//调用统一订单接口 // weixintrace.sendcustomlog("统一订单接口调用结束", "请求:" + xmldatainfo.tojson() + "\r\n\r\n返回结果:" + result.tojson()); var packagestr = "prepay_id=" + result.prepay_id; //var cachestrategy = cachestrategyfactory.getobjectcachestrategyinstance(); //cachestrategy.set($"wxopenunifiedorderrequestdata-{openid}", xmldatainfo, timespan.fromdays(4));//3天内可以发送模板消息 //cachestrategy.set($"wxopenunifiedorderresultdata-{openid}", result, timespan.fromdays(4));//3天内可以发送模板消息 return tosuccess(data: new { result.prepay_id, appid = "小程序appid", timestamp, noncestr, tradeid = sp_billno, package = packagestr, //signtype = "md5", paysign = tenpayv3.getjspaysign("小程序appid", timestamp, noncestr, packagestr, "小程序支付key") }); } catch (exception ex) { return toerror(msg: ex.message); }
*** 这个接口 在什么地方用呢(a:在小程序客户端调用微信支付的时候需要用到【而且是必须的】,也就是下午的 小程序客户端调用:wx.requestpayment 的时候)
4)回调地址(支付结果通知):
*** 需要注意的地方已 标出了。(值得注意的就是这个地址必须是外网能访问到的)
代码实现:
public actionresult inquirynotify() { try { var reshandler = new responsehandler(null); var return_code = reshandler.getparameter("return_code"); var return_msg = reshandler.getparameter("return_msg"); if (return_code != "success") return payresultnotify(); var attach = reshandler.getparameter("attach"); if (string.isnullorempty(attach)) return payresultnotify(); //这里请签名验证,并校验返回的订单金额是否与商户侧的订单金额一致 var cost = reshandler.getparameter("total_fee");//金额 var tradeid = reshandler.getparameter("out_trade_no");//商户订单号 //验证支付状态与金额 //业务逻辑处理,采用异步回调 return payresultnotify("success", "ok"); } catch (exception ex) { return payresultnotify(); } } private actionresult payresultnotify(string returncode = "fail", string returnmsg = "wrong") { var xml = $@"<xml> <return_code><![cdata[{returncode}]]></return_code> <return_msg><![cdata[{returnmsg}]]></return_msg> </xml>"; return content(xml, "text/xml"); }
3.2、客户端实现(小程序)
/** * 支付 */ bindpay: function(e) { let that = this let url = wx.getstoragesync(config.domainname) url += '服务端接口地址' let data = { sessionid: wx.getstoragesync(config.sessionid), cost: "支付金额", } wxrequest.getrequest(url, data).then((res) => { if (res.data.success) { let payinfo = res.data.data wx.requestpayment({ timestamp: payinfo.timestamp, noncestr: payinfo.noncestr, package: payinfo.package, paysign: payinfo.paysign, signtype: 'md5', success: function(res) { /*这里一定要注意,这里的方法有可能会不执行(支付完成后 用户在不点击 支付完成页面底部的“完成”按钮时 这里的方法是不会执行到的,因此这里请不要写业务逻辑代码)*/ that.querypayresult(payinfo.tradeid) }, fail: function(res) {
/*用户取消支付后 会执行这里的方面*/ that.paycancel(payinfo.tradeid) } }) } else { app.showtoast(res.data.msg) } }) }
至此,小程序的支付就完成了。感谢 盛派网络 对微信相关接口的封装,用起来很方便
说明:
1:如有疑问,可以与我取得联系
2:已官方文档为主,很有可能过些时间后文档及sdk会发生变化
3:官方文档已在上文中给出
4:文章首发于公众号
5:服务端使用的小程序包是盛派的sdk()github:https://github.com/senparc/weixinmpsdk
6:.net core 类似,有何疑问也可以与我取得联系
如果对您有帮助,点个推荐呗!
上一篇: java 基础排序(冒泡、插入、选择、快速)算法回顾
下一篇: JDK的环境配置