pay.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. <!-- 支付页面 -->
  2. <template>
  3. <view class="container">
  4. <view class="order_info flex_column_start_start">
  5. <view class="item b_b flex_row_between_center">
  6. <text class="tit">{{$L('订单编号')}}</text>
  7. <text class="order_sn">{{payInfo.paySn}}</text>
  8. </view>
  9. <view class="item flex_row_between_center">
  10. <text class="tit">{{$L('订单金额')}}</text>
  11. <view class="order_amount">
  12. <text class="unit">¥</text>
  13. <text class="price_int">{{payInfo.needPay?$getPartNumber(payInfo.needPay,'int'):''}}</text>
  14. <text class="price_decimal">{{payInfo.needPay?$getPartNumber(payInfo.needPay,'decimal'):''}}</text>
  15. </view>
  16. </view>
  17. </view>
  18. <view class="pay_part flex_column_start_start">
  19. <text class="title">{{$L('选择支付方式')}}</text>
  20. <view v-for="(item,index) in payMethod" :key='index' @click="selectPayMethod(item)" :class="{item:true, b_b:index<payMethod.length-1, flex_row_between_center:true}">
  21. <view class="left flex_row_start_center">
  22. <image class="pay_icon" :src="item.payIcon" />
  23. <text class="tit">{{item.payMethodName}}</text>
  24. </view>
  25. <view class="right">
  26. <text class="balance_available" v-if="item.payMethod == 'balance'" :class="{active:filters.toNum(payInfo.balanceAvailable) >= filters.toNum(payInfo.needPay)}">{{'余额:¥'+filters.toFix(payInfo.balanceAvailable)}}</text>
  27. <text :class="{iconfont:true, iconziyuan33:selData.payMethod == item.payMethod,iconziyuan43:selData.payMethod != item.payMethod,has_sel:selData.payMethod == item.payMethod}"></text>
  28. </view>
  29. </view>
  30. </view>
  31. <!-- 余额支付支付密码 start -->
  32. <view class="balance_password" v-if="balanceCon">
  33. <view class="balance_password_title">{{$L('支付密码')}}</view>
  34. <view class="input_password">
  35. <view class="input_password_con">
  36. <input type="password" v-model="passwordVal" :placeholder="$L('请输入支付密码')" @blur="passwordBlur" v-if="!isShowPasw"/>
  37. <input type="text" v-model="passwordVal" :placeholder="$L('请输入支付密码')" @blur="passwordBlur" v-else/>
  38. <text :class="{iconfont:true,iconziyuan81:isShowPasw,iconziyuan9:!isShowPasw}" @click="showPasword"></text>
  39. </view>
  40. </view>
  41. </view>
  42. <!-- 余额支付支付密码 end -->
  43. <view class="btn_recharge flex_row_center_center" @click="pay" :style="{top:windowHeight - 60 + 'px'}">{{$L('立即支付')}}</view>
  44. </view>
  45. </template>
  46. <!-- '@vue/cli-plugin-babel/preset' -->
  47. <script module="filters" lang="wxs" src="../../utils/filter.wxs"></script>
  48. <script>
  49. import {
  50. mapState
  51. } from 'vuex';
  52. export default {
  53. data() {
  54. return {
  55. paySn:'',//支付单号
  56. payInfo:{},//订单信息
  57. selData: {},
  58. payMethod: [], //支付方式
  59. client: 'wxbrowser',//支付发起来源 pc==pc,mbrowser==移动设备浏览器,app==app,wxxcx==微信小程序,wxbrowser==微信内部浏览器
  60. payMethodType: 'create',//从哪里进入支付,create 下单,orderList 订单列表 orderDetail 订单详情 recharge 充值
  61. isAllowAutoPay: true,//当浏览器地址有code时,是否允许自动支付,如果支付失败的话置为false
  62. autoPayInterval: '',//定时器
  63. wxBrowerCode: '',//微信浏览器支付的code
  64. balanceCon:false, //输入余额支付密码的框是否显示
  65. passwordVal:'', //密码值
  66. isShowPasw:false, //支付密码是否可见
  67. hasSetPassword:false, //用户是否已经设置了余额支付支付密码
  68. oriUrl:'',//不带code的页面地址
  69. windowHeight:'',
  70. ifOnShow:false
  71. }
  72. },
  73. computed: {
  74. ...mapState(['hasLogin', 'userInfo'])
  75. },
  76. onHide(){
  77. this.ifOnShow = true
  78. },
  79. onShow(){
  80. if(this.ifOnShow){
  81. this.isSetPassword()
  82. }
  83. },
  84. onLoad(option) {
  85. this.initClient();
  86. this.paySn = option.paySn;
  87. //#ifdef H5
  88. //判断code地址的参数 start
  89. let cur_code = this.$getQueryVariable('code');
  90. if(cur_code){
  91. uni.showLoading({
  92. title: '支付中...',
  93. mask: true,
  94. })
  95. let oriUrl = option.ori_url+'pages/order/pay';
  96. let tmp_data = '';
  97. for(let i in option){
  98. if(i!='ori_url'){
  99. tmp_data += i+'='+option[i]+'&'
  100. }
  101. }
  102. oriUrl += '?'+tmp_data;
  103. this.oriUrl = oriUrl;
  104. if(this.client == 'wxbrowser'){
  105. //微信浏览器的话要把浏览器地址里面的code去掉
  106. history.replaceState({},'',this.oriUrl);
  107. }
  108. this.wxBrowerCode = cur_code;
  109. }
  110. //判断code地址的参数 end
  111. //#endif
  112. this.payMethodType = option.payMethodType!=undefined?option.payMethodType:this.payMethodType;
  113. this.getPayMethod();
  114. this.getPayInfo();
  115. this.isSetPassword();
  116. uni.getSystemInfo({
  117. success:(res)=>{
  118. this.windowHeight = res.windowHeight;
  119. }
  120. });
  121. },
  122. onUnload(){
  123. if(this.autoPayInterval){
  124. clearInterval(this.autoPayInterval)
  125. }
  126. },
  127. methods: {
  128. //初始化终端类型
  129. initClient() {
  130. //#ifdef APP-PLUS
  131. this.client = 'app';
  132. //#endif
  133. //#ifdef H5
  134. this.client = this.$isWeiXinBrower()?'wxbrowser':'mbrowser';
  135. //#endif
  136. //#ifdef MP-WEIXIN
  137. this.client = 'wxxcx';
  138. //#endif
  139. },
  140. //获取支付方式
  141. getPayMethod() {
  142. let { client,payMethodType,wxBrowerCode } = this;
  143. this.$request({
  144. url: 'v3/business/front/orderPay/payMethod',
  145. data: {
  146. source: client,
  147. type: payMethodType == 'create' || payMethodType == 'orderList' || payMethodType == 'orderDetail' ?1:2,//支付发起类型 1==下单支付,2==余额充值/订单列表
  148. },
  149. }).then(res => {
  150. if (res.state == 200) {
  151. res.data.map(item => {
  152. item.payIcon = getApp().globalData.imgUrl+`pay/${item.payMethod}_pay_icon.png`;
  153. });
  154. this.payMethod = res.data;
  155. if(!wxBrowerCode){
  156. this.selData = this.payMethod[0];
  157. }else{
  158. //有code的话要默认选中微信支付,并直接提交订单
  159. this.selData = this.payMethod.filter(item=>item.payMethod == 'wx')[0];
  160. let _this = this;
  161. if(this.isAllowAutoPay){
  162. this.autoPayInterval = setInterval(function(){
  163. if(_this.payInfo.needPay!=undefined){
  164. _this.pay();
  165. //清除倒计时
  166. clearInterval(_this.autoPayInterval)
  167. }
  168. },1000);
  169. }
  170. }
  171. }
  172. }).catch((e) => {
  173. })
  174. },
  175. //选择支付方式事件
  176. selectPayMethod(val) {
  177. if(val.payMethod == 'balance'){ //余额支付
  178. if(this.hasSetPassword){
  179. if(Number(this.payInfo.balanceAvailable) < Number(this.payInfo.needPay)){ //余额小于订单金额
  180. uni.showToast({
  181. title:'您的余额不足,请选择其他支付方式!',
  182. icon:'none'
  183. })
  184. }else{
  185. this.balanceCon = true;
  186. this.selData = val;
  187. }
  188. }else{
  189. uni.showModal({
  190. title:'温馨提示!',
  191. content:'未设置支付密码',
  192. confirmColor:'#FC1E1C',
  193. confirmText:'立即设置',
  194. success:function(res){
  195. if(res.confirm){
  196. uni.navigateTo({
  197. url:'/pages/account/managePwd?source=set_pay'
  198. })
  199. }else if(res.cancel){
  200. }
  201. }
  202. })
  203. }
  204. }else{
  205. this.selData = val;
  206. this.balanceCon = false;
  207. }
  208. },
  209. //余额支付是否已设置过密码
  210. isSetPassword(){
  211. this.$request({
  212. url: 'v3/business/front/orderPay/payPwdCheck',
  213. method:'GET',
  214. }).then(res => {
  215. if (res.state == 200) {
  216. this.hasSetPassword = res.data;
  217. }
  218. }).catch((e) => {
  219. })
  220. },
  221. //获取支付信息
  222. getPayInfo(){
  223. this.$request({
  224. url: 'v3/business/front/orderPay/payInfo',
  225. data: {
  226. paySn: this.paySn,
  227. payFrom:this.payMethodType == 'create'?1:2
  228. },
  229. }).then(res => {
  230. if (res.state == 200) {
  231. this.payInfo = res.data;
  232. }
  233. }).catch((e) => {
  234. })
  235. },
  236. //失焦,获取输入密码值
  237. passwordBlur(e){
  238. this.passwordVal = e.detail.value;
  239. var reg = /^[\w.]{6,20}$/;
  240. //密码的验证 6~20位,英文、数字或符号
  241. let flag = reg.test(this.passwordVal);
  242. if(!flag){
  243. uni.showToast({
  244. title:'请输入6~20位英文、数字或符号',
  245. icon:'none',
  246. duration:1000
  247. })
  248. this.passwordVal = ''
  249. }
  250. },
  251. //密码是否可见
  252. showPasword(){
  253. this.isShowPasw = !this.isShowPasw
  254. },
  255. //立即支付事件
  256. pay() {
  257. const {
  258. selData,client,wxBrowerCode
  259. } = this;
  260. let _this = this;
  261. let param = {};
  262. param.url = 'v3/business/front/orderPay/doPay';
  263. param.method = 'POST';
  264. param.data = {};
  265. param.data.payType = selData.payType;
  266. param.data.payMethod = selData.payMethod;
  267. param.data.paySn = this.payInfo.paySn;
  268. if(selData.payMethod == 'balance'){
  269. //余额支付
  270. param.data.payPwd = this.passwordVal; //支付密码,使用余额时必传
  271. }else{
  272. if(client == 'wxxcx'){
  273. //微信小程序支付
  274. uni.login({
  275. success: code => {
  276. param.data.code = code.code;
  277. param.data.codeSource = 1 ;//用户code来源(JSAPI支付时必填):1==小程序,2==微信内部浏览器
  278. this.$request(param).then(res => {
  279. if (res.state == 200) {
  280. let tmp_data = res.data.payData;
  281. if(res.data.actionType == null){
  282. //微信小程序支付
  283. uni.requestPayment({
  284. 'timeStamp': tmp_data.timeStamp,
  285. 'nonceStr': tmp_data.nonceStr,
  286. 'package': tmp_data.packageValue,
  287. 'signType': 'MD5',
  288. 'paySign': tmp_data.paySign,
  289. success: function(res) {
  290. _this.payTip('success');
  291. },
  292. fail: function(res) {
  293. _this.payTip('fail');
  294. }
  295. });
  296. }
  297. }else{
  298. _this.$api.msg(res.msg);
  299. }
  300. }).catch((e) => {
  301. })
  302. }
  303. });
  304. return false;
  305. }else if(client == 'wxbrowser'){
  306. //微信h5支付
  307. if(!wxBrowerCode){
  308. let tar_url = location.href;
  309. tar_url += location.href.indexOf('?')>-1?'&':'?';
  310. tar_url += 'ori_url='+location.href;
  311. let uricode = encodeURIComponent(tar_url)
  312. window.location.href = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid='+getApp().globalData.h5AppId+'&redirect_uri=' + uricode + '&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect';
  313. return false;
  314. }else{
  315. param.data.code = wxBrowerCode;
  316. param.data.codeSource = 2 ;//用户code来源(JSAPI支付时必填):1==小程序,2==微信内部浏览器
  317. }
  318. }
  319. }
  320. this.$request(param).then(res => {
  321. if (res.state == 200) {
  322. if(selData.payMethod == 'balance'){
  323. uni.showToast({
  324. title:'支付成功!',
  325. duration:700
  326. })
  327. setTimeout(()=>{
  328. uni.redirectTo({
  329. url:'/pages/order/tradeSuccess?orderSn=' + this.payInfo.paySn + '&sourceType=tradeSuccess'
  330. })
  331. },1000)
  332. }else{
  333. let tmp_data = res.data.payData;
  334. if(res.data.actionType == 'redirect'){
  335. window.location.href = tmp_data;
  336. }else if(res.data.actionType == null){
  337. if(client == 'wxbrowser'){
  338. uni.hideLoading();
  339. this.wxBrowerCode = '';
  340. //微信h5支付
  341. this.$weiXinBrowerPay({
  342. timestamp: tmp_data.timeStamp,
  343. nonceStr: tmp_data.nonceStr,
  344. package: tmp_data.packageValue,
  345. signType: 'MD5',
  346. paySign: tmp_data.paySign,
  347. appId: tmp_data.appId, //此参数可不用
  348. success: function(r) {
  349. if (r.errMsg == "chooseWXPay:ok") {
  350. _this.payTip('success');
  351. } else {
  352. _this.payTip('fail');
  353. _this.isAllowAutoPay = false;//支付失败后禁止自动支付
  354. }
  355. },
  356. cancel: function(r) {
  357. _this.payTip('fail');
  358. _this.isAllowAutoPay = false;//支付失败后禁止自动支付
  359. }
  360. });
  361. }else if(client == 'wxxcx'){
  362. //微信小程序支付
  363. uni.requestPayment({
  364. 'timeStamp': tmp_data.timeStamp,
  365. 'nonceStr': tmp_data.nonceStr,
  366. 'package': tmp_data.packageValue,
  367. 'signType': 'MD5',
  368. 'paySign': tmp_data.paySign,
  369. 'success': function(res) {
  370. _this.payTip('success');
  371. },
  372. 'fail': function(res) {
  373. _this.payTip('fail');
  374. }
  375. });
  376. }else if(client == 'app'){
  377. //APP支付
  378. let provider = '';
  379. let orderInfo = {};
  380. if(selData.payMethod == 'wx'){
  381. provider = 'wxpay';
  382. orderInfo.appid = tmp_data.appId;
  383. orderInfo.noncestr = tmp_data.nonceStr;
  384. orderInfo.package = tmp_data.packageValue;
  385. orderInfo.partnerid = tmp_data.partnerId;
  386. orderInfo.prepayid = tmp_data.prepayId;
  387. orderInfo.timestamp = tmp_data.timeStamp;
  388. orderInfo.sign = tmp_data.sign;
  389. }else if(selData.payMethod == 'alipay'){
  390. provider = 'alipay';
  391. }
  392. uni.requestPayment({
  393. provider: provider,
  394. orderInfo: provider=='alipay'?res.data.payData:orderInfo, //订单数据
  395. success: function(res) {
  396. _this.payTip('success');
  397. },
  398. fail: function(err) {
  399. _this.payTip('fail');
  400. }
  401. });
  402. }
  403. }else if(res.data.actionType == 'autopost'){
  404. document.write(res.data.payData);
  405. }
  406. }
  407. }else{
  408. this.$api.msg(res.msg);
  409. if(this.passwordVal != ''){
  410. this.passwordVal = '';
  411. }
  412. }
  413. }).catch((e) => {
  414. })
  415. },
  416. //支付操作完成提示
  417. payTip(type){
  418. //如果来自下单,直接跳转订单列表,否则返回上一级页面(订单列表或者订单详情),并更新数据
  419. if(type == 'success'){
  420. //提示支付成功
  421. uni.showToast({
  422. title:'支付成功!',
  423. duration:700
  424. })
  425. }else if(type == 'fail'){
  426. //提示支付失败
  427. this.$api.msg('支付失败,请重试~');
  428. }
  429. if(this.client == 'wxbrowser'){
  430. uni.reLaunch({
  431. url: '/pages/order/list?state=0'
  432. });
  433. return;
  434. }
  435. if(this.payMethodType == 'create'){ //下单
  436. setTimeout(()=>{
  437. uni.redirectTo({
  438. url:'/pages/order/list?state=0'
  439. })
  440. },1000)
  441. }else{
  442. const pages = getCurrentPages(); //当前页面栈
  443. if (pages.length > 1) {
  444. const beforePage = pages[pages.length - 2]; //获取上一个页面实例对象
  445. if(this.payMethodType == 'orderList'){
  446. beforePage.$vm.loadData(); //触发上个面中的方法获取订单列表 *loadData为上个页面的方法*
  447. }else if(this.payMethodType == 'orderDetail'){
  448. beforePage.$vm.getOrderDetail(); //触发上个面中的方法获取订单详情 *getOrderDetail为上个页面的方法*
  449. }
  450. }
  451. setTimeout(() => {
  452. uni.navigateBack()
  453. }, 1000)
  454. }
  455. }
  456. }
  457. }
  458. </script>
  459. <style lang="scss">
  460. page {
  461. background: #FFFFFF;
  462. width: 750rpx;
  463. margin: 0 auto;
  464. }
  465. .container {
  466. display: flex;
  467. flex-direction: column;
  468. flex: 1;
  469. .order_info {
  470. border-top: 20rpx solid #F5F5F5;
  471. width: 750rpx;
  472. background: #fff;
  473. .item{
  474. position: relative;
  475. height: 100rpx;
  476. width: 100%;
  477. padding: 0 20rpx;
  478. .tit{
  479. color: $main-font-color;
  480. font-size: 30rpx;
  481. }
  482. .order_sn{
  483. color: #2D2D2D;
  484. font-size: 26rpx;
  485. }
  486. &.b_b:after{
  487. left: 20rpx;
  488. }
  489. .order_amount{
  490. color: $main-color;
  491. font-weight: bold;
  492. .unit,
  493. .price_decimal {
  494. font-size: 24rpx;
  495. margin-right: 3rpx;
  496. line-height: 24rpx;
  497. }
  498. .price_int {
  499. font-size: 34rpx;
  500. line-height: 34rpx;
  501. }
  502. }
  503. }
  504. }
  505. .pay_part {
  506. border-top: 20rpx solid #F5F5F5;
  507. background: #fff;
  508. flex: 1;
  509. .title {
  510. color: $main-font-color;
  511. font-size: 32rpx;
  512. margin-top: 30rpx;
  513. margin-left: 20rpx;
  514. }
  515. .item {
  516. width: 100%;
  517. padding: 20rpx;
  518. position: relative;
  519. .left {
  520. .pay_icon {
  521. width: 80rpx;
  522. height: 80rpx;
  523. }
  524. .tit {
  525. color: $main-font-color;
  526. font-size: 28rpx;
  527. margin-left: 20rpx;
  528. }
  529. }
  530. .right{
  531. .balance_available{
  532. font-size: 28rpx;
  533. color: #999;
  534. margin-right: 20rpx;
  535. }
  536. .active{
  537. color: #FC1C1C;
  538. }
  539. .iconfont {
  540. color: $main-third-color;
  541. font-size: 32rpx;
  542. }
  543. .has_sel {
  544. color: $main-color;
  545. }
  546. &.b_b:after {
  547. left: 20rpx;
  548. }
  549. }
  550. }
  551. }
  552. .balance_password{
  553. border-top: 20rpx solid #F5F5F5;
  554. .balance_password_title{
  555. width:750rpx;
  556. height: 100rpx;
  557. display: flex;
  558. align-items: center;
  559. font-size: 30rpx;
  560. font-family: PingFang SC;
  561. font-weight: 500;
  562. color: #333333;
  563. line-height: 39rpx;
  564. padding-left: 20rpx;
  565. box-sizing: border-box;
  566. border-bottom: 1rpx solid #F5F5F5;
  567. background: #FFFFFF;
  568. }
  569. .input_password{
  570. width: 750rpx;
  571. height: 86rpx;
  572. background: #FFFFFF;
  573. .input_password_con{
  574. margin: 0 41rpx;
  575. height: 86rpx;
  576. display: flex;
  577. justify-content: space-between;
  578. align-items: center;
  579. border-bottom: 1rpx solid #F5F5F5;
  580. input{
  581. font-size: 26rpx;
  582. font-family: PingFang SC;
  583. font-weight: 500;
  584. color: #999999;
  585. line-height: 39rpx;
  586. }
  587. text{
  588. color: #666666;
  589. font-size: 20rpx;
  590. }
  591. }
  592. }
  593. }
  594. .btn_recharge {
  595. width: 670rpx;
  596. height: 88rpx;
  597. background: #F30300;
  598. border-radius: 44rpx;
  599. color: #fff;
  600. font-size: 36rpx;
  601. position: absolute;
  602. right: 0rpx;
  603. left: 0rpx;
  604. margin: 0 auto;
  605. }
  606. }
  607. </style>