diff --git a/app.json b/app.json index fb2e7e5..ff9cfdf 100644 --- a/app.json +++ b/app.json @@ -12,5 +12,8 @@ "enablePullDownRefresh": false, "navigationStyle": "custom" }, - "sitemapLocation": "sitemap.json" + "sitemapLocation": "sitemap.json", + "navigateToMiniProgramAppIdList": [ + "wx959c8c1fb2d877b5" + ] } \ No newline at end of file diff --git a/components/payjs/payjs.js b/components/payjs/payjs.js new file mode 100644 index 0000000..e4b4990 --- /dev/null +++ b/components/payjs/payjs.js @@ -0,0 +1,108 @@ +// componenets/payjs/payjs.js +Component({ + /** + * 组件的属性列表 + */ + properties: { + params: { // 支付订单参数 + type: Object, + value: null + }, + envVersion: { + type: String, + value: "release" + } + }, + + /** + * 组件的初始数据 + */ + data: { + showPayModal: false, + paying: false + }, + + /** + * 组件的方法列表 + */ + methods: { + setPaying(newPayingData) { + this.setData({ + paying: newPayingData + }) + this.triggerEvent('dataChange', { paying: newPayingData }) + }, + onTapCancel () { + // 用户点击了支付组件外的地方(灰色地方) + console.log('[PAYJS] 跳转到 PAYJS 小程序失败 - 用户点击了提醒窗体以外的地方') + this.triggerEvent('fail', { navigateSuccess: false }) + this.triggerEvent('complete') + }, + navigateSuccess () { + console.log('[PAYJS] 跳转到 PAYJS 小程序成功') + // 成功跳转:标记支付中状态 + this.setPaying(true) + }, + navigateFail (e) { + // 跳转失败 + console.log('[PAYJS] 跳转到 PAYJS 小程序失败 - 失败回调', e) + this.triggerEvent('fail', { navigateSuccess: false, info: e }) + this.triggerEvent('complete') + } + }, + + /** + * 组件生命周期 + */ + lifetimes: { + attached() { + this.setPaying(false) + if (!this.data.params) { + console.error('[PAYJS] 跳转到 PAYJS 小程序失败 - 错误:没有传递跳转参数', r) + this.triggerEvent('fail', { error: true, navigateSuccess: false }) + this.triggerEvent('complete') + } + + // 监听 app.onShow 事件 + wx.onAppShow(appOptions => { + if (!this.data.paying) return; + + // 恢复支付前状态 + this.setPaying(false) + + if (appOptions.scene === 1038 && appOptions.referrerInfo.appId === 'wx959c8c1fb2d877b5') { + // 来源于 PAYJS 小程序返回 + console.log('[PAYJS] 确认来源于 PAYJS 回调返回') + let extraData = appOptions.referrerInfo.extraData + if (extraData.success) { + this.triggerEvent('success', extraData) + this.triggerEvent('complete') + } else { + this.triggerEvent('fail', { navigateSuccess: true, info: extraData }) + this.triggerEvent('complete') + } + } + }) + + // 尝试直接跳转到 PAYJS 发起小程序支付 + wx.navigateToMiniProgram({ + appId: 'wx959c8c1fb2d877b5', + path: 'pages/pay', + extraData: this.data.params, + envVersion: this.data.envVersion, + success: r => { + console.log('[PAYJS] 跳转到 PAYJS 小程序成功', r) + // 成功跳转:标记支付中状态 + this.setPaying(true) + }, + fail: e => { + // 跳转失败:弹出提示组件引导用户跳转 + console.log('[PAYJS] 跳转到 PAYJS 小程序失败 - 准备弹窗提醒跳转', e) + this.setData({ + showPayModal: true + }) + } + }) + } + } +}) diff --git a/components/payjs/payjs.json b/components/payjs/payjs.json new file mode 100644 index 0000000..e8cfaaf --- /dev/null +++ b/components/payjs/payjs.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} \ No newline at end of file diff --git a/components/payjs/payjs.wxml b/components/payjs/payjs.wxml new file mode 100644 index 0000000..9c90687 --- /dev/null +++ b/components/payjs/payjs.wxml @@ -0,0 +1,5 @@ + + + 支付需要跳转到第三方平台进行 + 确认跳转 + \ No newline at end of file diff --git a/components/payjs/payjs.wxss b/components/payjs/payjs.wxss new file mode 100644 index 0000000..87c56cd --- /dev/null +++ b/components/payjs/payjs.wxss @@ -0,0 +1,44 @@ +.bg { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + + background-color: black; + opacity: 0.5; +} + +.button { + background: none; +} + +.button::after { + border: none; +} + +.modal { + position: fixed; + left: 10vw; + top: 30vh; + width: 80vw; + height: 20vh; + + background-color: white; + border-radius: 5rpx; + + text-align: center; + line-height: 10vh; +} + +.modal .content { + height: 10vh; + color: #9d9d9d; + font-size: 28rpx; +} + +.modal .button { + height: 10vh; + color: #3cc51f; + font-size: 36rpx; +} \ No newline at end of file diff --git a/lib/api.js b/lib/api.js index 44dad23..f1eccf0 100644 --- a/lib/api.js +++ b/lib/api.js @@ -96,7 +96,6 @@ const delCode = (id, callBack) => { sign: util.getMD5(id) }, success: function (res) { - console.log(res) if (res.data.errcode !== 200) { wx.hideLoading(); wx.showToast({ diff --git a/lib/util.js b/lib/util.js index 624eb63..6f6feac 100644 --- a/lib/util.js +++ b/lib/util.js @@ -38,8 +38,8 @@ const getMD5 = obj => { const checkFlag = (arg,flag, callBack) => { flag-- - console.log(arg) - console.log(flag) + // console.log(arg) + // console.log(flag) if(flag == 0) { callBack && callBack() } @@ -49,22 +49,64 @@ const checkFlag = (arg,flag, callBack) => { const checkInput = (type,content) => { switch(type) { case 'alcode': { - return 'alcode' + if(content.indexOf('qr.alipay.com') != -1) { + return content + } else if (content.indexOf('QR.ALIPAY.COM') != -1) { + return content + } else { + wx.showToast({ + title: '请扫描合格的支付宝收款码', + icon: 'none' + }) + return '' + } } case 'qqcode': { - return 'qqcode' + if(content.indexOf('qianbao.qq.com') != -1) { + return content + } else { + wx.showToast({ + title:'请扫描合格的QQ收款码', + icon: 'none' + }) + return '' + } } case 'wxcode': { - return 'wxcode' + if(content.indexOf('wxp') != -1) { + return content + } else if(content.indexOf('payapp.weixin.qq.com') != -1) { + return content + } else { + wx.showToast({ + title: '请扫描合格的微信收款码', + icon: 'none' + }) + return '' + } } case 'username': { - return 'username' + content = content.replace(/"/gi,'') + content = content.replace(/ /gi,'') + content = content.replace(/'/gi,'') + content = content.replace(/\$/gi,'') + return content } case 'node': { - return 'node' + content = content.replace(/"/gi,'') + content = content.replace(/'/gi,'') + content = content.replace(/\$/gi,'') + content = content.replace(/ /gi,'') + return content } case 'month': { - return 'month' + content = parseInt(content) + if(content) { + if(0 < content && content < 13) { + return content + } + } + return '' } } } @@ -104,7 +146,7 @@ const checkSubmit = (c) => { data['openId'] = c.openId data['node'] = c.node data['timeout'] = ((new Date()).getTime() + 1000*60*60*24*30*c.month).toString() - var totalFee = c.month * 0.5 + var totalFee = c.month * 0.5 * 100 return { data: data, totalFee: totalFee diff --git a/pages/home/home.js b/pages/home/home.js index 92179aa..4853be7 100644 --- a/pages/home/home.js +++ b/pages/home/home.js @@ -34,7 +34,10 @@ Page({ node: '', timeout: '', month: 0 - } + }, + orderParams: {}, // 支付发起参数 + preparePay: false, // 控制 payjs 组件的创建与销毁 + paying: false, // 可选:如需知晓用户是否「已经跳转到了 PAYJS 小程序还未返回」的状态则可通过事件处理函数监听事件内的 paying 数据 }, /** @@ -105,23 +108,72 @@ Page({ showUtils: function(e) { var type = e.currentTarget.dataset.type if(this.data.animating) return + if(type == 'maked' && this.data.codes.length == 0) { + wx.showToast({ + title: '空空如也呢', + icon: 'none' + }) + return + } var that = this this.data.animating = true if(this.data.show) { //拉起主页 - this.data.codeSrc = null - let display = this.data.display - for(var i in display) { - if(i != 'createBox') display[i] = false + // 如果是start页先弹窗问是否返回, 是就清除数据 + if(this.data.display['start']) { + wx.showModal({ + title: '警告', + content: '此时返回将清除已录入的数据', + success (res) { + if (res.confirm) { + that.data.createCode = { + wxcode: '', + qqcode: '', + alcode: '', + username: '', + openId: wx.getStorageSync('openid'), + node: '', + timeout: '', + month: 0 + } + that.setData({ + createCode: that.data.createCode, + orderParams:{} + }) + that.data.codeSrc = null + let display = that.data.display + for(var i in display) { + if(i != 'createBox') display[i] = false + } + display['createBox'] = true + that.setData({ + display: display, + show:false + }) + setTimeout(function(){ + that.data.animating = false + },500) + } else if (res.cancel) { + console.log('用户点击取消') + that.data.animating = false + } + } + }) + } else { + this.data.codeSrc = null + let display = this.data.display + for(var i in display) { + if(i != 'createBox') display[i] = false + } + display['createBox'] = true + this.setData({ + display: display, + show:false + }) + setTimeout(function(){ + that.data.animating = false + },500) } - display['createBox'] = true - this.setData({ - display: display, - show:false - }) - setTimeout(function(){ - that.data.animating = false - },500) } else { //拉起utils页 //阻止点击返回键 @@ -143,12 +195,19 @@ Page({ }) that.data.animating = false if(type == 'example') that.rouseQRcode(true,null) + if(type == 'start') { + wx.showModal({ + title: '提示', + content: '由于技术原因,将于下个版本支持苹果手机', + showCancel: false + }) + } },500) } }, //处理数据 - manageCodes:function() { + manageCodes:function(callBack) { let codes = wx.getStorageSync('codes') for( let i in codes) { let end = this.TimeDown(codes[i]['timeout']) @@ -157,6 +216,7 @@ Page({ this.setData({ codes: codes }) + callBack && callBack() }, //时间处理辅助轮 TimeDown: function(arg) { @@ -180,7 +240,7 @@ Page({ //删除二维码 delCode:function(e) { var id = e.currentTarget.dataset.id - console.log(id) + var that = this wx.showModal({ title: '警告', content: '此操作不可恢复,并且不会返还费用', @@ -191,8 +251,14 @@ Page({ mask:true }) api.delCode(id,function(){ - wx.showToast({ - title: "删除成功" + //刷新并处理数据 + api.reflash(wx.getStorageSync('openid'),function(info) { + wx.setStorageSync('codes', info); + that.manageCodes(function(){ + wx.showToast({ + title: "删除成功" + }) + }) }) }) } else if (res.cancel) { @@ -216,6 +282,7 @@ Page({ title: '绘制中', mask:true }) + this.data.animating = true if(isExample) { var status = { wx: true, @@ -236,7 +303,6 @@ Page({ this.setData({ display:display }) - console.log(this.data.display) // data 的内容是一条记录的row var id = data._id['$oid'] var text = data.username @@ -306,13 +372,13 @@ Page({ destHeight: 2108, canvasId: 'qrcode', success: function (res) { - console.log(res) that.data.codeSrc = res.tempFilePath - console.log(that.data.codeSrc) wx.hideLoading() + that.data.animating = false wx.showModal({ title: '提示', content: '单击图片即可预览,预览界面长按即可保存', + showCancel: false }) } }) @@ -325,7 +391,6 @@ Page({ }, preview: function() { - console.log(this.data.codeSrc) wx.previewImage({ urls: [this.data.codeSrc], }) @@ -340,18 +405,20 @@ Page({ //utils里边写个校验,返回true false //写入 var check = util.checkInput(type,res.result) - console.log(check) - that.data.createCode[type] = res.result - that.setData({ - createCode: that.data.createCode - }) - wx.showToast({ - title: '录入成功', - icon: 'success', - }) + if(check) { + that.data.createCode[type] = check + that.setData({ + createCode: that.data.createCode + }) + wx.showToast({ + title: '录入成功', + icon: 'success', + }) + }else { + return + } }, fail (err) { - console.log(err) wx.showToast({ title: '扫码失败', icon: 'none', @@ -365,23 +432,139 @@ Page({ var type = e.currentTarget.dataset.type var value = e.detail.value var check = util.checkInput(type,value) - console.log(check) - this.data.createCode[type] = value - this.setData({ - createCode: this.data.createCode - }) - //utils校验,返回的东西直接装进createCode - //return value - console.log(e) + if(check) { + this.data.createCode[type] = check + this.setData({ + createCode: this.data.createCode + }) + } + return check }, //提交 submit: function() { + if (Object.keys(this.data.orderParams).length != 0) { + this.setData({ + preparePay: true + }) + return + } + var that = this; var res = util.checkSubmit(this.data.createCode) if (!res) return console.log(res) - //showLoading - //api.newOrder + wx.showLoading({ + title: '订单创建中', + mask:true + }) + api.createOder(res.totalFee,res.data,function(parms) { + that.setData({ + preparePay: true, + orderParams: parms + }) + wx.hideLoading() + }) + + + }, + + /** + * 支付成功的事件处理函数 + * + * res.detail 为 PAYJS 小程序返回的订单信息 + * + * 可通过 res.detail.payjsOrderId 拿到 PAYJS 订单号 + * 可通过 res.detail.responseData 拿到详细支付信息 + */ + bindPaySuccess (res) { + wx.showLoading({ + title: '订单查询中', + mask:true + }); + var that = this + console.log('success', res) + console.log('[支付成功] PAYJS 订单号:', res.detail.payjsOrderId) + console.log('outTradeNo', res.detail.outTradeNo) + // 进行checkOrder + api.checkOrder(res.detail.outTradeNo,function(order_id) { + //重置createCode + var data = that.data.createCode + data._id = {} + data._id['$oid'] = order_id + that.data.createCode = { + wxcode: '', + qqcode: '', + alcode: '', + username: '', + openId: wx.getStorageSync('openid'), + node: '', + timeout: '', + month: 0 + } + that.setData({ + createCode: that.data.createCode, + orderParams:{} + }) + //调起绘制二维码的界面 + wx.hideLoading() + that.rouseQRcode(false,data) + }) + + //刷新并处理数据 + api.reflash(wx.getStorageSync('openid'),function(info) { + wx.setStorageSync('codes', info); + that.manageCodes() + }) + }, + /** + * 支付失败的事件处理函数 + * + * res.detail.error 为 true 代表传入小组件的参数存在问题 + * res.detail.navigateSuccess 代表了是否成功跳转到 PAYJS 小程序 + * res.detail.info 可能存有失败的原因 + * + * 如果下单成功但是用户取消支付则可在 res.detail.info.payjsOrderId 拿到 payjs 订单号 + */ + bindPayFail (res) { + console.log('fail', res) + if (res.detail.error) { + // 后端订单生成完成 + // !!!!苹果手机会卡在这里!!! + console.error('发起支付失败', res.detail.info) + + } else if (res.detail.navigateSuccess) { + // 跳转到了付款界面但是没付款 + console.log('[取消支付] PAYJS 订单号:', res.detail.info.payjsOrderId) + + } else { + // 用户放弃了付款,没跳转,再次跳转还可以进行支付 + // 目测啥都不用干 + } + }, + /** + * 支付完毕的事件处理函数 + * + * 无论支付成功或失败均会执行 + * 应当在此销毁 payjs 组件 + */ + bindPayComplete () { + console.log('complete') + this.setData({ + preparePay: false, // 销毁 payjs 组件 + }) + }, + /** + * 【可选】组件内部数据被修改时的事件 + * + * 当前仅用于监听 paying 数据 + * 当用户跳转到 PAYJS 小程序并等待返回的过程中 paying 值为 true + */ + bindDataChange (res) { + if (res.detail.paying) { + this.setData({ + paying: res.detail.paying + }) + } } }) diff --git a/pages/home/home.json b/pages/home/home.json index 8835af0..a1bde67 100644 --- a/pages/home/home.json +++ b/pages/home/home.json @@ -1,3 +1,5 @@ { - "usingComponents": {} + "usingComponents": { + "payjs": "../../components/payjs/payjs" + } } \ No newline at end of file diff --git a/pages/home/home.wxml b/pages/home/home.wxml index 6f9406c..4b4c697 100644 --- a/pages/home/home.wxml +++ b/pages/home/home.wxml @@ -76,16 +76,16 @@ 标题 - + 备注 - + - 使用时长(月) + 使用时长(0.5元/月) @@ -106,4 +106,5 @@ {{codes.length}} 个 - \ No newline at end of file + + \ No newline at end of file diff --git a/pages/home/home.wxss b/pages/home/home.wxss index fb842c1..79fe305 100644 --- a/pages/home/home.wxss +++ b/pages/home/home.wxss @@ -95,6 +95,21 @@ page { justify-content: flex-end; } +.main .main-box .main-warning text { + width: 30rpx; + height: 30rpx; + background-image: url("data:image/svg+xml,%3Csvg t='1573649986989' class='icon' viewBox='0 0 1025 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' p-id='2146' width='200' height='200'%3E%3Cpath d='M999.126733 754.39104L646.123213 118.1696c-31.46752-56.61696-80.42496-89.09824-134.30784-89.09824-53.9648 0-102.93248 32.52224-134.30784 89.20064L24.872653 754.33984C-6.103347 810.20928-8.212787 869.08928 19.066573 915.87584c27.36128 46.92992 79.6672 73.85088 143.52384 73.85088h698.89024c63.88736 0 116.19328-26.92096 143.4624-73.7792 27.33056-46.72512 25.21088-105.61536-5.81632-161.55648zM924.548813 869.0688c-10.20928 17.53088-33.18784 27.58656-63.06816 27.58656H162.590413c-29.87008 0-52.86912-10.07616-63.11936-27.648-10.3424-17.73568-7.85408-43.07968 6.79936-69.5296l352.64512-636.08832c14.51008-26.20416 33.80224-41.23648 52.89984-41.23648 19.08736 0 38.38976 15.03232 52.95104 41.216l352.9728 636.17024c14.68416 26.4704 17.17248 51.79392 6.8096 69.5296z' fill='%23F15533' p-id='2147'%3E%3C/path%3E%3Cpath d='M512.020173 634.07104a46.53056 46.53056 0 0 0 46.5408-46.44864V370.25792a46.52032 46.52032 0 0 0-46.5408-46.44864 46.53056 46.53056 0 0 0-46.5408 46.44864v217.35424a46.53056 46.53056 0 0 0 46.5408 46.45888zM512.020173 680.6016a62.09536 62.09536 0 0 0-62.0544 62.0544c0 34.2016 27.78112 62.0544 62.0544 62.0544 34.2016 0 62.0544-27.78112 62.0544-62.0544 0-34.2016-27.78112-62.0544-62.0544-62.0544z' fill='%23F15533' p-id='2148'%3E%3C/path%3E%3C/svg%3E"); + background-size: 100% 100%; + display: inline-block; + margin-right: 20rpx; +} + +.main .main-box .main-warning span { + font-size: 25rpx; + line-height: 50rpx; +} + + .main .create-warp { background: #fff; border-radius: 20rpx;