Commit 94732e1b authored by 郭志伟's avatar 郭志伟

feat(pay): 支付相关功能调整

parent c9bc8ed6
This diff is collapsed.
......@@ -3,45 +3,10 @@ import config from '@/config';
import { saDeviceId } from '@/service/sa.service';
const { talosHost } = config;
const queryPayInfo = function(data) {
return request.post(`${talosHost}/open/checkout`, data);
};
const prepay = async function(data) {
const scDeviceId = await saDeviceId();
return request.post(`${talosHost}/open/checkout/prepay`, data, {
customHeader: {
scDeviceId
},
hideToast: true
});
};
const pay = async function(data) {
const scDeviceId = await saDeviceId();
return request.post(`${talosHost}/open/checkout/pay`, data, {
customHeader: {
scDeviceId
},
hideToast: true
});
};
const queryPayStatus = function(data) {
return request.post(`${talosHost}/open/checkout/pay_status/query`, data);
};
const sendSms = function(data) {
return request.post(`${talosHost}/open/checkout/send_sms`, data);
};
const desSalt = function() {
return request.get(`${talosHost}/vcc/account/salt`);
};
const h5AppyUrl = function() {
return request.get(`${talosHost}/vcc/xyqb_mall/app_url`);
};
const getCoupon = async function(params) {
const scDeviceId = await saDeviceId();
return request.post(`${talosHost}/open/checkout/pay_coupon_list`, params, {
......@@ -51,43 +16,4 @@ const getCoupon = async function(params) {
});
};
const ocrFaceId = function(params) {
return request.post(`${talosHost}/open/checkout/ocr_faceId`, params);
};
const getGoodsList = function(data) {
return request.get(`${talosHost}/vcc/xyqb/recommend/goods-list`, data, {
needScDeviceId: true,
hideLoading: true
});
};
// KA流程节点
const kaGetNextUrl = function() {
return request.get(`${talosHost}/api/kdsp/ka/process/get-next-url`);
};
const reissueContract = function(params) {
return request.post(`${talosHost}/open/checkout/payReissueContract`, params);
};
// 获取用户手机号
const getPhoneNumber = function() {
return request.get(`${talosHost}/api/kdsp/user/phone`);
};
export {
pay,
prepay,
sendSms,
desSalt,
h5AppyUrl,
getCoupon,
ocrFaceId,
queryPayInfo,
getGoodsList,
kaGetNextUrl,
queryPayStatus,
reissueContract,
getPhoneNumber
};
export { getCoupon, queryPayStatus };
......@@ -4,5 +4,6 @@ let payHost = protocol + '//mapi-qa.liangkebang.net/pay';
let shenceHost = 'https://bn.xyqb.com/sa?project=default'; // 测试地址
let talosHost = 'http://talos-test1.liangkebang.net'; // 电商分期测试环境服务地址
let operatorHost = 'https://operator.liangkebang.com';
const phobosHost = 'http://192.168.24.158:8080';
const toBHost = 'https://tob.liangkebang.net';
export default { talosHost, operatorHost, payHost, shenceHost, test: true, toBHost };
export default { talosHost, operatorHost, payHost, shenceHost, test: true, toBHost, phobosHost };
......@@ -5,6 +5,7 @@ const operatorHost = protocol + '//auth.quantgroup.cn';
const payHost = protocol + '//payapi.xyqb.com';
const shenceHost = 'https://bn.xyqb.com/sa?project=production';
const toBHost = 'https://tob.liangkebang.net';
const phobosHost = 'https://phobos.q-gp.com';
export default {
// apiHost,
test: false,
......@@ -12,5 +13,6 @@ export default {
talosHost,
payHost,
toBHost,
phobosHost,
operatorHost
};
import cfg from '@/config/index';
import localStorage from '@/service/localStorage.service';
export default {
methods: {
toPay(orderNo) {
const redirectUrl = window.location.origin;
// this.$router.push({ name: 'pay', query: { orderNo: data.orderNo } });
window.location.replace(
`${cfg.phobosHost}?orderNo=${orderNo}&vccToken=${localStorage.get(
'vccToken'
)}&vccChannel=${localStorage.get('vccChannel')}&hideOrder=true&redirectUrl=${redirectUrl}`
);
}
}
};
......@@ -2,14 +2,12 @@
* @Description: 数据加密,aes加密数据主体,rsa加密aes密钥,aes密钥前端自己存储
* @Date: 2020-12-08 11:08:28
* @LastEditors: gzw
* @LastEditTime: 2021-07-07 17:44:22
* @LastEditTime: 2021-10-25 18:04:59
*/
import { cipher as AES, util as UTIL, pki as PKI, md as SHA1 } from 'node-forge';
import uuidv1 from 'uuid/v1';
import { parseTime } from './utils.service';
import { APP_ID, PUBLIC_KEY, PRIVATE_KEY } from '@/config/encrypt.config';
import CryptoJS from 'crypto-js';
import { desSalt } from '@/api/pay.api';
/**
* @description: 数据加密
......@@ -33,21 +31,6 @@ export function encryption(data = '') {
};
}
/**
* @description: 数据加密
* @message {String} message 数据源
* @return {String} 加密后的数据16进制
*/
export async function encryptByDESModeEBC(message) {
const [{ payPwdSalt }] = await desSalt();
var keyHex = CryptoJS.enc.Utf8.parse(payPwdSalt);
var encrypted = CryptoJS.DES.encrypt(message, keyHex, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return encrypted.ciphertext.toString().toLocaleUpperCase();
}
/**
* @description: AES加密数据,默认CBC, Pki#cs7
* @param {String} txt 数据源
......
......@@ -27,6 +27,7 @@
</div>
</template>
<script>
import toPayMixins from '@/mixins/toPay.mixins';
import api from '@/api/recharge.api';
import orderApi from '@/api/order.api';
import RechargeTop from './components/RechargeTop.vue';
......@@ -41,6 +42,7 @@ export default {
RechargeList,
RechargeInput
},
mixins: [toPayMixins],
data() {
return {
vipLife: [],
......@@ -102,7 +104,7 @@ export default {
if (error) {
return;
}
res?.orderNo && this.$router.push({ path: '/pay', query: { orderNo: res.orderNo } });
res?.orderNo && this.toPay(res.orderNo);
}
}
};
......
......@@ -96,7 +96,7 @@
plain
type="primary"
shape="circle"
@click="toPay"
@click="toPay(orderNo)"
>
付款</cr-button
>
......@@ -127,6 +127,7 @@
import ListItem from '../orderList/components/ListItem.vue';
import { setClipboardData } from '@/service/utils.service';
import { isBack } from '@/service/routerStorage';
import toPayMixins from '@/mixins/toPay.mixins';
import orderApi from '@/api/order.api';
import img11 from '@/assets/images/order/11.png';
import img21 from '@/assets/images/order/21.png';
......@@ -145,6 +146,7 @@ export default {
components: {
ListItem
},
mixins: [toPayMixins],
data() {
return {
orderNo: '',
......@@ -216,9 +218,6 @@ export default {
this.$set(this.orderInfo.orderStatusInfo, `orderStatusText`, '交易关闭');
}
},
toPay() {
this.$router.push({ path: '/pay', query: { orderNo: this.orderNo } });
},
toGoods() {
this.$router.push({
path: '/vipLife'
......
......@@ -24,6 +24,7 @@ import orderApi from '@/api/order.api';
import List from './components/List';
import { isApp } from '@/service/validation.service';
import Cookie from '@/service/cookieStorage.service';
import toPayMixins from '@/mixins/toPay.mixins';
const commonParams = {
loading: false,
finished: false,
......@@ -36,6 +37,7 @@ export default {
components: {
List
},
mixins: [toPayMixins],
data() {
return {
tabOffset: isApp || Cookie.get('h') == '0' ? 0 : 48,
......@@ -94,7 +96,7 @@ export default {
this.orderCancelPopup();
break;
case 'pay':
this.toPay();
this.toPay(this.currentOrder.orderNo);
break;
case 'notify':
this.orderNotify();
......@@ -107,10 +109,6 @@ export default {
break;
}
},
toPay() {
this.$router.push({ path: '/pay', query: { orderNo: this.currentOrder.orderNo } });
},
toGoods() {
this.$router.push({
path: '/vipLife'
......
<template>
<div class="contract">
<cr-checkbox
v-model="isCheck"
shape="round"
class="contract-checkbox"
btn-size="mini"
@change="change"
/>
<p class="contract-list">
勾选同意
<span
v-for="contract in contractList"
:key="contract.contractName"
@click="goview(contract.contractVisitUrl)"
>
{{ contract.contractName }}</span
>
</p>
</div>
</template>
<script>
export default {
name: 'Contract',
props: {
contractList: Array,
value: Boolean
},
data() {
return {
isCheck: this.value
};
},
methods: {
goview(url) {
window.location.href = url;
},
change(e) {
this.$emit('input', e);
}
}
};
</script>
<style lang="less">
.contract {
width: 100%;
position: relative;
display: flex;
align-items: flex-start;
color: @gray-5;
.text-10;
&-list {
color: @gray-5;
span {
color: @red;
}
}
}
</style>
<template>
<div class="pay-type-list">
<p class="type-title">{{ value.title }}</p>
<payGroupItem
v-if="value.isGroupPay"
v-model="payList"
:pay-type="payType"
:disabled="disabled"
:coupon-info="couponInfo"
:show-coupon="showCoupon"
:coupon-disabled="showCoupon && !payCouponCouldBeUsed"
/>
<template v-else>
<template v-for="(item, key) of payList">
<payCardItem
v-if="item.show"
:key="key"
:pay-type="payType"
:value="item"
:disabled="disabled"
:coupon-info="couponInfo"
:show-coupon="showCoupon"
:coupon-disabled="showCoupon && !payCouponCouldBeUsed"
/>
</template>
</template>
</div>
</template>
<script>
import payCardItem from './PayCardItem';
import payGroupItem from './PayGroupCard.vue';
export default {
name: 'PayCard',
components: {
payCardItem,
payGroupItem
},
provide() {
return {
payCard: this
};
},
props: {
value: Object,
payType: Number,
payText: String,
disabled: Boolean,
couponInfo: Object,
riskLimit: Boolean,
showCoupon: Boolean,
payCouponCouldBeUsed: Boolean,
single: { type: Boolean, default: false },
creditPay: { type: Boolean, default: false }
},
data() {
return {
payList: this?.value?.payList || {}
};
},
mounted() {},
methods: {}
};
</script>
<style lang="less">
.pay-type-list {
background-color: #fff;
border-radius: @border-radius-sm;
margin-top: @padding-sm;
.type-title {
height: 48px;
.text-16;
font-weight: bold;
padding-left: @padding-md;
position: relative;
line-height: 48px;
}
}
</style>
<template>
<div class="payCardItem">
<div
:class="['type-item', { 'b-t': !isGroupPay || value.payType === 1 }]"
@click="changePayType(value)"
>
<div class="type-item-content">
<div class="type-item-content-info">
<cr-image :src="value.icon" class="icon" mode="aspectFit" width="39" height="39" />
<div class="content-info-con">
<div class="content-info-tit">
<div class="content-info-tit-wrap">
<p class="content-info-tit">{{ value.name }}</p>
<p v-if="value.tagName" class="content-info-tit_tag">{{ value.tagName }}</p>
</div>
<p v-if="isGroupPay && value.payAmt" class="content-info-amount">
{{ value.payAmt }}
</p>
</div>
<p>{{ value.accountStatusDesc }}</p>
</div>
</div>
<div v-if="showCoupon && value.payType === 1" class="coupon">
<p class="couponDes" :class="{ disable: couponDisabled }" @click.stop="openCouponModal">
<span v-if="couponInfo.pickupId">
{{ `满${couponInfo.limitAmountNew}减${couponInfo.faceValueNew}元` }}
</span>
<span v-else>
未选择优惠券
</span>
<cr-icon type="arrow" class="selectArrow" size="15px" />
</p>
</div>
<p v-if="value.riskLimitDesc" class="limitDes">
{{ value.riskLimitDesc }}
</p>
</div>
<cr-checkbox
v-if="!isGroupPay"
v-model="value.isCheck"
shape="round"
class="type-item-checkbox"
checked-color="#EC1500"
:disabled="disabled || value.disabled"
@click.native="changePayType(value)"
/>
</div>
</div>
</template>
<script>
export default {
name: 'PayCardItem',
inject: ['payCard', 'pay'],
props: {
value: Object,
payType: Number,
disabled: Boolean,
couponInfo: Object,
riskLimit: Boolean,
showCoupon: Boolean,
isGroupPay: Boolean,
couponDisabled: Boolean
},
methods: {
changePayType({ payType, mergePayPretreatmentInfo }) {
if (this.disabled || this.value.disabled || this.isGroupPay) {
return;
}
this.$emit('click');
this.pay.changePayType(payType, mergePayPretreatmentInfo);
},
openCouponModal() {
if (this.couponDisabled) {
this.$toast('优惠券不可更改');
return;
}
this.pay.openCouponModal(this.pay.orderNo);
}
}
};
</script>
<style lang="less" scoped>
.payCardItem {
width: 100%;
padding-bottom: 2px;
}
.coupon {
margin-top: 4px;
height: 20px;
padding-left: 51px;
display: flex;
}
.couponDes {
display: inline-block;
color: @text-color-red;
.text-12;
border: @border-width-base solid @text-color-light;
border-radius: 3px;
padding: 0 0 0 3px;
&.disable {
color: @font-color-disabled;
border-color: @font-color-disabled;
}
text {
color: @text-color-red;
.text-12;
&.disable {
color: @font-color-disabled;
}
}
}
.limitDes {
color: #ed6a0c;
background: #fffbe8;
.text-11;
padding: 4px 8px;
margin: 8px 8px 8px 4px;
border-radius: 3px;
}
.type-item {
width: 100%;
padding: 12px 8px 12px 12px;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
&-content {
width: 100%;
}
&-content-info {
width: 100%;
display: flex;
align-items: center;
}
&-checkbox {
width: 18px;
}
}
.icon {
width: 39px;
height: 39px;
margin-right: @padding-sm;
}
.content-info-tit {
font-size: 15px;
color: @font-color-dark;
margin-bottom: 2px;
display: flex;
align-items: center;
justify-content: space-between;
&-wrap {
display: flex;
}
&_tag {
display: inline-flex;
align-items: center;
height: 16px;
border: @border-width-base solid @text-color-light;
border-radius: 3px;
font-size: 11px;
color: @text-color-light;
margin-left: @padding-unit;
padding: 0 2px;
}
}
.content-info-con {
flex: 1;
display: flex;
flex-direction: column;
.text-12;
color: @font-color-light;
}
.content-info-amount {
width: 100px;
text-align: left;
word-break: break-all;
&::before {
content: '¥';
}
.text-15;
color: @black;
}
.selectArrow {
vertical-align: text-top;
margin-left: -2px;
}
</style>
<template>
<div>
<div @click="changePayType(thirdPayInfo)">
<div class="groupCard">
<PayCardItem
:is-group-pay="true"
:value="creditPayInfo"
:coupon-info="couponInfo"
:show-coupon="showCoupon"
:risk-limit="riskLimit"
:coupon-disabled="couponDisabled"
/>
</div>
<div class="dashed">
<p class="dashed-line" />
<cr-image
src="../../../assets/images/addicon.png"
width="24px"
height="24px"
class="dashed-icon"
/>
<cr-checkbox
v-model="thirdPayInfo.isCheck"
shape="round"
checked-color="#EC1500"
:disabled="disabled || thirdPayInfo.disabled"
class="dashed-checkbox"
@click.native="changePayType(thirdPayInfo)"
/>
</div>
<div class="groupCard">
<PayCardItem :is-group-pay="true" :value="thirdPayInfo" />
</div>
</div>
<p class="group-more b-t" @click="openMore">更多支付组合<cr-icon type="arrow" size="15px" /></p>
<cr-popup v-model="morePopup" round position="bottom" :style="{ height: '30%' }" closeable>
<p class="more-title">更换支付组合</p>
<PayCardItem
v-for="item in thirdPayList"
:key="item.payType"
:value="item"
:coupon-info="couponInfo"
:show-coupon="showCoupon"
:risk-limit="riskLimit"
@click="morePopup = false"
/>
</cr-popup>
</div>
</template>
<script>
import PayCardItem from './PayCardItem.vue';
export default {
name: 'PayGroupCard',
inject: ['payCard', 'pay'],
components: {
PayCardItem
},
props: {
value: Object,
payType: Number,
disabled: Boolean,
couponInfo: Object,
showCoupon: Boolean,
riskLimit: Boolean,
couponDisabled: Boolean
},
data() {
return {
morePopup: false,
thirdPayList: [],
creditPayInfo: this.value.creditPayInfo || {}
};
},
computed: {
thirdPayInfo: function() {
let temp = {};
for (const key in this.value) {
if (this.value[key].isRecommend || this.value[key].isCheck) {
const mergePayPretreatmentInfo = this.value[key]?.mergePayPretreatmentInfo || {};
temp = {
...this.value[key],
payAmt: mergePayPretreatmentInfo?.otherPayAmt
};
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
this.creditPayInfo.payAmt = mergePayPretreatmentInfo.creditPayAmt;
}
}
return temp;
}
},
mounted() {
for (const key in this.value) {
if (this.value[key].isGroupPay) {
this.thirdPayList.push(this.value[key]);
}
}
},
methods: {
changePayType({ payType, mergePayPretreatmentInfo }) {
if (this.disabled || this.creditPayInfo.disabled) {
return;
}
this.pay.changePayType(payType, mergePayPretreatmentInfo);
},
openMore() {
this.morePopup = true;
}
}
};
</script>
<style lang="less">
.groupCard {
width: 100%;
}
.group-more {
.text-13;
width: 335px;
height: 36px;
margin: auto;
display: flex;
color: #ec1500;
position: relative;
align-items: center;
justify-content: center;
}
.cr-popup--close-top-right {
z-index: 2;
}
.dashed {
width: 100%;
padding: 0 12px;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
&-line {
width: 310px;
border-bottom: 1px dashed #dcdcdc;
}
&-checkbox {
width: 18px;
}
&-icon {
position: absolute;
left: 50%;
transform: translateX(-50%);
}
}
.more-title {
.text-16;
display: flex;
justify-content: center;
align-items: center;
height: 48px;
color: @black;
position: relative;
}
</style>
<template>
<cr-overlay :show="value">
<div class="sms-modal">
<div class="sms">
<p class="sms-icon"><cr-icon type="cross" color="#999999" @click="closeModal" /></p>
<p class="sms-title">请输入短信验证码</p>
<p class="sms-des">为保证您账户安全,此笔交易需要短信验证</p>
<p class="phone">已发送至 {{ phoneNumber }}</p>
<cr-authcode-field
span-size="24px"
type="number"
border-type="bottom"
:number="6"
height="40px"
span-color="#666"
input-color="#666"
input-size="20px"
class="sms-input"
:success="success"
/>
</div>
<div slot="footer" class="sms-action">
<p v-if="errorInfo" class="sms-error">{{ errorInfo }}</p>
<p v-if="!send" class="sms-set" @click="sendSms()">获取验证码</p>
<p v-else class="sms-set grey">{{ time }}s后重新获取验证码</p>
</div>
</div>
</cr-overlay>
</template>
<script>
import { sendSms as sendSmsApi, getPhoneNumber } from '@/api/pay.api';
export default {
props: {
value: {
type: Boolean,
default: true
},
orderNo: String,
flowOrderNo: String,
errorInfo: String
},
data() {
return {
send: false,
time: 60,
timer: null,
phoneNumber: ''
};
},
computed: {},
watch: {
value: function(val) {
if (val) {
this.sendSms(1);
}
},
errorInfo: function() {}
},
created() {
this.getPhone();
},
methods: {
closeModal() {
this.clearTimer();
this.$emit('close');
},
success(smsCode) {
this.$emit('submit', smsCode);
},
async sendSms() {
// 页面摧毁清掉定时器
if (this.send) return;
const [, error] = await sendSmsApi({ orderNo: this.orderNo, flowOrderNo: this.flowOrderNo });
if (!error) {
this.send = true;
this.timer = setInterval(() => {
--this.time;
if (!this.time) {
this.clearTimer();
}
}, 1000);
}
},
async getPhone() {
const [phoneNumber] = await getPhoneNumber();
this.phoneNumber = phoneNumber;
},
clearTimer() {
clearInterval(this.timer);
this.send = false;
this.time = 60;
}
}
};
</script>
<style lang="less">
.sms-modal {
box-sizing: border-box;
position: absolute;
left: 50%;
top: 26%;
margin-left: -150px;
padding: 12px 18px 24px;
width: 300px;
border-radius: 16px;
background: #fff;
}
.sms {
&-icon {
width: 100%;
height: 20px;
line-height: 20px;
text-align: right;
}
&-title {
text-align: center;
width: 100%;
font-size: 16px;
font-weight: bold;
}
&-des {
padding: 18px 0 0 8px;
display: flex;
justify-content: flex-start;
font-size: 12px;
color: #999999;
}
&-input {
margin: auto;
}
.phone {
padding-left: 8px;
margin-top: 8px;
color: #323233;
font-size: 14px;
}
&-sms {
margin-top: 4px;
font-size: 30px;
color: #333333;
&:before {
content: '¥';
.text-12;
}
}
&-input {
margin: 20px 0;
}
}
.sms-action {
width: 100%;
display: flex;
justify-content: space-between;
p {
font-size: 14px;
color: #666666;
}
.sms-error {
flex: 1;
color: #ee0a24;
}
.sms-set {
flex: 1;
text-align: right;
}
.grey {
color: #999999;
}
}
</style>
@import url('../../style/mixins');
.pay {
padding: @padding-xs;
}
.price-box {
background-color: @white;
height: 120px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.text-13;
color: @black;
border-radius: @border-radius-sm;
.price{
.text-30;
color: @font-color-red;
font-weight: bold;
margin-top: @padding-xs;
&:before{
content: '¥';
.text-14;
font-weight: bold;
}
}
.time{
display: flex;
align-items: center;
margin-top: @padding-unit;
}
}
.payBtn{
width: 100%;
position: fixed;
padding: @padding-xs @padding-lg;
box-sizing: border-box;
z-index: 99;
bottom: 0;
left:0;
background-color:@white;
.iphonex-fix-margin;
button{
.text-16;
width: 100%;
color: @white;
font-size: @line-height-sm;
border-radius: @border-radius-lx;
span{
font-weight: bold;
}
}
button[disabled] {
color: rgba(255,255,255,.6);
}
&-primary {
@primary-bg();
}
}
.btn{
height:50px;
width: 100%;
position: fixed;
padding: 0 @padding-lg;
box-sizing: border-box;
bottom: 0;
left:0;
background-color:@white;
display: flex;
align-items: center;
justify-content: center;
button{
flex:1;
color: @white;
font-size: @line-height-sm;
border-radius: @border-radius-lx;
}
button[disabled] {
color: rgba(255,255,255,.6);
}
&-primary {
@primary-bg();
}
&-default{
border: 1px solid @cherry-color-error;
color: @cherry-color-error !important;
margin-right: @padding-xs;
background-color: @white !important;
}
}
.placeholder{
width: 100%;
height: 100px;
}
\ No newline at end of file
This diff is collapsed.
......@@ -40,6 +40,7 @@
</template>
<script>
import { onKeyboardStateChange, clearKeyboard } from '@/service/utils.service';
import toPayMixins from '@/mixins/toPay.mixins';
import rechargeApi from '@/api/recharge.api';
import orderApi from '@/api/order.api';
import tipsData from '@/api/tips';
......@@ -57,6 +58,7 @@ export default {
SkuList,
AccountInput
},
mixins: [toPayMixins],
data() {
return {
account: '',
......@@ -196,7 +198,7 @@ export default {
if (error) {
return;
}
res?.orderNo && this.$router.push({ path: '/pay', query: { orderNo: res.orderNo } });
res?.orderNo && this.toPay(res.orderNo);
}, 1000),
goOrderList() {
this.$track.registeredEvents('h5_OtherRechargePageOrderSuspendedBtnClick', {}, () => {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment