提交 60662f4e authored 作者: lhh's avatar lhh

新增支付

上级 f4f61766
......@@ -33,10 +33,17 @@ export function getVideoView(params: { id: string }) {
return httpRequest.get('/api/psp/v1/admission/video-view', { params })
}
// 获取推荐课程列表
export function getRecommendCourse(params?: { page_size?: string; page?: string; category:string }) {
export function getRecommendCourse(params?: { page_size?: string; page?: string; category: string }) {
return httpRequest.get('/api/psp/v2/learning/course-list', { params })
}
// 获取导师列表
export function getTeacherList(params?: { page_size?: string; page?: string; type?: string }) {
return httpRequest.get('/api/psp/v1/lecturer/list', { params })
}
/**
* 创建订单
* */
export function createOrder(data?: any) {
return httpRequest.post('https://shop-show-pc.ezijing.com/api/shop/order/add', data)
}
\ No newline at end of file
export function useDevice() {
const navigator = window.navigator
const userAgent = ref(navigator.userAgent)
const mobile = computed(() => {
return /iphone/i.test(userAgent.value) || (/android/i.test(userAgent.value) && /mobile/i.test(userAgent.value))
})
const wechat = computed(() => {
return /micromessenger/i.test(userAgent.value)
})
const alipay = computed(() => {
return /alipayclient/i.test(userAgent.value)
})
addEventListener('resize', () => {
if (navigator) userAgent.value = navigator.userAgent
})
return { userAgent, mobile, wechat, alipay }
}
......@@ -28,7 +28,7 @@ getCourseDetail({ id: route.query?.id as string }).then((res: any) => {
const goExam = function () {
// 考试有成绩
if (isExam?.status !== 0) {
Dialog.alert({
Dialog.confirm({
title: data.course.category_name,
message: `您的考试成绩:${data.course?.exams?.[0]?.score || 0}`,
confirmButtonText: `${(data.course?.exams?.[0]?.score || 0) >= 60 ? '查看证书' : '重新考试'}`
......@@ -43,6 +43,7 @@ const goExam = function () {
// 没提交考试
if (data.course.progress >= 80) {
router.push(`/exam?type=course&id=${route.query?.id}&eId=${data.course?.exams[0].examination_id}`)
return
} else {
Dialog.alert({
title: '提示',
......
......@@ -42,7 +42,7 @@ const isExam = function () {
}
const viewResult = function () {
Dialog.alert({
Dialog.confirm({
title: data.course.category_name,
message: `您的考试成绩:${data.course?.exams?.[0]?.score || 0}`,
confirmButtonText: `${(data.course?.exams?.[0]?.score || 0) >= 60 ? '查看证书' : '重新考试'}`
......@@ -54,6 +54,10 @@ const viewResult = function () {
}
})
}
const goPay = function () {
router.push(`/shop/pay?id=${route.query.id}`)
}
</script>
<template>
......@@ -92,7 +96,7 @@ const viewResult = function () {
课程价格<span></span>
<p>{{ data.course?.prices }}</p>
</div>
<div class="btn">立即购买</div>
<div class="btn" @click="goPay">立即购买</div>
</div>
<!-- 已购买没考试 -->
<div :class="data.course?.is_free_name === '已过期' ? 'btn-s2 free' : 'btn-s2'" v-else @click="handleStudy">
......
import httpRequest from '@/utils/axios'
/**
* 创建订单
* */
export function createOrder(data?: any) {
return httpRequest.post('https://shop-show-pc.ezijing.com/api/shop/order/add', data, {
headers: { 'Content-Type': 'application/json' }
})
}
/**
* 获取订单
* */
export function getOrderList(data?: any) {
return httpRequest.post('https://shop-show-pc.ezijing.com/api/shop/order/search', data, {
headers: { 'Content-Type': 'application/json' }
})
}
/**
* 获取用户OpenId
* */
export function getOpenId(data?: any) {
return httpRequest.post('https://shop-show-pc.ezijing.com/api/usercenter/v1/wechat/get-openid', data, {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
})
}
export function getCourseDetail(params: { id: string }) {
return httpRequest.get('/api/psp/v2/learning/course-view', { params })
}
\ No newline at end of file
<script setup lang="ts">
import { usePay } from '../composables/usePay'
import { Toast } from 'vant'
import { getUser } from '@/api/base'
const props = defineProps({ shopItem: { type: Object, default: () => ({}) } })
const emit = defineEmits(['success'])
const router = useRouter()
const params = $ref({ payment_method: '4' })
const { order, pay } = usePay()
watchEffect(() => {
if (order.value?.order_status === '4') {
// 支付成功
emit('success', order.value)
}
})
// 去支付
function handlePay() {
if (!checked) {
checked = true
Toast('请先勾选学习服务协议')
return
}
const payUrl = getURLParameters(
props.shopItem?.url ||
'https://h5-shop.ezijing.com/buy?shop_id=7238733808088907776&spu_id=7238739080446476288&sku_id=7238739080467447808'
)
const data = {
shop_id: payUrl?.shop_id || '',
spu_id: payUrl?.spu_id || '',
sku_id: payUrl?.sku_id || '',
nUrl: `https://wmpc-show-h5.ezijing.com/v2/index/pay-callback?tenant=wmpc&course_id=${props.shopItem.id}&user_id=${uId}`,
amount: props.shopItem?.prices,
type: params.payment_method
}
pay(params, data)
}
const getURLParameters = (url: any) =>
(url.match(/([^?=&]+)(=([^&]*))/g) || []).reduce(
(a: any, v: any) => ((a[v.slice(0, v.indexOf('='))] = v.slice(v.indexOf('=') + 1)), a),
{}
)
const checkedChange = (val: string) => {
params.payment_method = val
}
let checked = $ref(false)
let uId = $ref(0)
getUser().then((res: any) => {
uId = res.data.info?.sso_id
})
</script>
<template>
<div>
<div class="main_con">
<img :src="shopItem?.course_picture" class="banner" />
<div class="pad">
<div class="c-info">
<div class="c-name">{{ shopItem?.course_name }}</div>
<div class="info">
<div class="t">{{ shopItem?.category_name }}</div>
<div class="r">已学习:{{ shopItem?.pv }}</div>
</div>
<div class="money">
需支付金额:<span>{{ shopItem?.prices }}</span>
</div>
</div>
<div class="pay-method">
<div class="n">支付方式:</div>
<div class="list">
<div :class="params.payment_method === '4' ? 'item active' : 'item'" @click="checkedChange('4')">
<van-icon name="wechat-pay" size="25" color="#5ead48" />
</div>
<div :class="params.payment_method === '12' ? 'item active' : 'item'" @click="checkedChange('12')">
<van-icon name="alipay" size="25" color="#367df0" />
</div>
</div>
</div>
<div class="xy">
<div class="flx">
<van-checkbox v-model="checked" shape="square" icon-size="12px"></van-checkbox>
&nbsp;&nbsp;同意<a
target="_blank"
href="https://webapp-pub.ezijing.com/project_online/fi/%E7%BD%91%E7%BB%9C%E5%9C%A8%E7%BA%BF%E5%AD%A6%E4%B9%A0%E6%9C%8D%E5%8A%A1%E5%8D%8F%E8%AE%AE.pdf"
>《清控紫荆教育学习服务协议》</a
>
</div>
<!-- https://webapp-pub.ezijing.com/project_online/fi/%E7%BD%91%E7%BB%9C%E5%9C%A8%E7%BA%BF%E5%AD%A6%E4%B9%A0%E6%9C%8D%E5%8A%A1%E5%8D%8F%E8%AE%AE.pdf -->
<div class="tips">请同意并勾选《清控紫荆教育学习服务协议》</div>
</div>
</div>
<div class="pay-btn" @click="handlePay">立即支付</div>
</div>
<!-- <div class="main_con">
<div class="con_nav" @click="handlePrev">
<img src="https://webapp-pub.ezijing.com/project_online/fi/prev_mini.png" />
<div class="nav_title">确认订单</div>
</div>
<div class="course_con">
<img :src="shopItem?.image_url" />
<div class="course_dec">
<div class="info_title">{{ shopItem.title }}</div>
<div class="info_date">有效期:{{ start_time }}{{ end_time }}</div>
</div>
</div>
<div class="pay_con">
<div class="pay_phone"></div>
<div class="pay_line"></div>
<div class="pay_mode">
<div class="mode_item">
<img src="https://webapp-pub.ezijing.com/project_online/fi/pay_ali.png" />
<div class="radio_tit">支付宝支付</div>
<div
:class="params.payment_method === '12' ? 'radio_check_active' : 'radio_check'"
@click="checkedChange('12')"
>
<template v-if="params.payment_method === '12'">
</template>
</div>
</div>
<div class="mode_item">
<img src="https://webapp-pub.ezijing.com/project_online/fi/pay_wechat.png" />
<div class="radio_tit">微信支付</div>
<div
:class="params.payment_method === '4' ? 'radio_check_active' : 'radio_check'"
@click="checkedChange('4')"
>
<template v-if="params.payment_method === '4'">
</template>
</div>
</div>
</div>
</div>
<div class="argreement_con">
<div class="left_top">
<el-checkbox v-model="isAgree">
<span>同意</span>
</el-checkbox>
<a
href="https://webapp-pub.ezijing.com/project_online/fi/%E7%BD%91%E7%BB%9C%E5%9C%A8%E7%BA%BF%E5%AD%A6%E4%B9%A0%E6%9C%8D%E5%8A%A1%E5%8D%8F%E8%AE%AE.pdf"
target="_blank"
>学习服务协议</a
>
</div>
<div class="left_desc" :class="isAgreeText === true ? 'left_desc_active' : ''">请先勾选学习服务协议</div>
</div>
<div class="to_pay_main" @click="handlePay">
<div class="pay_price">
<span class="to_pay">立即支付</span>
<span class="to_price"><i style="font-size: 12px">¥</i>{{ shopItem.price }}</span>
</div>
</div>
</div> -->
</div>
</template>
<style lang="scss" scoped>
.pad {
padding: 0 0.2rem;
}
.main_con {
.banner {
width: 100%;
}
.c-info {
padding-top: 0.5rem;
.c-name {
font-weight: bold;
font-size: 0.28rem;
color: #333333;
line-height: 100%;
}
.info {
padding-top: 0.2rem;
display: flex;
justify-content: space-between;
align-items: center;
.t {
font-weight: bold;
font-size: 0.24rem;
color: #ffffff;
line-height: 100%;
background-color: #b51e4d;
padding: 0.08rem 0.19rem;
border-radius: 0.24rem;
}
.r {
font-size: 0.22rem;
color: #999999;
}
}
.money {
padding-top: 0.8rem;
text-align: right;
font-size: 0.24rem;
color: #333333;
span {
font-size: 0.4rem;
color: #aa1941;
}
}
}
.pay-method {
padding-top: 0.8rem;
.n {
font-size: 0.24rem;
color: #333333;
margin-bottom: 0.2rem;
}
.list {
display: flex;
justify-content: space-between;
.item {
display: flex;
justify-content: center;
align-items: center;
width: 3rem;
height: 0.8rem;
background: #ffffff;
border-radius: 0.12rem;
border: 0.01rem solid #fff;
&.active {
border: 0.01rem solid #aa1941;
}
}
}
}
.xy {
margin-top: 0.2rem;
font-size: 0.24rem;
color: #333333;
.flx {
display: flex;
}
.tips {
font-size: 0.2rem;
color: #656565;
padding: 0.1rem 0 0 0.4rem;
}
}
.pay-btn {
width: 4rem;
line-height: 0.8rem;
background: #aa1941;
border-radius: 0.4rem;
text-align: center;
font-weight: bold;
font-size: 0.28rem;
color: #ffffff;
margin: 0.68rem auto;
}
}
</style>
import type { PayOrder, Order } from '../types'
import * as api from '../api'
import { useUserStore } from '@/stores/user'
// import { useShopStore } from '@/stores/shop'
import { useStorage } from '@vueuse/core'
import { useDevice } from '@/composables/useDevice'
// https://h5-shop.ezijing.com/buy?shop_id=7238733808088907776&spu_id=7238739080446476288
const { wechat } = useDevice()
export function usePay() {
const route = useRoute()
const userStore = useUserStore()
// const shopStore = useShopStore()
// 订单信息
const order = ref<Order>()
// 支付的订单信息
const payOrder = ref<PayOrder>()
const openId = useStorage('openId', '')
let redirectURL = $ref(`${location.origin}/shop/result?id=${route.query.id}`)
// 支付
async function pay(options = {}, data: any) {
redirectURL = `${location.origin}/shop/result?id=${route.query.id}&type=${data.type}&am=${data.amount}`
// 默认参数
const defaultParams: Record<string, any> = reactive({
shop_id: data.shop_id,
spu_id: data.spu_id,
sku_id: data.sku_id,
buy_count: '1',
redirect_url: redirectURL,
notify_url: data.nUrl
})
const params = Object.assign(defaultParams, options)
console.log(params, 'params')
if (wechat.value && params.payment_method === '4') {
params.payment_method = '3'
params.open_id = await getOpenId()
}
// 创建订单
await createOrder(params)
if (!payOrder.value) return
// 网页支付,直接唤起对应支付网页
if (params.payment_method === '12') {
location.href = payOrder.value.payment_url
return
}
// 微信h5支付
if (params.payment_method === '4') {
const paymentUrl = `${payOrder.value.payment_url}&redirect_url=${encodeURIComponent(
`https://pages.ezijing.com/base/auth.html?redirect_uri=${encodeURIComponent(redirectURL)}`
)}`
location.href = `https://pages.ezijing.com/base/pay.html?payment_url=${encodeURIComponent(
paymentUrl
)}&redirect_url=${redirectURL}`
return
}
// 微信JS支付
if (params.payment_method === '3') {
wxJSPay()
}
// 检测订单信息
checkOrder()
}
// 创建订单
async function createOrder(params: any) {
const res: any = await api.createOrder(params)
payOrder.value = res
localStorage.setItem('order_detail_id', res.order_detail_id)
await getOrder()
return res
}
// 获取订单信息
async function getOrder() {
if (!payOrder.value) return
const res = await api.getOrderList({ order_detail_id: payOrder.value.order_detail_id })
const currentOrder = res.data[0]
order.value = currentOrder
return currentOrder
}
// 检测订单状态
let timer = 0
async function checkOrder() {
if (!payOrder.value) return
timer && clearInterval(timer)
timer = setInterval(async () => {
const currentOrder = await getOrder()
if (currentOrder?.order_status === '4') clearInterval(timer)
}, 1000)
}
// onUnmounted(() => {
// timer && clearInterval(timer)
// })
// 微信支付相关
// 获取code
function getCode() {
const href = location.href.includes('?') ? `${location.href}&is_pay=true` : `${location.href}?is_pay=true`
const redirectURI = `https://pages.ezijing.com/base/auth.html?redirect_uri=${encodeURIComponent(href)}`
location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx451c01d40d090d7a&redirect_uri=${redirectURI}&response_type=code&scope=snsapi_base`
}
// 获取openId
async function getOpenId() {
if (openId.value) return openId.value
const code = route.query.code || getCode()
return api
.getOpenId({ code, identity: 'ezijing' })
.then((res: any) => {
openId.value = res.openid
return res.data.openid
})
.catch(getCode)
}
function wxJSPay() {
if (!payOrder.value?.payment_more_info) {
alert('订单创建错误')
return
}
location.href = `https://pages.ezijing.com/base/pay.html?payment_more_info=${payOrder.value.payment_more_info}&redirect_url=${redirectURL}`
// const payInfo: any = JSON.parse(payOrder.value.payment_more_info)
// ;(window as any).WeixinJSBridge?.invoke('getBrandWCPayRequest', payInfo, (res: any) => {
// console.log(res)
// })
}
return { order, payOrder, pay }
}
import type { RouteRecordRaw } from 'vue-router'
import AppLayout from '@/components/layout/Index.vue'
export const routes: Array<RouteRecordRaw> = [
{
path: '/shop/pay',
component: AppLayout,
children: [
{
path: '/shop/pay',
component: () => import('./views/Index.vue'),
props: true,
meta: { loginRequired: true }
},
{
path: '/shop/result',
component: () => import('./views/Result.vue'),
props: true,
meta: { loginRequired: true }
}
]
}
]
// 创建订单之后生成的订单信息
export interface PayOrder {
order_id: string
order_detail_id: string
payment_order_id: string
pay_order_id: string
payment_url: string
payment_more_info: string
}
// 订单信息
export interface Order {
order_id: string
order_detail_id: string
shop_id: string
spu_id: string
sku_id: string
spu_name: string
chart_oss: string
buy_count: string
customer_id: string
customer_phone: string
product_price: string
district_money: string
payment_money: string
shipping_user: string
shipping_phone: string
payment_method: string
app_button_text: string
payment_order_id: string
pay_order_id: string
create_time: string
order_status: string
update_time: string
pay_time: string
}
\ No newline at end of file
<script setup lang="ts">
import type { Order } from '../types'
import { getCourseDetail } from '../api'
// import { useShopStore } from '@/stores/shop'
import PayH5 from '../components/PayH5.vue'
const route = useRoute()
const router = useRouter()
// const { shopItem } = useShopStore()
// const { mobile } = useDevice()
// const router = useRouter()
const handleSuccess = (data: Order) => {
router.replace({
path: `/shop/result?id=${route.query.id}`,
query: { order_detail_id: data.order_detail_id }
})
}
let data: any = reactive({
course: {}
})
getCourseDetail({ id: route.query?.id as string }).then((res: any) => {
const d = res.data.course
if (d.chapters?.length) {
d.chapters = d.chapters.map((item: any) => {
item.show = false
return item
})
}
data.course = d
})
</script>
<template>
<AppContainer :title="data.course?.course_name || ''" headerAlign="center"></AppContainer>
<div class="pay_main">
<PayH5 v-if="Object.keys(data.course).length" :shopItem="data.course" @success="handleSuccess"></PayH5>
</div>
</template>
<style lang="scss" scoped>
.pay_main {
background: #f7f8fa;
}
</style>
<script setup lang="ts">
import type { Order } from '../types'
import { getOrderList } from '../api'
const router = useRouter()
const route = useRoute()
const orderId = useStorage('order_detail_id', route.query.order_detail_id)
const order = ref<Order>()
let isPay = $ref(false)
async function getOrder() {
getOrderList({ order_detail_id: orderId.value })
.then(res => {
order.value = res.data[0]
if (order.value?.order_status === '4') {
// 支付成功
// 刷新已购买的课程列表
// useUserStore().getCourse()
// useUserStore().addCourse({ course_id: shopItem?.course_id })
isPay = true
} else {
// 未支付,返回支付页面
router.replace(`/shop/pay?id=${route.query.id}`)
}
})
.catch(() => {
router.replace(`/shop/pay?id=${route.query.id}`)
})
}
onMounted(() => {
getOrder()
})
const goCourse = function () {
router.replace(`/course/detail?id=${route.query.id}`)
}
</script>
<template>
<div v-if="isPay">
<div class="info-box">
<div style="display: flex; justify-content: center">
<van-icon name="wechat-pay" size="60" color="#5ead48" v-if="$route.query.type === '4'" />
<van-icon name="alipay" size="60" color="#367df0" v-else />
</div>
<div class="t1">支付成功</div>
<div class="t2">{{ $route.query.am }}</div>
</div>
<div class="scu-btn" @click="goCourse">返回课程</div>
</div>
</template>
<style lang="scss" scoped>
.info-box {
position: absolute;
top: 30%;
left: 50%;
transform: translate(-50%);
.t1 {
text-align: center;
color: #000;
font-size: 0.4rem;
padding: 0.3rem 0;
}
.t2 {
font-size: 0.7rem;
color: #000;
text-align: center;
}
}
.scu-btn {
position: absolute;
bottom: 1rem;
left: 50%;
transform: translateX(-50%);
width: 4rem;
line-height: 0.8rem;
background: #aa1941;
border-radius: 0.4rem;
text-align: center;
font-weight: bold;
font-size: 0.28rem;
color: #ffffff;
// margin: 0.68rem auto;
}
</style>
import * as api from '@/api/base'
/**
* 获取微信openId
* */
export function getCode() {
const href = location.href.includes('?') ? `${location.href}&is_pay=true` : `${location.href}?is_pay=true`
const redirectURI = `https://pages.ezijing.com/given/auth.html?redirect_uri=${encodeURIComponent(href)}`
location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx451c01d40d090d7a&redirect_uri=${redirectURI}&response_type=code&scope=snsapi_base`
}
/**
* 获取微信openId
* */
export function getOpenId(code, callback) {
if (!code) {
return getCode()
}
api.getOpenId({ code, identity: 'ezijing' }).then((resp) => {
const openId = resp.data.openid
openId && localStorage.setItem('openId', openId)
callback && callback(openId)
})
}
/**
* 微信JSAPI支付
* */
export function wxJSPay(order, callback) {
if (!order.payment_more_info) {
alert('订单创建错误')
return
}
const payInfo = JSON.parse(order.payment_more_info)
window.WeixinJSBridge.invoke('getBrandWCPayRequest', payInfo, (resp) => {
callback && callback(resp)
})
}
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论