This commit is contained in:
xingyy 2025-03-03 10:35:52 +08:00
commit e49b6a78d4
38 changed files with 719 additions and 220 deletions

View File

@ -1,4 +1,4 @@
import { request } from '@/api/http.js'
import { request } from '@/api-collect-code/http.js'
export async function checkPhone(data) {
return await request({
@ -9,7 +9,7 @@ export async function checkPhone(data) {
}
export async function userSend(data) {
return await request( {
url:'/api/v1/m/user/send',
url:'/api/v1/m/user/mobile/send',
method: 'POST',
data
})

View File

@ -1,4 +1,4 @@
import { request } from '@/api/http.js'
import { request } from '@/api-collect-code/http.js'
export async function offlineQrcodeList(data) {
return await request( {
@ -56,7 +56,7 @@ export async function offlineQrcode(data) {
export async function createOrder(data) {
return await request( {
url:'/api/v1/offlineQrcode/createOrder',
url:'/api/v1/offlineQrcode/createOrder/V2',
method: 'POST',
data
})

View File

@ -9,9 +9,9 @@ let http
// HTTP 状态码映射 - 使用i18n国际化
export function setupHttp() {
if (http) return http
const {token}= codeAuthStore()
const {codeToken}= codeAuthStore()
const config = useRuntimeConfig()
const baseURL = config.public.NUXT_PUBLIC_API_COLLECT_CODE
const baseURL = config.public.NUXT_PUBLIC_API_BASE
const router = useRouter()
const i18n = useNuxtApp().$i18n
@ -43,7 +43,7 @@ export function setupHttp() {
// 添加 token
options.headers = {
...options.headers,
Authorization: token.value,
Authorization: codeToken.value,
'accept-language': i18n.locale.value
}
@ -64,11 +64,11 @@ export function setupHttp() {
if (data.status === 1) {
message.error(data.msg || i18n.t('http.error.operationFailed'))
}
console.log('拦截响应',data)
// 处理登录失效
if (data.status === 401) {
message.error(i18n.t('http.error.loginExpired'))
token.value = '' // 清除 token
codeToken.value = '' // 清除 token
router.replace('/collectCode/login')
}

View File

@ -31,7 +31,7 @@ const subTitle = computed(() => {
return route.meta.subTitle ? t(route.meta.subTitle) : ''
})
const showLeftArrow = computed(() => route.name && routeWhiteList.includes(route.name))
console.log('route.name',route.name)
</script>
<template>

View File

@ -0,0 +1,276 @@
<template>
<div class="stripe-container">
<form id="payment-form" @submit.prevent="handleSubmit">
<div id="payment-element">
<!--Stripe.js injects the Payment Element-->
</div>
<button id="submit">
<div class="spinner hidden" id="spinner"></div>
<span id="button-text">Pay now</span>
</button>
<div id="payment-message" class="hidden"></div>
</form>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
// The items the customer wants to buy
const items = [{ id: "xl-tshirt", amount: 1000 }]
const stripe = ref(null)
const elements = ref(null)
const isLoading = ref(false)
onMounted(async () => {
try {
// Stripe
stripe.value = window.Stripe("pk_test_51QfbSAAB1Vm8VfJq3AWsR4k2mZjnlF7XFrmlbc6XVXrtwXquAUfwzZmOFDbxMIAwqJBgqao8KLt2wmPc4vNOCTeo00WB78KtfV")
await initialize()
} catch (error) {
showMessage("Failed to initialize payment system")
}
})
// Fetches a payment intent and captures the client secret
const initialize = async () => {
try {
const response = await fetch("/create-payment-intent", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ items }),
})
const { clientSecret } = await response.json()
const appearance = {
theme: 'stripe',
}
// elements
elements.value = stripe.value.elements({
appearance,
clientSecret,
})
// Payment Element
const paymentElement = elements.value.create("payment", {
layout: "accordion",
})
// DOM
const mountElement = document.getElementById("payment-element")
if (mountElement) {
paymentElement.mount(mountElement)
} else {
throw new Error("Payment element mount point not found")
}
} catch (error) {
showMessage("Failed to load payment form")
}
}
const handleSubmit = async () => {
if (!stripe.value || !elements.value) {
showMessage("Payment system not initialized")
return
}
setLoading(true)
try {
const { error } = await stripe.value.confirmPayment({
elements: elements.value,
confirmParams: {
return_url: window.location.origin + "/complete.html",
},
})
if (error) {
if (error.type === "card_error" || error.type === "validation_error") {
showMessage(error.message)
} else {
showMessage("An unexpected error occurred.")
}
}
} catch (e) {
showMessage("Payment processing failed")
} finally {
setLoading(false)
}
}
const showMessage = (messageText) => {
const messageContainer = document.querySelector("#payment-message")
messageContainer.classList.remove("hidden")
messageContainer.textContent = messageText
setTimeout(() => {
messageContainer.classList.add("hidden")
messageContainer.textContent = ""
}, 4000)
}
// Show a spinner on payment submission
const setLoading = (isLoading) => {
if (isLoading) {
// Disable the button and show a spinner
document.querySelector("#submit").disabled = true
document.querySelector("#spinner").classList.remove("hidden")
document.querySelector("#button-text").classList.add("hidden")
} else {
document.querySelector("#submit").disabled = false
document.querySelector("#spinner").classList.add("hidden")
document.querySelector("#button-text").classList.remove("hidden")
}
}
</script>
<style scoped>
.stripe-container {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
font-size: 16px;
-webkit-font-smoothing: antialiased;
display: flex;
flex-direction: column;
justify-content: center;
align-content: center;
height: 100vh;
width: 100vw;
}
form {
width: 30vw;
min-width: 500px;
align-self: center;
box-shadow: 0px 0px 0px 0.5px rgba(50, 50, 93, 0.1),
0px 2px 5px 0px rgba(50, 50, 93, 0.1), 0px 1px 1.5px 0px rgba(0, 0, 0, 0.07);
border-radius: 7px;
padding: 40px;
margin-top: auto;
margin-bottom: auto;
}
.hidden {
display: none;
}
#payment-message {
color: rgb(105, 115, 134);
font-size: 16px;
line-height: 20px;
padding-top: 12px;
text-align: center;
}
#payment-element {
margin-bottom: 24px;
}
button {
background: #0055DE;
font-family: Arial, sans-serif;
color: #ffffff;
border-radius: 4px;
border: 0;
padding: 12px 16px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
display: block;
transition: all 0.2s ease;
box-shadow: 0px 4px 5.5px 0px rgba(0, 0, 0, 0.07);
width: 100%;
}
button:hover {
filter: contrast(115%);
}
button:disabled {
opacity: 0.5;
cursor: default;
}
.spinner,
.spinner:before,
.spinner:after {
border-radius: 50%;
}
.spinner {
color: #ffffff;
font-size: 22px;
text-indent: -99999px;
margin: 0px auto;
position: relative;
width: 20px;
height: 20px;
box-shadow: inset 0 0 0 2px;
-webkit-transform: translateZ(0);
-ms-transform: translateZ(0);
transform: translateZ(0);
}
.spinner:before,
.spinner:after {
position: absolute;
content: "";
}
.spinner:before {
width: 10.4px;
height: 20.4px;
background: #0055DE;
border-radius: 20.4px 0 0 20.4px;
top: -0.2px;
left: -0.2px;
-webkit-transform-origin: 10.4px 10.2px;
transform-origin: 10.4px 10.2px;
-webkit-animation: loading 2s infinite ease 1.5s;
animation: loading 2s infinite ease 1.5s;
}
.spinner:after {
width: 10.4px;
height: 10.2px;
background: #0055DE;
border-radius: 0 10.2px 10.2px 0;
top: -0.1px;
left: 10.2px;
-webkit-transform-origin: 0px 10.2px;
transform-origin: 0px 10.2px;
-webkit-animation: loading 2s infinite ease;
animation: loading 2s infinite ease;
}
@-webkit-keyframes loading {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes loading {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@media only screen and (max-width: 600px) {
form {
width: 80vw;
min-width: initial;
}
}
</style>

View File

@ -18,7 +18,6 @@ let container = null // DOM容器元素
*/
export const showMinWindow1 = (props = {}) => {
// 服务端渲染时直接返回
console.log('!process.client',!process.client)
if (!process.client) return null
// 如果实例已存在,避免重复创建
@ -60,7 +59,6 @@ export const showMinWindow1 = (props = {}) => {
return minWindowInstance
} catch (error) {
console.error('创建浮动气泡时发生错误:', error)
// 发生错误时确保清理资源
hideMinWindow1()
return null
@ -72,7 +70,7 @@ export const showMinWindow1 = (props = {}) => {
* 清理所有相关资源和DOM元素
*/
export const hideMinWindow1 = () => {
console.log('!minWindowApp && !container', !minWindowApp && !container);
if (!minWindowApp && !container) return
@ -93,8 +91,7 @@ export const hideMinWindow1 = () => {
document.body.removeChild(existingContainer)
}
} catch (error) {
console.error('清理浮动气泡时发生错误:', error)
} finally {
} finally {
// 重置所有状态
minWindowApp = null
minWindowInstance = null

View File

@ -28,7 +28,6 @@ export const showMinWindow = (snapshot, props = {}) => {
})
app.config.errorHandler = (err) => {
console.error('MinWindow Error:', err)
hideMinWindow()
}

View File

@ -15,8 +15,7 @@ const initData = async () => {
const res = await userArtwork({uuid})
if (res.status === 0) {
detail.value = res.data
console.log('detail',detail.value)
}
}
}
const router = useRouter();
const position = ref({x: window?.innerWidth - 120 || 0, y: 240})
@ -59,8 +58,7 @@ const goPay=()=>{
}else if (detail.value.status===4){
router.push('/payment')
}
console.log('detail',detail.value)
//router.push('/payment')
//router.push('/payment')
}
onMounted(() => {
document.addEventListener('mousemove', onDrag)

View File

@ -1,26 +1,72 @@
<script setup>
import { onMounted, ref } from 'vue'
import {authStore} from "~/stores/auth/index.js";
const {checkoutSessionUrl,payUid}= authStore()
import {orderQuery} from "~/api/goods/index.js";
import { WebSocketClient } from '@/utils/websocket'
const config = useRuntimeConfig()
definePageMeta({
layout: 'default',
title: 'Stripe支付'
})
console.log('config.public.NUXT_PUBLIC_PKEY',config.public.NUXT_PUBLIC_PKEY);
const stripe = Stripe(config.public.NUXT_PUBLIC_PKEY)
const route = useRoute()
const baseURL = config.public.NUXT_PUBLIC_API_BASE
const items = [{ id: "xl-tshirt", amount: 1000 }]
const elements = ref(null)
const paymentMessage = ref('')
const isLoading = ref(false)
const showSpinner = ref(false)
let pollTimer = null
let timeoutTimer = null
const router = useRouter()
const startPolling = () => {
pollTimer = setInterval(async () => {
const res = await orderQuery({
orderNo: route.query.payUid
})
if (res.status === 0) {
if (res.data.status !== 3) {
clearInterval(pollTimer)
clearTimeout(timeoutTimer)
router.replace({
path: route.query.returnUrl,
query: {
orderNo: route.query.payUid
}
})
}
}
}, 1000)
/* timeoutTimer = setTimeout(() => {
clearInterval(pollTimer)
setLoading(false)
}, 180000)*/
}
let wsClient=null
const watchWebSocket = () => {
wsClient = new WebSocketClient(
config.public.NUXT_PUBLIC_SOCKET_URL
)
const ws = wsClient.connect('/api/v1/order/ws/v2', {
PayUid: route.query.payUid,
})
ws.onOpen(() => {
})
ws.onMessage((event) => {
router.replace({
path: route.query.returnUrl,
query: {
orderNo: route.query.payUid
}
})
})
ws.onClose(() => {
})
}
async function initialize() {
const clientSecret = checkoutSessionUrl.value
console.log('clientSecret',clientSecret);
const clientSecret = route.query.stripeKey
const appearance = {
theme: 'stripe',
}
@ -38,20 +84,25 @@ async function handleSubmit(e) {
e.preventDefault()
setLoading(true)
const { error } = await stripe.confirmPayment({
elements: elements.value,
confirmParams: {
return_url: "http://192.168.88.68:3000/payment/result?orderNo="+payUid.value,
return_url: `${baseURL}${route.query.returnUrl}?orderNo=${route.query.payUid}`,
},
})
if (error.type === "card_error" || error.type === "validation_error") {
showMessage(error.message)
} else {
showMessage("An unexpected error occurred.")
if (error) {
/* clearInterval(pollTimer)
clearTimeout(timeoutTimer)*/
if (error.type === "card_error" || error.type === "validation_error") {
showMessage(error.message)
} else {
showMessage("An unexpected error occurred.")
}
setLoading(false)
}
setLoading(false)
}
function showMessage(messageText) {
@ -66,8 +117,17 @@ function setLoading(loading) {
showSpinner.value = loading
}
onUnmounted(()=>{
wsClient.disconnect()
clearTimeout(timeoutTimer)
clearInterval(pollTimer)
})
onMounted(() => {
watchWebSocket()
initialize()
startPolling()
})
</script>
@ -339,4 +399,4 @@ form {
}
/* 其他样式保持不变... */
</style>
</style>

View File

@ -8,7 +8,7 @@ import {message} from '@/components/x-message/useMessage.js'
import FingerprintJS from '@fingerprintjs/fingerprintjs'
import {checkPhone, mobileLogin, userSend} from "@/api-collect-code/auth/index.js";
const {userInfo, token, fingerprint} = codeAuthStore()
const {userInfo, codeToken, fingerprint} = codeAuthStore()
const router = useRouter();
const route = useRoute();
const {locale} = useI18n()
@ -36,7 +36,6 @@ const countdown = ref(0);
const phoneNum = ref('')
const code = ref('')
const pane = ref(0)
const showKeyboard = ref(false);
const getFingerprint = async () => {
const fp = await FingerprintJS.load()
const result = await fp.get()
@ -50,36 +49,29 @@ const checkFingerprint = async () => {
await router.push('/collectCode/mine')
}
}
const codeInput = ref(null)
const isFocused = ref(false)
checkFingerprint()
const vanSwipeRef = ref(null)
const getCode = async () => {
loadingRef.value.loading1 = true
const res = await checkPhone({
tel: phoneNum.value,
})
loadingRef.value.loading1 = false
if (res.status === 0) {
const res = await userSend({telNum: phoneNum.value, zone: '+86'})
try {
const res = await checkPhone({
tel: phoneNum.value,
})
if (res.status === 0) {
pane.value = 1
vanSwipeRef.value?.swipeTo(pane.value)
showKeyboard.value = true
const sendRes = await userSend({telNum: phoneNum.value, zone: '+86'})
startCountdown()
pane.value = 1
await nextTick()
vanSwipeRef.value?.swipeTo(pane.value)
}
} catch (error) {
console.error('获取验证码失败:', error)
} finally {
loadingRef.value.loading1 = false
}
/* loadingRef.value.loading1 = false
if (res.status === 0) {
}
pane.value = 1
vanSwipeRef.value?.swipeTo(pane.value)
showKeyboard.value = true
startCountdown();*/
/* pane.value = 1
vanSwipeRef.value?.swipeTo(pane.value)
showKeyboard.value=true
startCountdown();*/
}
const changeToPwd = async () => {
loginType.value = loginType.value === 0 ? 1 : 0
@ -98,7 +90,7 @@ const goLogin = async () => {
})
if (res.status === 0) {
userInfo.value = res.data.accountInfo
token.value = res.data.token
codeToken.value = res.data.token
fingerprint.value = await getFingerprint()
await router.push('/collectCode/mine');
@ -114,98 +106,124 @@ const togglePasswordVisibility = () => {
</script>
<template>
<div class="h-[100vh] w-[100vw] bg-[url('@/static/images/asdfsdd.png')] bg-cover px-[31px] pt-[86px]">
<div class="grow-1 w-[100vw] bg-[url('@/static/images/asdfsdd.png')] bg-bottom bg-cover px-[31px] pt-[86px]">
<div class="w-full flex justify-center mb-[100px] flex-col items-center">
<img class="h-[105px] w-[189px]" src="@/static/images/ghfggff.png" alt="">
<img class="h-[29px] w-[108px]" src="@/static/images/qrcodetext.png" alt="">
</div>
<van-swipe ref="vanSwipeRef" :show-indicators="false" :touchable="false" :lazy-render="true" :loop="false">
<van-swipe-item>
<div v-show="pane === 0">
<div class="">
<div class="border-b-[1.7px] mt-[8px]">
<van-field v-model="phoneNum" clearable :placeholder="$t('collectCode.login.phoneNumberPlaceholder')">
<template #label>
<div class="text-[16px] text-[#1A1A1A] flex align-center justify-start">
{{ $t('collectCode.login.phoneNumber') }}
</div>
</template>
</van-field>
</div>
<div class="border-b-[1.7px] mt-[8px]" v-show="loginType === 1">
<van-field
v-model="password"
:type="showPassword ? 'text' : 'password'"
clearable
:placeholder="$t('collectCode.login.passwordPlaceholder')"
>
<template #label>
<div class="text-[16px] text-[#1A1A1A] flex align-center justify-start">
{{ $t('collectCode.login.password') }}
</div>
</template>
<template #button>
<div class="flex justify-center items-center">
<van-icon
size="20"
:name="showPassword ? 'eye-o' : 'closed-eye'"
@click="togglePasswordVisibility"
/>
</div>
</template>
</van-field>
</div>
<div class="flex justify-end mt-[10px]">
<div class="text-[14px] text-[#2B53AC]" @click="changeToPwd">
{{ loginType === 0 ? $t('collectCode.login.passwordLogin') : $t('collectCode.login.codeLogin') }}
<div v-if="pane === 0">
<div>
<div class="">
<div class="border-b-[1.7px] mt-[8px]">
<van-field v-model="phoneNum" clearable :placeholder="$t('collectCode.login.phoneNumberPlaceholder')">
<template #label>
<div class="text-[16px] text-[#1A1A1A] flex align-center justify-start">
{{ $t('collectCode.login.phoneNumber') }}
</div>
</template>
</van-field>
</div>
<div class="border-b-[1.7px] mt-[8px]" v-if="loginType === 1">
<van-field
v-model="password"
:type="showPassword ? 'text' : 'password'"
clearable
:placeholder="$t('collectCode.login.passwordPlaceholder')"
>
<template #label>
<div class="text-[16px] text-[#1A1A1A] flex align-center justify-start">
{{ $t('collectCode.login.password') }}
</div>
</template>
<template #button>
<div class="flex justify-center items-center">
<van-icon
size="20"
:name="showPassword ? 'eye-o' : 'closed-eye'"
@click="togglePasswordVisibility"
/>
</div>
</template>
</van-field>
</div>
<div class="flex justify-end mt-[10px]">
<div class="text-[14px] text-[#2B53AC]" @click="changeToPwd">
{{ loginType === 0 ? $t('collectCode.login.passwordLogin') : $t('collectCode.login.codeLogin') }}
</div>
</div>
</div>
<div/>
</div>
<div class="mt-[55px]">
<div v-if="loginType === 0">
<van-button :loading="loadingRef.loading1" v-if="phoneNum" :loading-text="$t('collectCode.login.getCode')"
type="primary" block style="height: 48px" @click="getCode">{{ $t('collectCode.login.getCode') }}
</van-button>
<van-button v-else type="primary" color="#D3D3D3" block style="height: 48px">{{ $t('collectCode.login.getCode') }}</van-button>
</div>
<div v-else>
<van-button type="primary" v-if="password" block :loading="loadingRef.loading2" :loading-text="$t('collectCode.login.login')"
style="height: 48px;margin-top:10px" @click="goLogin">{{ $t('collectCode.login.login') }}
</van-button>
<van-button v-else type="primary" color="#D3D3D3" block style="height: 48px">{{ $t('collectCode.login.login') }}</van-button>
<div class="mt-[55px]">
<div v-if="loginType === 0">
<van-button :loading="loadingRef.loading1" v-if="phoneNum" :loading-text="$t('collectCode.login.getCode')"
type="primary" block style="height: 48px" @click="getCode">{{
$t('collectCode.login.getCode')
}}
</van-button>
<van-button v-else type="primary" color="#D3D3D3" block style="height: 48px">
{{ $t('collectCode.login.getCode') }}
</van-button>
</div>
<div v-else>
<van-button type="primary" v-if="password" block :loading="loadingRef.loading2"
:loading-text="$t('collectCode.login.login')"
style="height: 48px;margin-top:10px" @click="goLogin">{{ $t('collectCode.login.login') }}
</van-button>
<van-button v-else type="primary" color="#D3D3D3" block style="height: 48px">
{{ $t('collectCode.login.login') }}
</van-button>
</div>
</div>
</div>
</div>
</van-swipe-item>
<van-swipe-item>
<div v-show="pane === 1">
<div class="flex mb-[16px]">
<div class="text-[16px] text-[#BDBDBD] mr-[10px]">{{ $t('collectCode.login.hasSendTo') }}</div>
<div class="text-[16px] text-[#000]">+86 {{ phoneNum }}</div>
</div>
<van-password-input :value="code" :gutter="10" :mask="false" focused @focus="showKeyboard = true"/>
<div class="flex justify-between">
<div :class="`${countdown>0?'text-#BDBDBD':'text-#2B53AC'} text-14px`">
{{ $t('collectCode.login.reSend') }}<span v-if="countdown>0">({{ countdown }})</span>
<div v-if="pane == 1">
<div>
<div class="flex mb-[16px]">
<div class="text-[16px] text-[#BDBDBD] mr-[10px]">{{ $t('collectCode.login.hasSendTo') }}</div>
<div class="text-[16px] text-[#000]">+86 {{ phoneNum }}</div>
</div>
<div @click="goBack" class="text-#2B53AC text-14px">
{{ $t('collectCode.login.back') }}
<div class="relative">
<van-password-input
:value="code"
:gutter="10"
:mask="false"
:focused="isFocused"
/>
<input
v-model="code"
type="tel"
maxlength="6"
ref="codeInput"
class="opacity-0 absolute top-0 left-0 h-full w-full z-999"
@input="code = $event.target.value.replace(/\D/g, '').slice(0, 6)"
@focus="isFocused = true"
@blur="isFocused = false"
/>
</div>
<div class="flex justify-between">
<div :class="`${countdown>0?'text-#BDBDBD':'text-#2B53AC'} text-14px`">
{{ $t('collectCode.login.reSend') }}<span v-if="countdown>0">({{ countdown }})</span>
</div>
<div @click="goBack" class="text-#2B53AC text-14px">
{{ $t('collectCode.login.back') }}
</div>
</div>
<div class="mt-[17px]">
<van-button v-if="code.length === 6" type="primary" block :loading="loadingRef.loading2"
:loading-text="$t('collectCode.login.login')" style="height: 48px" @click="goLogin">
{{ $t('collectCode.login.login') }}
</van-button>
<van-button v-else type="primary" color="#D3D3D3" block style="height: 48px">
{{ $t('collectCode.login.login') }}
</van-button>
</div>
</div>
<div class="mt-[17px]">
<van-button v-if="code.length === 6" type="primary" block :loading="loadingRef.loading2"
:loading-text="$t('collectCode.login.login')" style="height: 48px" @click="goLogin">
{{ $t('collectCode.login.login') }}
</van-button>
<van-button v-else type="primary" color="#D3D3D3" block style="height: 48px">
{{ $t('collectCode.login.login') }}
</van-button>
</div>
</div>
</van-swipe-item>
</van-swipe>
<van-number-keyboard v-model="code" :show="showKeyboard" @blur="showKeyboard = false"/>
</div>
</template>

View File

@ -33,7 +33,6 @@ const getQRBase64 = async () => {
errorCorrectionLevel: 'H'
})
} catch (err) {
console.error('生成二维码失败:', err)
return null
}
}
@ -45,7 +44,26 @@ const openQrCode=async ()=>{
QRUrl.value=base64
show.value=true
}
/**
* 将数字格式化为"250XX"格式其中XX是两位数
* @param {number} num - 要格式化的数字
* @return {string} - 格式化后的字符串
*/
function formatNumber(num) {
//
if (typeof num !== 'number' && isNaN(Number(num))) {
throw new Error('输入必须是有效数字');
}
// ()
const number = Number(num);
// 0
const formattedNum = number.toString().padStart(2, '0');
// "250"
return `250${formattedNum}`;
}
</script>
<template>
@ -59,7 +77,7 @@ const openQrCode=async ()=>{
<XImage class="w-57px h-56px rounded-4px" :src="data.hdPic"></XImage>
</div>
<div class="text-12px text-#1E1E1E">
<div>{{ $t('collectCode.qrcode.card.lotNo') }}{{ data.lotNo }}</div>
<div>{{ $t('collectCode.qrcode.card.lotNo') }}{{ formatNumber(data.lotNo) }}</div>
<div>{{ $t('collectCode.qrcode.card.creator') }}{{ data.userName }}</div>
<div>{{ $t('collectCode.qrcode.card.createTime') }}{{data.createdAt}}</div>
</div>

View File

@ -30,7 +30,6 @@ const initData = async () => {
}
const show=ref(false)
const close=()=>{
console.log('show',show.value)
show.value=false
}
const logOut=()=>{
@ -81,8 +80,11 @@ const loadMore = async () => {
const abnormal=ref(false)
const abnormalRow=ref({})
const inputLotNo=async (data)=>{
if (createForm.value.lotNo<=25000){
return
}
const res=await offlineQrcodeList({
lotNo:createForm.value.lotNo
lotNo:createForm.value.lotNo-25000
})
if (res.status===0){
if (res.data.Data?.length>0){

View File

@ -10,7 +10,7 @@ import {codeAuthStore} from "~/stores-collect-code/auth/index.js";
import {useI18n} from "vue-i18n";
const {t} = useI18n();
const {checkoutSessionUrl,qrUid,qrData} = codeAuthStore()
const {checkoutSessionUrl,qrUid,qrData,codePKey,codePayUid} = codeAuthStore()
const payStatus = ref(0)
definePageMeta({
i18n: 'payment.title'
@ -19,6 +19,7 @@ const changePayStatus = () => {
payStatus.value = payStatus.value === 0 ? 1 : 0
}
const amount = ref('')
const router = useRouter()
const confirmPay = async () => {
if (payStatus.value === 1 && !amount.value) {
message.warning(t('collectCode.payment.enterAmount'))
@ -40,7 +41,18 @@ const confirmPay = async () => {
testReturnEndPoint: '/collectCode/payment/result'
})
if (res.status === 0) {
window.location.href = res.data.checkoutSessionUrl
codePKey.value=res.data.checkoutSessionUrl
codePayUid.value=res.data.payUid
router.push({
path:'/checkoutPage',
query:{
payUid:res.data.payUid,
returnUrl:'/collectCode/payment/result',
stripeKey:res.data.checkoutSessionUrl
}
})
}
}
@ -75,8 +87,8 @@ const handleInput = (e) => {
{{ qrData?.leftPrice }}
</div>
<div class="mb-12px" v-else>
<input v-model="amount" class="w-272px h-48px bg-#F3F3F3 px-11px text-16px" type="text"
:placeholder="$t('collectCode.payment.maxAmount', { currency: qrData.currency, price: qrData?.leftPrice })" @input="handleInput">
<input v-model="amount" class="w-272px h-48px bg-#F3F3F3 px-11px text-16px" type="text"
:placeholder="`${$t('collectCode.payment.maxAmount')} ${qrData.currency} ${qrData?.leftPrice}`" @input="handleInput">
</div>
<div class="text-#2B53AC text-14px" @click="changePayStatus">{{ payStatus === 1 ? $t('collectCode.payment.fullPayment') : $t('collectCode.payment.partialPayment') }}</div>
<div class="w-full mt-auto mb-40px">

View File

@ -39,8 +39,7 @@ const fetchPmblPdf = async () => {
})
pmblUrl.value = res.data?.viewUrl // PDF URLdata
} catch (error) {
console.error('获取拍卖笔录失败:', error)
}
}
}
//

View File

@ -8,7 +8,6 @@ definePageMeta({
i18n: 'countryRegion.title',
})
const router = useRouter()
console.log('router',router)
const { t, locale } = useI18n()
const value = ref('');
const alphabet = computed(() => {
@ -144,7 +143,6 @@ const handleCountrySelect = (country) => {
}
initData()
console.log('searchCountry',searchCountry.value)
//
watch(locale, () => {
initData()

View File

@ -40,6 +40,26 @@ const openShow = async (item) => {
localState.value.showDetail = true
currentItem.value = item
}
/**
* 将数字格式化为"250XX"格式其中XX是两位数
* @param {number} num - 要格式化的数字
* @return {string} - 格式化后的字符串
*/
function formatNumber(num) {
//
if (typeof num !== 'number' && isNaN(Number(num))) {
throw new Error('输入必须是有效数字');
}
// ()
const number = Number(num);
// 0
const formattedNum = number.toString().padStart(2, '0');
// "250"
return `250${formattedNum}`;
}
</script>
<template>
@ -73,7 +93,7 @@ const openShow = async (item) => {
<div
class="absolute rounded-2px overflow-hidden line-height-12px left-[8px] top-[8px] h-[17px] w-[55px] flex items-center justify-center bg-[#2b53ac] text-[12px] text-[#fff]"
>
Lot{{ item.index }}
Lot{{ formatNumber(item.index) }}
</div>
</div>
<div class="pt-[8px]">

View File

@ -30,7 +30,6 @@ const captureVideoFrame = () => {
try {
const video = document.querySelector('#J_prismPlayer video')
if (!video) {
console.error('未找到视频元素')
return null
}
@ -42,7 +41,6 @@ const captureVideoFrame = () => {
ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
return canvas.toDataURL('image/jpeg', 0.9)
} catch (error) {
console.error('获取视频截图失败:', error)
return null
}
}

View File

@ -118,7 +118,6 @@ const captureVideoFrame = () => {
try {
const video = document.querySelector('#J_prismPlayer video')
if (!video) {
console.error('未找到视频元素')
return null
}
@ -130,7 +129,6 @@ const captureVideoFrame = () => {
ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
return canvas.toDataURL('image/jpeg', 0.9)
} catch (error) {
console.error('获取视频截图失败:', error)
return null
}
}
@ -144,8 +142,7 @@ const handleCapture = () => {
onClick:()=>{
router.replace('/')
fullLive.value=true
console.log('执行')
}
}
})*!/
}*/
showMinWindow1({

View File

@ -66,8 +66,7 @@ const loadMore = async () => {
watch(()=>{
return auctionData.value?.artwork?.index
},(newValue)=>{
console.log('newValue',newValue)
})
})
watch(()=>props.show,(newValue)=>{
if (newValue){
nextTick(()=>{

View File

@ -19,7 +19,6 @@ const player = ref(null)
const {quoteStatus, show, playerId, show1, auctionData, getSocketData, getLiveLink, fullLive} = liveStore()
const pullLink = ref('')
const handlePlayerError = (error) => {
console.error('播放器错误:', error)
showConfirmDialog({
message: t('live_room.error_mess'),
showCancelButton: true
@ -81,8 +80,7 @@ const initializePlayer = async () => {
})
player.value.on('loading', () => {
console.log('loading')
})
})
player.value.on('error', handlePlayerError)
} catch (error) {
showConfirmDialog({
@ -92,8 +90,7 @@ const initializePlayer = async () => {
initializePlayer()
}).catch(() => {
})
console.error('播放器初始化失败:', error)
}
}
}

View File

@ -19,6 +19,7 @@ const loadingRef=ref({
})
const isExist=ref(false)// true
const isReal=ref(false) //isReal
const codeInput=ref(null)
function goToPage() {
router.push('/countryRegion');
}
@ -40,11 +41,9 @@ const countdown = ref(0);
const phoneNum = ref('')
const code = ref('')
const pane = ref(0)
const showKeyboard = ref(false);
//
const getDefaultCountry = () => {
let defaultCode = 'CN' //
console.log('locale.value',locale.value)
switch (locale.value) {
case 'zh-CN':
defaultCode = 'CN'
@ -74,7 +73,6 @@ const defaultCountry = getDefaultCountry()
const selectedCountry = ref(route.query.countryName || defaultCountry.name)
console.log('selectedCountry',selectedCountry.value)
onMounted(()=>{
selectedZone.value=route.query.zone || defaultCountry.zone
})
@ -100,13 +98,8 @@ const getCode =async () => {
}
pane.value = 1
vanSwipeRef.value?.swipeTo(pane.value)
showKeyboard.value=true
startCountdown();
/* pane.value = 1
vanSwipeRef.value?.swipeTo(pane.value)
showKeyboard.value=true
startCountdown();*/
}
const goBack = () => {
code.value = ''
@ -136,8 +129,7 @@ const goLogin =async () => {
if (res1.status===0){
window.location.href=res1.data.h5Url
}
console.log('123')
}else {
}else {
await router.push('/');
}
}
@ -145,7 +137,7 @@ const goLogin =async () => {
}
const isKeyboardVisible = ref(false)
const windowHeight = ref(window.innerHeight)
const isFocused = ref(false)
onMounted(() => {
//
windowHeight.value = window.innerHeight
@ -163,7 +155,7 @@ onUnmounted(() => {
</script>
<template>
<div class="h-screen-nav w-[100vw] bg-[url('@/static/images/asdfsdd.png')] bg-cover px-[31px] pt-[86px]">
<div class="w-[100vw] bg-[url('@/static/images/asdfsdd.png')] bg-bottom bg-cover grow-1 px-[31px] pt-[86px]">
<div class="w-full flex justify-center mb-[100px]">
<img class="h-[105px] w-[189px]" src="@/static/images/ghfggff.png" alt="">
</div>
@ -186,7 +178,6 @@ onUnmounted(() => {
</template>
</van-field>
</div>
<div />
</div>
<div class="mt-[55px]">
<van-button :loading="loadingRef.loading1" v-if="phoneNum" :loading-text="$t('login.getCode')" color="#2B53AC" block style="height: 48px" @click="getCode">{{ $t('login.getCode') }}</van-button>
@ -200,7 +191,24 @@ onUnmounted(() => {
<div class="text-[16px] text-[#BDBDBD] mr-[10px]">{{ $t('login.hasSendTo') }}</div>
<div class="text-[16px] text-[#000]">+{{ selectedZone }} {{ phoneNum }}</div>
</div>
<van-password-input :value="code" :gutter="10" :mask="false" focused @focus="showKeyboard = true" />
<div class="relative">
<van-password-input
:value="code"
:gutter="10"
:mask="false"
:focused="isFocused"
/>
<input
v-model="code"
type="tel"
maxlength="6"
ref="codeInput"
class="opacity-0 absolute top-0 left-0 h-full w-full"
@input="code = $event.target.value.replace(/\D/g, '').slice(0, 6)"
@focus="isFocused = true"
@blur="isFocused = false"
/>
</div>
<div class="flex justify-between">
<div :class="`${countdown>0?'text-#BDBDBD':'text-#2B53AC'} text-14px`">
{{ $t('login.reSend') }}<span v-if="countdown>0">({{countdown}})</span>
@ -216,7 +224,6 @@ onUnmounted(() => {
</div>
</van-swipe-item>
</van-swipe>
<van-number-keyboard v-model="code" :show="showKeyboard" @blur="showKeyboard = false" />
<div v-if="!isKeyboardVisible" class="text-center text-14px absolute left-1/2 transform translate-x--1/2 bottom-20px">
{{ $t('login.agreement') }}<span class="text-#3454AF " @click="$router.push('/privacyPolicy')">{{ $t('login.privacyPolicy') }}</span>
</div>

View File

@ -4,6 +4,7 @@ import {createBuyOrder} from "~/api/goods/index.js";
import {goodStore} from "~/stores/goods/index.js";
import { showLoadingToast ,closeToast} from 'vant';
import {authStore} from "~/stores/auth/index.js";
import {message} from "~/components/x-message/useMessage.js";
const {checkoutSessionUrl,payment,payUid}= authStore()
const payStatus=ref(0)
@ -38,14 +39,18 @@ const confirmPay=async ()=>{
testReturnHost:window.location.origin,
testReturnEndPoint:'/payment/result'
})
console.log('res',res);
if (res.status===0){
// if (res.status===0){
// window.location.href=res.data.checkoutSessionUrl
// }
checkoutSessionUrl.value=res.data.checkoutSessionUrl
payUid.value=res.data.payUid
router.push('/payment/checkoutPage')
router.push({
path:'/checkoutPage',
query:{
payUid:res.data.payUid,
returnUrl:'/payment/result',
stripeKey:res.data.checkoutSessionUrl
}
})
}
}
const handleInput = (e) => {

View File

@ -14,21 +14,28 @@ let timer = null
let startTime = Date.now()
const queryOrder = async () => {
// 5
if (Date.now() - startTime > 5000) {
clearInterval(timer)
closeToast()
return
}
showLoadingToast({
message: '加载中...',
forbidClick: true,
});
try {
const res = await orderQuery({
orderNo: route.query.orderNo
})
if (res.status === 0) {
resData.value = res.data
// 15
if (resData.value.status === 1 || Date.now() - startTime > 5000) {
//
if (resData.value.status === 1) {
clearInterval(timer)
closeToast()
}
@ -68,11 +75,12 @@ const goHome = () => {
</script>
<template>
<div class="w-[100vw] h-screen-nav bg-[url('@/static/images/3532@2x.png')] bg-cover grow-1 flex flex-col items-center px-30px">
<div
class="w-[100vw] h-screen-nav bg-[url('@/static/images/3532@2x.png')] bg-cover grow-1 flex flex-col items-center px-30px">
<div class="flex flex-col items-center mt-150px">
<img class="w-119px h-120px mb-36px" src="@/static/images/5554@2x1.png" alt="">
<div class="text-#000 text-16px mb-25px">{{statusLabel[resData.status]}}!</div>
<div class="text-#999 text-16px">{{resData.currency}}{{resData.money}}</div>
<div class="text-#000 text-16px mb-25px">{{ statusLabel[resData.status] }}!</div>
<div class="text-#999 text-16px">{{ resData.currency }}{{ resData.money }}</div>
</div>
<div class="w-full mt-auto mb-40px">
<van-button type="primary" block @click="goHome">

View File

@ -37,8 +37,7 @@ const fetchData = async () => {
showMyList.value = groupByDate(res.data.data)
}
} catch (error) {
console.error('获取数据失败:', error)
}
}
}
const onRefresh = async () => {

View File

@ -33,8 +33,7 @@ const fetchPmblPdf = async () => {
})
pmblUrl.value = res.data?.viewUrl // PDF URLdata
} catch (error) {
console.error('获取拍卖笔录失败:', error)
}
}
}
//

View File

@ -1,5 +1,6 @@
import { setupHttp } from '@/api/http'
import { setupHttp as setupHttp1} from '@/api-collect-code/http'
export default defineNuxtPlugin(() => {
setupHttp()
setupHttp1()
})

View File

@ -1,6 +1,6 @@
import { createGlobalState,useLocalStorage } from '@vueuse/core'
export const codeAuthStore = createGlobalState(() => {
const token=useLocalStorage('token','')
const codeToken=useLocalStorage('codeToken','')
const RefreshToken=useLocalStorage('RefreshToken','')
const userInfo=useLocalStorage('userInfo',{})
const fingerprint=useLocalStorage('fingerprint','')
@ -26,7 +26,11 @@ export const codeAuthStore = createGlobalState(() => {
currency:''
})
const qrData=useLocalStorage('qrData',{})
const codePKey=useLocalStorage('codePKey','')
const codePayUid=useLocalStorage('codePayUid','')
return{
codePKey,
codePayUid,
qrData,
qrUid,
cpayment,
@ -37,7 +41,7 @@ export const codeAuthStore = createGlobalState(() => {
formData,
userInfo,
RefreshToken,
token,
codeToken,
fingerprint
}
})

View File

@ -1,4 +1,7 @@
import { createGlobalState,useLocalStorage } from '@vueuse/core'
import { WebSocketClient } from '@/utils/websocket'
import {message} from "~/components/x-message/useMessage.js";
export const authStore = createGlobalState(() => {
const token=useLocalStorage('token','')
const RefreshToken=useLocalStorage('RefreshToken','')

View File

@ -34,8 +34,7 @@ export const goodStore = createGlobalState(() => {
auctionDetail.value = res.data
}
} catch (err) {
console.error('获取拍卖详情错误:', err)
} finally {
} finally {
loading.value = false
}
}
@ -69,7 +68,6 @@ export const goodStore = createGlobalState(() => {
}
return { finished: true, items: [] }
} catch (err) {
console.error('获取艺术品列表错误:', err)
return { finished: true, items: [] }
} finally {
loading.value = false
@ -85,8 +83,7 @@ export const goodStore = createGlobalState(() => {
artWorkDetail.value = res.data
}
} catch (err) {
console.error('获取艺术品详情错误:', err)
} finally {
} finally {
loading.value = false
}
}

View File

@ -197,12 +197,10 @@ export const liveStore = createGlobalState(() => {
// WebSocket 事件处理
ws.onOpen(() => {
console.log('WebSocket connected')
})
})
ws.onMessage((data) => {
auctionData.value = data.data
console.log(' auctionData.value', auctionData.value)
const { wsType, tip } = data.data || {}
switch (wsType) {
@ -210,13 +208,11 @@ export const liveStore = createGlobalState(() => {
handleTipMessage(tip?.tipType)
break
case WS_TYPES.STOP_ARTWORK:
console.log('changeQuote',quoteStatus.value)
//quoteStatus.value = false
break
case WS_TYPES.OVER:
quoteStatus.value = false
console.log('changeQuote',quoteStatus.value)
message.success(createMessageConfig(
t('live_room.text10'),
'#575757',
@ -230,16 +226,13 @@ export const liveStore = createGlobalState(() => {
break
}
console.log('onmessage', data)
})
})
ws.onClose(() => {
console.log('WebSocket disconnected')
})
})
ws.onError((error) => {
console.error('WebSocket error:', error)
})
})
}
const changeStatus = () => {
if (auctionData.value.artwork?.isSelling&&!auctionData.value.artwork.isSoled){
@ -249,8 +242,7 @@ export const liveStore = createGlobalState(() => {
quoteStatus.value = false
}
}
console.log('changeQuote',quoteStatus.value)
}
}
return{
fullLive,
isMinWindow,

View File

@ -423,7 +423,7 @@
"fullPayment": "Pay in Full",
"partialPayment": "Partial Payment",
"confirmPayment": "Confirm Payment",
"maxAmount": "Maximum {currency}{price}",
"maxAmount": "most",
"enterAmount": "Please enter amount",
"exceedTotal": "Cannot exceed total amount"
},

View File

@ -423,7 +423,7 @@
"fullPayment": "全額支払い",
"partialPayment": "一部支払い",
"confirmPayment": "支払い確認",
"maxAmount": "最大 {currency}{price}",
"maxAmount": "ほとんど",
"enterAmount": "金額を入力してください",
"exceedTotal": "合計金額を超えることはできません"
},

View File

@ -426,7 +426,8 @@
"confirmPayment": "确认支付",
"text1": "最多",
"enterAmount": "请输入金额",
"exceedTotal": "不得高于全部金额"
"exceedTotal": "不得高于全部金额",
"maxAmount": "最多"
},
"signature": {
"resultText": "领取您的专属号牌",

View File

@ -423,7 +423,7 @@
"fullPayment": "支付全部",
"partialPayment": "支付部分",
"confirmPayment": "確認支付",
"maxAmount": "最多{currency}{price}",
"maxAmount": "最多",
"enterAmount": "請輸入金額",
"exceedTotal": "不得高於全部金額"
},

View File

@ -1,6 +1,8 @@
import dotenv from 'dotenv'
import process from 'node:process'
import { currentLocales } from './i18n/i18n'
import fs from 'fs'
import path from 'path'
const envFile = process.env.ENV_FILE || '.env.test'
dotenv.config({ path: `./env/${envFile}` })
const publicConfig = Object.entries(process.env)
@ -10,6 +12,19 @@ const publicConfig = Object.entries(process.env)
return config
}, {})
let httpsOptions = {}
try {
// 读取文件并转换为字符串
const key = fs.readFileSync(path.resolve(__dirname, 'ssl/localhost-key.pem'), 'utf-8')
const cert = fs.readFileSync(path.resolve(__dirname, 'ssl/localhost.pem'), 'utf-8')
httpsOptions = { key, cert }
} catch (error) {
// 失败时使用HTTP
httpsOptions = false
}
export default defineNuxtConfig({
modules: [
'@vant/nuxt',
@ -136,7 +151,8 @@ export default defineNuxtConfig({
// 指定 Nuxt 应用程序的兼容性日期,确保应用程序在未来的 Nuxt 版本中保持稳定性
compatibilityDate: '2025-02-28',
devServer: {
host: '0.0.0.0', // Set the host to 'localhost'
port: 3000, // Set the port to 3000 or any other port you prefer
// https: httpsOptions,
host: '0.0.0.0',
port: 3000,
},
})

View File

@ -0,0 +1,25 @@
export default defineEventHandler(async (event) => {
const url = getRequestURL(event)
// 只处理 create-payment-intent 请求
if (url.pathname === '/create-payment-intent' && event.method === 'POST') {
try {
const body = await readBody(event)
const { items } = body
// 计算总金额
const amount = items.reduce((total: number, item: any) => total + item.amount, 0)
// 模拟创建支付意向的响应
// 注意clientSecret 格式应该类似于 'pi_xxxxx_secret_xxxxx'
return {
clientSecret: `pi_${Math.random().toString(36).substring(2)}_secret_${Math.random().toString(36).substring(2)}`
}
} catch (error) {
throw createError({
statusCode: 500,
message: '创建支付意向失败'
})
}
}
})

28
ssl/localhost-key.pem Normal file
View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDFtE2AWs1o0X/A
0+506WAG49xbO3KJQTWKwHl5pfeP0GieVjV3qKXkHXl9Fyhg9oJnCAz2nzxzavAP
W9NevDbX7KdEiZQjDhpgO+V9IGUqqJKEQnpVk57G3fHBX40nQ4zAQ/S1Vj4Aw9Tk
iqNFgLRX3HTAT0NO3eXENJbsDdGxtOuLn5h+DWThrqCaUK2Xnkee3eG/VwgKJKVH
LWuC8xyb1NydtchaRFBjoO9oT6Tb/MGeBOrNH9e7Ndh9mCFepXUhzNxrN1fKjn9s
107pSEeZb4il2K7GqE0W6r6Y3eeBR2+mEBRqeFnZObT0JkBLI2QIrAwOnFO6ObQC
ZaEjwHcZAgMBAAECggEASMZB8Ql7qyXS3OwmTqrJSj/+ESck1hlG2DhZfsn1At84
Y3BgZheSWRHwcndfybFz9vEjtHSRD/tBOqYWfDzUA099kuEBwpWiZ+Ika5bNJpK+
vCisV2vrelCgeQnvL5DR8sQRA98nG6j6aNYPm7nwqJbh8xg6MoHD3iFtnJ7JnZvQ
pSa6Z9qq4Po+cp63/U3yEzFeiVVDTMQJMVClANUCX3jLHs8B85WMbb1eKKFe/xCA
n2BWlFVI7Hld+hxhKWkc71+kafC5hUz1w88FcBaN2W/DAtJgKC0dHYATwLCUGC4Z
CoCZfB7b3JOzK4mGJ/XxxUcBUk+oweExOrYwCfW4MQKBgQDJbTM4qgW2ca/Xc7cp
zKvclgtkJ8rWbVZqMYW6fpXoOdhhxjJSx+LfeGk1whz3Xj04EdSZbRI6zYVaHfHa
HFkA2Na/Wi9hboid4WVXUi5RXzthVTOYi1jAJNmK4R25wuSdcGVoYxSrSLyDcHbx
MF2cFdQ4A386L+RcoDYzImWCXwKBgQD7RO4DZJgkqDng6YFrYqIdQkJn6keXl4yW
Hq1FGaa7XDjlun/X2jT0xJJPFcwLQLbWwrkwmUYN/VQEbYwYlyB0MegF9VflfILl
/leCXC8/9WEknQkPqu8N1JiYhajIKLxfQX35nW/oK+S5prJOBxNw+3Of/S43R5Xe
60EEI9iphwKBgQCrs/Sn5vd7sKnOpYuLjDcskJMhS3JzGz1AxPpUIbgz/6tenY8k
VdQl3wUAmHoMvD6/XyO1re6ORcfZLBGQdf3A5RcagwxEp+65dvvmVd256844iGK1
NIPxNvhilMe8JFCxjLBFLcDeyeA4w1QBAdOqTEldfk2kElM+SiwppraVTQKBgCUP
O5OgiJgPf8neZsox1/s8xJKTCVAgeAnEKIYijGbh6Tpo0WZCtsDLJVEow9l9B/qQ
6cNzN9PkYznr9lfCInVAzxnh377nKF9Hrhx6ADYMuPEvgCChc3S0wHTuccBj0bSy
8iOYxuKVZrzDC1Va0dE+JQWZz/EzS7V/OS2lI9WNAoGBAKujoPDn36/hJ/Zr8XAM
CEbOi0q0N7I37aRKO8Wm55SCGDYWtBlu+NiIMqk3gzgomtm/cVF+fUNv0BOKc+hx
x6PQE98AEn5LdGeqLpDY66vhyR8WGUyCBPB+Dn8OFFT+njL2E8NcQi0kS3t/YlR/
oobyxGhm4M1fM8HtGwqQJX60
-----END PRIVATE KEY-----

26
ssl/localhost.pem Normal file
View File

@ -0,0 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIEbjCCAtagAwIBAgIRAJjJaJNy+AO5JPuasTslJMQwDQYJKoZIhvcNAQELBQAw
gZ8xHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTE6MDgGA1UECwwxREVT
S1RPUC1DOTVCMVIzXDM3MzYzQERFU0tUT1AtQzk1QjFSMyAo6YKi6Zuo5p2oKTFB
MD8GA1UEAww4bWtjZXJ0IERFU0tUT1AtQzk1QjFSM1wzNzM2M0BERVNLVE9QLUM5
NUIxUjMgKOmCoumbqOadqCkwHhcNMjUwMzAyMDIzMzM3WhcNMjcwNjAyMDIzMzM3
WjBlMScwJQYDVQQKEx5ta2NlcnQgZGV2ZWxvcG1lbnQgY2VydGlmaWNhdGUxOjA4
BgNVBAsMMURFU0tUT1AtQzk1QjFSM1wzNzM2M0BERVNLVE9QLUM5NUIxUjMgKOmC
oumbqOadqCkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDFtE2AWs1o
0X/A0+506WAG49xbO3KJQTWKwHl5pfeP0GieVjV3qKXkHXl9Fyhg9oJnCAz2nzxz
avAPW9NevDbX7KdEiZQjDhpgO+V9IGUqqJKEQnpVk57G3fHBX40nQ4zAQ/S1Vj4A
w9TkiqNFgLRX3HTAT0NO3eXENJbsDdGxtOuLn5h+DWThrqCaUK2Xnkee3eG/VwgK
JKVHLWuC8xyb1NydtchaRFBjoO9oT6Tb/MGeBOrNH9e7Ndh9mCFepXUhzNxrN1fK
jn9s107pSEeZb4il2K7GqE0W6r6Y3eeBR2+mEBRqeFnZObT0JkBLI2QIrAwOnFO6
ObQCZaEjwHcZAgMBAAGjXjBcMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggr
BgEFBQcDATAfBgNVHSMEGDAWgBQVJpbTujNcXUH/91CnxLerp/gKbDAUBgNVHREE
DTALgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggGBAERAae9YDQgjnVtDQUWL
kIbMowvN6BospgO2srV+aXCDLbB22jnq4cGsTpVjxo80Nl6M0iSRz29K+jy4YFsL
efTOeks1EpVQB/UnYuo391p5wzevXwa3s7dH7Oc+917y8JDiLNnSVEct+tk4zeOZ
QbVzx6Gexiii7k1uSG/G1NYrRiXf3ggM93Fyu5NM+u8CzZvWm46ix9reYimVqfPa
VjHsiQnmKbh+CD6iDWm9y1jUxqBay4cAbo2AVxIvBDdsC9KSCTsbP4hBPx9foy1U
cLRxUGsWTVPPS2BmP8o6CSa2tNPeVNCWSP89tanY2mzGErfVXLV8t5E4awF0ea+a
kbjyG3svVC6/rLo8LpFPonr4mQWfGcFntmGUC314d5z1ZCCS5ENEWAGZ3b3XzPsU
Yh2QQnt4gtvWaTRqwqhSL9DLFp106/tok3hq8MyDFcxTxWKyDZsgaieoRGnF11EW
tdIqnK9nwVOyAzaO603SuEoMiGBpb9nj/cAFsvm56YUVrg==
-----END CERTIFICATE-----