pay.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699
  1. <template>
  2. <view :class="['qn-page-' + theme]" class="app">
  3. <view class="price-box">
  4. <text>支付金额</text>
  5. <text class="price primary-color">{{ price }}</text>
  6. </view>
  7. <view class="pay-type-list" @click="changePayType(item)" v-for="(item, index) in payWay" :key="index">
  8. <view class="type-item">
  9. <text v-if="item.title.indexOf('微信') !== -1" class="ibonfont ibonweixinzhifu"></text>
  10. <text v-else-if="item.title.indexOf('支付宝') !== -1" class="ibonfont ibonumidd17"></text>
  11. <text v-else class="ibonfont ibonhuodaofukuan"></text>
  12. <view class="con">
  13. <view class="tit">
  14. <text>{{ item.title }}</text>
  15. <text class="user-balance" v-if="item.id === 7">(余额:¥{{ Number(userInfo.memberBalance) }})</text>
  16. </view>
  17. <text v-if="item.defaultStatus === 5">推荐使用{{ item.title }}</text>
  18. <block v-if="item.id === 7">
  19. <text v-if="price > Number(userInfo.memberBalance)" class="balance-tip primary-color">可支付余额不足,请搭配其他支付方式</text>
  20. </block>
  21. </view>
  22. <label class="radio"><radio :value="item.id" :color="primaryColor" :checked="pay_id.indexOf(parseInt(item.id)) > -1"></radio></label>
  23. </view>
  24. </view>
  25. <text class="mix-btn primary-bg" @click="confirm">确认支付</text>
  26. <!-- 余额支付密码弹窗 -->
  27. <u-keyboard
  28. default=""
  29. ref="uKeyboard"
  30. mode="number"
  31. :mask="true"
  32. :mask-close-able="false"
  33. :dot-enabled="false"
  34. v-model="pwd_show"
  35. :safe-area-inset-bottom="true"
  36. :tooltip="false"
  37. @change="balancePopChange"
  38. @backspace="onBackspace"
  39. >
  40. <view>
  41. <view class="u-text-center u-padding-20 balance-money">
  42. <text v-if="pay_id.length > 1">{{ Number(userInfo.memberBalance) }}</text>
  43. <text v-else>{{ price }}</text>
  44. <text class="u-font-20 u-padding-left-10">元</text>
  45. <view class="u-padding-10 close" data-flag="false" @tap="balanceShowPop(false)"><u-icon name="close" color="#333333" size="28"></u-icon></view>
  46. </view>
  47. <view class="u-flex u-row-center">
  48. <u-message-input mode="box" :maxlength="6" :dot-fill="true" v-model="balance_pwd" :disabled-keyboard="true" @finish="balanceFinish"></u-message-input>
  49. </view>
  50. <view class="u-text-center u-padding-top-10 u-padding-bottom-20 tips">支付键盘</view>
  51. </view>
  52. </u-keyboard>
  53. <!-- 提示去设置支付密码 -->
  54. <u-modal
  55. v-model="tip_show"
  56. content="抱歉,您未设置支付密码,无法使用余额支付"
  57. :show-cancel-button="true"
  58. confirm-text="立即设置"
  59. cancel-text="取消"
  60. @confirm="setPassword"
  61. ></u-modal>
  62. </view>
  63. </template>
  64. <script>
  65. import QiniuUpload from '../components/qiniu/QiniuUpload.vue';
  66. export default {
  67. components: {
  68. QiniuUpload
  69. },
  70. data() {
  71. return {
  72. tip_show: false, // 余额支付提示
  73. pwd_show: false, // 余额支付密码输入弹窗
  74. balance_pwd: '', // 余额支付密码
  75. bank_data: {
  76. // 银行信息
  77. id: '',
  78. name: '',
  79. bankName: '',
  80. accountNumber: '',
  81. image: ''
  82. },
  83. pay_id: [],
  84. orderInfo: {},
  85. vipId: '',
  86. payWay: {},
  87. price: '',
  88. no: '',
  89. orderId: '',
  90. vip: 0,
  91. source: '', //订单来源
  92. payInfo: '',
  93. tmplIds: []
  94. };
  95. },
  96. onLoad(options) {
  97. if (options.payType) {
  98. this.pay_id = options.payType.split(',').map(item => parseInt(item));
  99. }
  100. this.orderId = options.id ? Number(options.id) : '';
  101. this.price = Number(options.price);
  102. this.vip = options.vip || '';
  103. this.vipId = options.vipId || '';
  104. this.source = this.$common.source();
  105. this.no = options.no;
  106. this.getPaymentType();
  107. this.getCustomerInfo();
  108. },
  109. computed: {
  110. userInfo() {
  111. return this.$store.state.userStatus;
  112. }
  113. },
  114. methods: {
  115. // 获取用户信息
  116. getCustomerInfo() {
  117. this.$u.api.getCustomerInfo().then(({ data }) => {
  118. this.userInfo = data;
  119. // 如果余额为0 则不能选择余额支付方式
  120. const index = this.pay_id.findIndex(item => item === 7);
  121. if (!Number(data.memberBalance)) {
  122. if (index > -1) {
  123. this.pay_id.splice(index, 1);
  124. }
  125. }
  126. this.$store.commit('commit_userStatus', data);
  127. });
  128. },
  129. // 选择支付的方式
  130. changePayType(item) {
  131. if (item.id === 7 && Number(this.userInfo.memberBalance) <= 0) {
  132. this.$u.toast('抱歉,您的可用余额不足');
  133. } else {
  134. // 已选择过该支付方式就把支付方式从数组去掉
  135. const payIndex = this.pay_id.indexOf(item.id);
  136. if (payIndex > -1) {
  137. this.pay_id.splice(payIndex, 1);
  138. return;
  139. }
  140. // 用户可支付余额不足
  141. if (this.price > Number(this.userInfo.memberBalance)) {
  142. // 查找余额支付的支付方式ID
  143. const balaceObj = this.payWay.find(item => item.id === 7);
  144. // 已选择过余额支付,可选择其他支付
  145. const balaceindex = balaceObj ? this.pay_id.indexOf(balaceObj.id) : -1;
  146. if (balaceObj && balaceindex > -1) {
  147. //余额付款只能和微信支付进行组合
  148. if (item.id !== 1) {
  149. this.pay_id = [item.id];
  150. return;
  151. }
  152. if (this.pay_id.length >= 2) {
  153. if (balaceindex === 0) {
  154. this.$set(this.pay_id, 1, item.id);
  155. } else {
  156. this.$set(this.pay_id, 0, item.id);
  157. }
  158. } else {
  159. this.pay_id.push(item.id);
  160. }
  161. } else {
  162. // 未选择余额支付,当支付数组length未0,直接把支付方式放入数组
  163. if (this.pay_id.length === 0) {
  164. this.pay_id.push(item.id);
  165. } else {
  166. //余额付款只能和微信支付进行组合
  167. if (this.pay_id.indexOf(1) === -1) {
  168. // 替换 之前选择的支付方式
  169. this.pay_id.splice(0, 1, item.id);
  170. } else {
  171. // 如果当前选择的支付方式是余额支付,则放入数组
  172. if (item.id === 7) {
  173. this.pay_id.push(item.id);
  174. } else {
  175. // 否则 替换 之前选择的支付方式
  176. this.pay_id.splice(0, 1, item.id);
  177. }
  178. }
  179. }
  180. }
  181. } else {
  182. this.$set(this.pay_id, 0, item.id);
  183. }
  184. }
  185. },
  186. // 支付列表
  187. getPaymentType() {
  188. this.$u.api
  189. .getPaymentType({
  190. price: this.price
  191. })
  192. .then(({ data }) => {
  193. this.payWay = data.filter(item => item.id !== 3);
  194. // 判断支付购买会员卡,支付列表没有货到付款
  195. if (this.vip) {
  196. this.payWay = data.filter(item => item.id === 1);
  197. } else {
  198. this.payWay = data.filter(item => item.title.indexOf('支付宝') === -1);
  199. }
  200. // 银行信息
  201. const banck_data = this.payWay.find(item => item.id === 4);
  202. this.bank_data = banck_data
  203. ? {
  204. id: banck_data.accountData.id,
  205. name: banck_data.accountData.name,
  206. bankName: banck_data.accountData.bankName,
  207. accountNumber: banck_data.accountData.accountNumber,
  208. image: ''
  209. }
  210. : {};
  211. });
  212. },
  213. // 余额支付密码键盘按键被点击(不包含退格键被点击)
  214. balancePopChange(val) {
  215. if (this.balance_pwd.length < 6) {
  216. this.balance_pwd += val;
  217. }
  218. if (this.balance_pwd.length >= 6) {
  219. // 唤起密码验证
  220. this.checkPayPassword();
  221. }
  222. },
  223. //键盘退格键被点击
  224. onBackspace(e) {
  225. if (this.balance_pwd.length > 0) {
  226. this.balance_pwd = this.balance_pwd.substring(0, this.balance_pwd.length - 1);
  227. }
  228. },
  229. // 余额密码弹窗显示
  230. balanceShowPop(flag = true) {
  231. this.balance_pwd = '';
  232. this.pwd_show = flag;
  233. },
  234. // 余额密码输入完成
  235. balanceFinish() {
  236. // 唤起支付
  237. this.addOrder();
  238. },
  239. // 去设置支付密码
  240. setPassword() {
  241. this.goPage('/pagesT/money/PayPassword');
  242. },
  243. // 支付密码校验
  244. checkPayPassword() {
  245. this.$u.api
  246. .checkPayPassword({
  247. id: this.userInfo.id,
  248. payPassword: this.balance_pwd
  249. })
  250. .then(res => {
  251. this.addOrder();
  252. })
  253. .catch(res => {
  254. this.balance_pwd = '';
  255. });
  256. },
  257. //确认支付
  258. confirm() {
  259. if (!this.pay_id.length) {
  260. uni.showToast({
  261. title: '请选择支付方式',
  262. icon: 'none'
  263. });
  264. return false;
  265. }
  266. if (this.price > Number(this.userInfo.memberBalance)) {
  267. if (this.pay_id.indexOf(7) > -1 && this.pay_id.length !== 2) {
  268. this.$u.toast('可支付余额不足,请搭配其他支付方式');
  269. return;
  270. }
  271. }
  272. // 余额支付弹窗
  273. if (this.pay_id.indexOf(7) > -1) {
  274. if (this.userInfo.payPassword) {
  275. this.balanceShowPop();
  276. } else {
  277. this.tip_show = true;
  278. }
  279. return;
  280. }
  281. this.addOrder();
  282. },
  283. // 打开银行上传凭证弹窗
  284. openBankPop() {
  285. // 银行打款弹窗
  286. if (this.pay_id.indexOf(4) > -1) {
  287. uni.redirectTo({
  288. url: `/pagesT/money/BankPay?orderId=${this.orderId}&payAmount=${this.price}`
  289. });
  290. return;
  291. }
  292. },
  293. addOrder() {
  294. this.createOrder();
  295. return;
  296. // #ifdef MP-WEIXIN
  297. if (this.tmplIds.length) {
  298. uni.requestSubscribeMessage({
  299. tmplIds: this.tmplIds,
  300. success: res => {
  301. this.createOrder();
  302. },
  303. fail: res => {
  304. this.createOrder();
  305. }
  306. });
  307. } else {
  308. this.createOrder();
  309. }
  310. // #endif
  311. // #ifdef APP-PLUS
  312. this.createOrder();
  313. // #endif
  314. },
  315. createOrder() {
  316. if (!this.vip) {
  317. // 余额支付数据
  318. let admixPayData = [];
  319. if (this.pay_id.length === 2) {
  320. // 查找余额支付的支付方式ID
  321. const balaceObj = this.payWay.find(item => item.id === 7);
  322. admixPayData = this.pay_id.map(item => {
  323. let obj = {};
  324. if (balaceObj.id === item) {
  325. obj = {
  326. payType: item,
  327. title: balaceObj.title,
  328. payMoney: Number(this.userInfo.memberBalance)
  329. };
  330. } else {
  331. obj = {
  332. payType: item,
  333. title: this.payWay.find(pay => pay.id === item).title,
  334. payMoney: (this.price - this.userInfo.memberBalance).toFixed(2)
  335. };
  336. }
  337. return obj;
  338. });
  339. }
  340. const params = {
  341. payType: this.pay_id.join(','),
  342. no: this.no,
  343. source: this.source
  344. };
  345. if (this.pay_id.length === 2) {
  346. params.admixPayData = admixPayData;
  347. }
  348. this.$u.api.payOrder(params).then(({ data }) => {
  349. // 货到付款 直接进入订单页面
  350. if (this.pay_id.length === 1) {
  351. if (this.pay_id.indexOf(3) > -1) {
  352. uni.redirectTo({
  353. url: '/pagesT/order/order?state=0'
  354. });
  355. return;
  356. } else if (this.pay_id.indexOf(4) > -1) {
  357. // 银行打款弹窗
  358. this.openBankPop();
  359. return;
  360. }
  361. }
  362. this.payInfo = data;
  363. this.requestPayment();
  364. });
  365. } else {
  366. this.$u.api
  367. .createVipCardOrder({
  368. payType: this.pay_id.join(','),
  369. source: this.source,
  370. vipCardId: this.vipId
  371. })
  372. .then(({ data }) => {
  373. this.payInfo = data;
  374. this.requestPayment();
  375. });
  376. }
  377. },
  378. requestPayment() {
  379. // #ifdef APP-PLUS
  380. let provider = '';
  381. if (this.pay_id.indexOf(1) > 1) {
  382. provider = 'wxpay';
  383. } else if (this.pay_id.indexOf(2) > 1) {
  384. provider = 'alipay';
  385. }
  386. if (!provider) {
  387. if (!this.vip) {
  388. uni.redirectTo({
  389. url: '/pagesT/order/order?state=0'
  390. });
  391. } else {
  392. uni.redirectTo({
  393. url: '/pagesT/user/VipList'
  394. });
  395. }
  396. return;
  397. }
  398. uni.requestPayment({
  399. provider: provider, //供应商
  400. orderInfo: this.payInfo,
  401. success: res => {
  402. if (!this.vip) {
  403. // 银行打款弹窗
  404. this.openBankPop();
  405. setTimeout(() => {
  406. uni.redirectTo({
  407. url: '/pagesT/money/paySuccess?price=' + this.price
  408. });
  409. }, 2000);
  410. } else {
  411. this.$api.msg('支付成功');
  412. setTimeout(() => {
  413. uni.redirectTo({
  414. url: '/pagesT/user/VipList'
  415. });
  416. }, 2000);
  417. }
  418. },
  419. fail: err => {
  420. this.$api.msg('支付未成功');
  421. if (!this.vip) {
  422. setTimeout(() => {
  423. uni.redirectTo({
  424. url: '/pagesT/order/order?state=0'
  425. });
  426. }, 2000);
  427. } else {
  428. setTimeout(() => {
  429. uni.redirectTo({
  430. url: '/pagesT/user/VipList'
  431. });
  432. }, 2000);
  433. }
  434. }
  435. });
  436. // #endif
  437. // #ifdef MP-TOUTIAO
  438. if (this.pay_id.indexOf(1) === 1) {
  439. uni.redirectTo({
  440. url: '/pagesT/order/order?state=0'
  441. });
  442. return;
  443. }
  444. uni.requestPayment({
  445. provider: 'toutiao',
  446. service: 3, //字节跳动小程序必填,service=3: 微信API支付,不拉起小程序收银台;service=4: 支付宝API支付,不拉起小程序收银台。
  447. orderInfo: this.payInfo,
  448. success: res => {
  449. this.$u.api
  450. .orderquery({
  451. out_trade_no: this.payInfo.out_order_no
  452. })
  453. .then(data => {
  454. const payStatus = data.data;
  455. if (payStatus === 'SUCCESS') {
  456. if (!this.vip) {
  457. // 银行打款弹窗
  458. this.openBankPop();
  459. setTimeout(() => {
  460. uni.redirectTo({
  461. url: '/pagesT/money/paySuccess?price=' + this.price
  462. });
  463. }, 2000);
  464. } else {
  465. this.$api.msg('支付成功');
  466. setTimeout(() => {
  467. uni.redirectTo({
  468. url: '/pagesT/user/VipList'
  469. });
  470. }, 2000);
  471. }
  472. } else {
  473. uni.redirectTo({
  474. url: '/pagesT/order/order?state=0'
  475. });
  476. }
  477. });
  478. },
  479. fail: err => {
  480. console.log('err:', err);
  481. if (!this.vip) {
  482. setTimeout(() => {
  483. uni.redirectTo({
  484. url: '/pagesT/order/order?state=0'
  485. });
  486. }, 2000);
  487. } else {
  488. this.$api.msg('支付未成功');
  489. setTimeout(() => {
  490. uni.redirectTo({
  491. url: '/pagesT/user/VipList'
  492. });
  493. }, 2000);
  494. }
  495. }
  496. });
  497. // #endif
  498. // #ifdef MP-WEIXIN
  499. if (this.pay_id.indexOf(1) === -1) {
  500. uni.redirectTo({
  501. url: '/pagesT/order/order?state=0'
  502. });
  503. return;
  504. }
  505. uni.requestPayment({
  506. provider: this.payInfo.provider, //供应商
  507. timeStamp: this.payInfo.timeStamp, //当前时间
  508. nonceStr: this.payInfo.nonceStr, //随机字符串,长度在32一下
  509. package: this.payInfo.package, //统一单接口返回的prepay_id
  510. signType: this.payInfo.signType, //签名算法,目前支持MD5
  511. paySign: this.payInfo.paySign, //签名
  512. success: res => {
  513. if (!this.vip) {
  514. // 银行打款弹窗
  515. this.openBankPop();
  516. setTimeout(() => {
  517. uni.redirectTo({
  518. url: '/pagesT/money/paySuccess?price=' + this.price
  519. });
  520. }, 100);
  521. } else {
  522. this.$api.msg('支付成功');
  523. uni.redirectTo({
  524. url: '/pagesT/user/VipList'
  525. });
  526. }
  527. },
  528. fail: err => {
  529. this.$api.msg('支付未成功');
  530. if (!this.vip) {
  531. setTimeout(() => {
  532. uni.redirectTo({
  533. url: '/pagesT/order/order?state=0'
  534. });
  535. }, 100);
  536. }
  537. }
  538. });
  539. // #endif
  540. },
  541. // 获取消息模版ID
  542. getSettingDataByMessageId() {
  543. this.$u.api
  544. .getSettingDataByMessageId({
  545. id: [2]
  546. })
  547. .then(res => {
  548. if (res.data.length) {
  549. this.tmplIds = res.data.map(item => {
  550. return item.weixinTemplateId;
  551. });
  552. }
  553. });
  554. }
  555. }
  556. };
  557. </script>
  558. <style lang="scss">
  559. .app {
  560. width: 100%;
  561. }
  562. .imgLogo {
  563. width: 52upx;
  564. height: 54upx;
  565. border-radius: 50%;
  566. }
  567. .price-box {
  568. background-color: #fff;
  569. height: 265upx;
  570. display: flex;
  571. flex-direction: column;
  572. justify-content: center;
  573. align-items: center;
  574. font-size: 28upx;
  575. color: #909399;
  576. .price {
  577. font-size: 52upx;
  578. color: $price-color;
  579. font-weight: bold;
  580. margin-top: 12upx;
  581. font-family: DIN-Medium;
  582. &:before {
  583. content: '¥';
  584. font-size: 28upx;
  585. }
  586. }
  587. }
  588. .pay-type-list {
  589. width: 710rpx;
  590. margin: 20rpx auto;
  591. background-color: #fff;
  592. padding-left: 60upx;
  593. border-radius: 16rpx;
  594. .type-item {
  595. height: 120upx;
  596. padding: 20upx 0;
  597. display: flex;
  598. justify-content: space-between;
  599. align-items: center;
  600. padding-right: 60upx;
  601. font-size: 30upx;
  602. position: relative;
  603. .balance-tip {
  604. font-size: 22rpx;
  605. line-height: 36rpx;
  606. bottom: 10rpx;
  607. left: 0;
  608. }
  609. }
  610. .ibonfont {
  611. width: 80upx;
  612. font-size: 48upx;
  613. }
  614. .ibonhuodaofukuan {
  615. color: #fe8e2e;
  616. }
  617. .ibonweixinzhifu {
  618. color: #36cb59;
  619. }
  620. .ibonumidd17 {
  621. color: #01aaef;
  622. }
  623. .tit {
  624. font-size: 28rpx;
  625. color: $font-color-dark;
  626. margin-bottom: 4upx;
  627. }
  628. .user-balance {
  629. font-size: 24rpx;
  630. color: #999999;
  631. }
  632. .con {
  633. flex: 1;
  634. display: flex;
  635. flex-direction: column;
  636. font-size: $font-sm;
  637. color: $font-color-light;
  638. }
  639. }
  640. .mix-btn {
  641. display: flex;
  642. align-items: center;
  643. justify-content: center;
  644. width: 630upx;
  645. height: 80upx;
  646. margin: 80upx auto 30upx;
  647. font-size: $font-lg;
  648. color: #fff;
  649. background-color: #f53c28;
  650. border-radius: 10upx;
  651. }
  652. .self-shop-title {
  653. padding: 0 30upx;
  654. line-height: 88upx;
  655. font-weight: bold;
  656. font-size: 32upx;
  657. .ibonfont {
  658. color: #999;
  659. font-weight: 400;
  660. }
  661. }
  662. .confirm-btn {
  663. color: #ffffff;
  664. line-height: 90rpx;
  665. text-align: center;
  666. }
  667. .balance-money {
  668. font-size: 80rpx;
  669. color: $u-type-warning;
  670. position: relative;
  671. .close {
  672. position: absolute;
  673. top: 20rpx;
  674. right: 20rpx;
  675. line-height: 28rpx;
  676. font-size: 28rpx;
  677. }
  678. }
  679. </style>