From c8d12c37b3f78e41aafef1ccb3e380573308c34a Mon Sep 17 00:00:00 2001
From: shinnosuke <tantao@1hoper.com>
Date: 星期二, 04 八月 2026 17:41:46 +0800
Subject: [PATCH] feat(admin): 添加商户登录密码修改功能并完善优惠券金额验证
---
talent-card-server/src/main/java/com/talentcard/server/business/merchant/MerchantSettlementService.java | 2
rencaika_merchant/pages/withdrawal-detail/withdrawal-detail.uvue | 8 +
rencaika_merchant/manifest.json | 2
rencaika_merchant/pages/verification-detail/verification-detail.uvue | 8 -
talent-card-server/src/main/java/com/talentcard/server/business/admin/AdminBusinessService.java | 11 ++
rencaika_merchant/config/api.uts | 9 +
rencaika_merchant/pages/orders/orders.uvue | 21 ---
talent-card-server/src/main/java/com/talentcard/server/business/admin/CouponUpdateRequest.java | 3
talent-card-server/src/main/java/com/talentcard/server/business/admin/MerchantUpdateRequest.java | 3
talent-card-admin/src/views/MerchantCreateView.vue | 37 +++++-
talent-card-server/src/main/resources/application.yml | 31 ++---
rencaika_merchant/pages/withdrawal/withdrawal.uvue | 5
rencaika_merchant/pages/order-detail/order-detail.uvue | 72 +++----------
rencaika_merchant/services/settlement.uts | 4
talent-card-server/src/main/java/com/talentcard/server/business/admin/CouponCreateRequest.java | 3
talent-card-server/src/main/java/com/talentcard/server/business/merchant/HuifuMerchantWithdrawalService.java | 15 ++
talent-card-admin/src/style.css | 30 ++++++
rencaika_merchant/pages/merchant/merchant.uvue | 17 ---
talent-card-admin/src/views/MerchantSettlementView.vue | 6
19 files changed, 149 insertions(+), 138 deletions(-)
diff --git a/rencaika_merchant/config/api.uts b/rencaika_merchant/config/api.uts
index 8b9a674..7c423f4 100644
--- a/rencaika_merchant/config/api.uts
+++ b/rencaika_merchant/config/api.uts
@@ -9,13 +9,14 @@
// Toggle homepage mock metrics independently from the login bypass.
// When disabled, the dashboard requests the authenticated merchant summary API.
-export const useDashboardDemoDataInDevelopment = true
+export const useDashboardDemoDataInDevelopment = false
-// 商户中心、商品与营业时间的演示数据开关,与登录跳过和首页经营数据开关相互独立。
-export const useMerchantManagementDemoDataInDevelopment = true
+// Toggle merchant-center, product, and business-hour demo data independently from login bypass and dashboard demo data.
+export const useMerchantManagementDemoDataInDevelopment = false
// 后台提供微信小程序合法 HTTPS 域名后,在对应环境填写完整基础地址。
-const developmentBaseUrl = 'http://192.168.110.13:8080'
+//const developmentBaseUrl = 'http://192.168.110.13:8080'
+const developmentBaseUrl = 'http://221.182.45.100:11451'
const stagingBaseUrl = ''
const productionBaseUrl = ''
diff --git a/rencaika_merchant/manifest.json b/rencaika_merchant/manifest.json
index b8b7add..aad0f3f 100644
--- a/rencaika_merchant/manifest.json
+++ b/rencaika_merchant/manifest.json
@@ -9,7 +9,7 @@
"quickapp": {},
/* 小程序特有相关 */
"mp-weixin": {
- "appid": "wxfb87c6ef164c2922",
+ "appid": "wxe91f1af7638aa5dd",
"setting": {
"urlCheck": false
},
diff --git a/rencaika_merchant/pages/merchant/merchant.uvue b/rencaika_merchant/pages/merchant/merchant.uvue
index 35c0496..5fdb03f 100644
--- a/rencaika_merchant/pages/merchant/merchant.uvue
+++ b/rencaika_merchant/pages/merchant/merchant.uvue
@@ -37,12 +37,10 @@
</template>
<script setup lang="uts">
- import { apiEnvironment, useMerchantManagementDemoDataInDevelopment } from '../../config/api.uts'
import { hasActiveSession } from '../../services/session.uts'
- import { MerchantProfile, loadDemoMerchantProfile, loadMerchantProfile, updateDemoMerchantBusinessStatus, updateMerchantBusinessStatus } from '../../services/merchant-management.uts'
+ import { MerchantProfile, loadMerchantProfile, updateMerchantBusinessStatus } from '../../services/merchant-management.uts'
- const useDemoData = apiEnvironment == 'development' && useMerchantManagementDemoDataInDevelopment
- const merchant = ref<MerchantProfile>(loadDemoMerchantProfile())
+ const merchant = ref<MerchantProfile>({ merchantId: '', storeId: '', merchantName: '', storeName: '', address: '', coverUrl: '', businessStatus: '', onlineProductCount: 0 })
const loadState = ref('loading')
const traceId = ref('')
const businessUpdating = ref(false)
@@ -56,11 +54,6 @@
}
loadState.value = 'loading'
traceId.value = ''
- if (useDemoData) {
- merchant.value = loadDemoMerchantProfile()
- loadState.value = 'success'
- return
- }
if (!hasActiveSession()) {
loadState.value = 'error'
return
@@ -81,12 +74,6 @@
if (businessUpdating.value || loadState.value != 'success') return
const nextStatus = isBusinessOpen.value ? 'closed' : 'open'
businessUpdating.value = true
- if (useDemoData) {
- merchant.value = updateDemoMerchantBusinessStatus(nextStatus)
- businessUpdating.value = false
- uni.showToast({ title: nextStatus == 'open' ? '已切换为营业中' : '已切换为休息中', icon: 'none' })
- return
- }
updateMerchantBusinessStatus(nextStatus, (result: MerchantProfile, _traceId: string) => {
merchant.value.businessStatus = result.businessStatus
businessUpdating.value = false
diff --git a/rencaika_merchant/pages/order-detail/order-detail.uvue b/rencaika_merchant/pages/order-detail/order-detail.uvue
index d69d7f6..8b1b86c 100644
--- a/rencaika_merchant/pages/order-detail/order-detail.uvue
+++ b/rencaika_merchant/pages/order-detail/order-detail.uvue
@@ -37,8 +37,6 @@
<script setup lang="uts">
import { revokeOrderVerification } from '../../services/orders.uts'
import { loadMerchantOrderDetail, MerchantOrderDetail } from '../../services/orders.uts'
- import { apiEnvironment } from '../../config/api.uts'
- import { getLocalOrderDetail } from '../../config/order-demo-data.uts'
import { formatErrorMessage } from '../../services/diagnostics.uts'
const orderId = ref('')
@@ -47,20 +45,19 @@
const canRevokeVerification = ref(false)
const revokeExpiredMessage = ref('已过可撤销时间')
const revokeSubmitting = ref(false)
- const statusTitle = ref('待支付')
- const statusDesc = ref('订单已生成,等待用户完成支付后即可继续核销。')
+ const statusTitle = ref('订单加载中')
+ const statusDesc = ref('正在获取订单详情。')
const statusIcon = ref('/static/icons/lucide/clock-3.svg')
- const orderNo = ref('155464871234')
- const productAmount = ref('¥256.00')
- const talentDiscount = ref('-¥128.00')
- const productDiscount = ref('-¥0.00')
- const paidAmount = ref('¥128.00')
- const subsidyAmount = ref('¥128.00')
- const createdAt = ref('2026-07-27 10:26:15')
- const verificationAt = ref('待核销')
- const customerName = ref('刘先生')
- const customerPhone = ref('183***4568')
- const useDemoData = (): boolean => apiEnvironment == 'development' && uni.getStorageSync('merchant-orders-demo-data') != false
+ const orderNo = ref('')
+ const productAmount = ref('')
+ const talentDiscount = ref('')
+ const productDiscount = ref('')
+ const paidAmount = ref('')
+ const subsidyAmount = ref('')
+ const createdAt = ref('')
+ const verificationAt = ref('')
+ const customerName = ref('')
+ const customerPhone = ref('')
const formatCents = (value: number): string => '¥' + (value / 100).toFixed(2)
const applyRemoteOrder = (detail: MerchantOrderDetail) => {
@@ -86,33 +83,15 @@
onLoad((options: any) => {
orderId.value = options.id != null ? options.id as string : ''
orderNo.value = options.orderNo != null ? options.orderNo as string : orderNo.value
- if (!useDemoData() && orderNo.value.length > 0) {
- loadMerchantOrderDetail(orderNo.value, (detail: MerchantOrderDetail, _traceId: string) => applyRemoteOrder(detail), (error: any) => {
- uni.showToast({ title: error.message != null ? error.message : '订单详情加载失败', icon: 'none' })
- })
+ if (orderNo.value.length == 0) {
+ uni.showToast({ title: '订单参数缺失', icon: 'none' })
return
}
- const detail = getLocalOrderDetail(orderId.value)
- isStoreCompleted.value = detail.category == 'store' && detail.status == 'completed'
- isCompleted.value = detail.status == 'completed'
- orderNo.value = detail.orderNo
- productAmount.value = detail.productAmount
- talentDiscount.value = detail.talentDiscount
- productDiscount.value = detail.productDiscount
- paidAmount.value = detail.paidAmount
- subsidyAmount.value = detail.subsidyAmount
- createdAt.value = detail.createdAt
- verificationAt.value = detail.verificationAt
- customerName.value = detail.customerName
- customerPhone.value = detail.customerPhone
- canRevokeVerification.value = detail.canRevoke
- if (detail.revokeReason.length > 0) revokeExpiredMessage.value = detail.revokeReason
-
- if (isCompleted.value) {
- statusTitle.value = '已完成'
- statusDesc.value = '订单已完成核销,相关权益已结算。'
- statusIcon.value = '/static/icons/lucide/badge-check.svg'
- }
+ loadMerchantOrderDetail(orderNo.value, (detail: MerchantOrderDetail, _traceId: string) => applyRemoteOrder(detail), (error: any) => {
+ statusTitle.value = '订单加载失败'
+ statusDesc.value = error.message != null ? error.message : '请返回订单列表后重试。'
+ uni.showToast({ title: error.message != null ? error.message : '订单详情加载失败', icon: 'none' })
+ })
})
const goBack = () => {
@@ -132,19 +111,6 @@
const submitRevokeVerification = () => {
if (revokeSubmitting.value) return
revokeSubmitting.value = true
- if (useDemoData()) {
- setTimeout(() => {
- revokeSubmitting.value = false
- isCompleted.value = false
- canRevokeVerification.value = false
- statusTitle.value = '待核销'
- statusDesc.value = '核销已撤销,订单已退回待核销状态。'
- statusIcon.value = '/static/icons/lucide/clock-3.svg'
- verificationAt.value = '待核销'
- uni.showToast({ title: '撤销成功', icon: 'success' })
- }, 300)
- return
- }
const idempotencyKey = 'revoke-verification-' + orderId.value + '-' + Date.now()
revokeOrderVerification(orderId.value, idempotencyKey, (result: any, traceId: string) => {
revokeSubmitting.value = false
diff --git a/rencaika_merchant/pages/orders/orders.uvue b/rencaika_merchant/pages/orders/orders.uvue
index c9c7912..db08743 100644
--- a/rencaika_merchant/pages/orders/orders.uvue
+++ b/rencaika_merchant/pages/orders/orders.uvue
@@ -2,7 +2,7 @@
<view class="page">
<view class="header">
<view class="status-safe-space"></view>
- <view class="title-row"><text class="page-title">订单管理</text><view v-if="isDevelopment" class="demo-control"><text class="demo-control-text">演示数据</text><switch class="demo-switch" :checked="demoDataEnabled" color="#FF7924" @change="toggleDemoData"></switch></view></view>
+ <view class="title-row"><text class="page-title">订单管理</text></view>
<view class="category-tabs">
<view class="category-tab" :class="activeCategory == 'group' ? 'active' : ''" @tap="selectCategory('group')"><text>团购</text></view>
<view class="category-tab" :class="activeCategory == 'store' ? 'active' : ''" @tap="selectCategory('store')"><text>到店消费</text></view>
@@ -146,8 +146,7 @@
<script setup lang="uts">
import { getOrderStatusText, getPendingStatusByCategory } from '../../config/order.uts'
- import { loadDemoMerchantOrders, loadMerchantBusinessDate, loadMerchantOrders, MerchantOrder, MerchantOrderPage, MerchantOrderQuery } from '../../services/orders.uts'
- import { apiEnvironment } from '../../config/api.uts'
+ import { loadMerchantBusinessDate, loadMerchantOrders, MerchantOrder, MerchantOrderPage, MerchantOrderQuery } from '../../services/orders.uts'
import { hasActiveSession } from '../../services/session.uts'
const activeCategory = ref('group')
@@ -164,8 +163,6 @@
const currentPage = ref(1)
const loadMoreStep = ref(0)
const orders = ref<Array<MerchantOrder>>([])
- const isDevelopment = apiEnvironment == 'development'
- const demoDataEnabled = ref(isDevelopment && uni.getStorageSync('merchant-orders-demo-data') != false)
const dateFilterApplied = ref(false)
const pageSize = 3
let cancelOrderRequest: (() => void) | null = null
@@ -262,12 +259,6 @@
loadMoreState.value = 'loading'
}
const query = buildOrderQuery(page)
- if (demoDataEnabled.value) {
- setTimeout(() => {
- applyOrderPage(loadDemoMerchantOrders(query), append)
- }, 180)
- return
- }
if (!hasActiveSession()) {
showOrderError('请登录后加载真实订单', '', append)
return
@@ -282,12 +273,6 @@
}
const reloadOrders = () => requestOrders(1, false)
-
- const toggleDemoData = (event: any) => {
- demoDataEnabled.value = event.detail != null && event.detail.value == true
- uni.setStorageSync('merchant-orders-demo-data', demoDataEnabled.value)
- reloadOrders()
- }
const formatAmount = (amount: number): string => (amount / 100).toFixed(2)
const formatDateTime = (value: string): string => value.length > 0 ? value.replace('T', ' ') : '—'
@@ -503,7 +488,7 @@
}
onLoad(() => {
- if (!demoDataEnabled.value && hasActiveSession()) loadMerchantBusinessDate((value: string) => applyBusinessDate(value), () => {})
+ if (hasActiveSession()) loadMerchantBusinessDate((value: string) => applyBusinessDate(value), () => {})
reloadOrders()
})
diff --git a/rencaika_merchant/pages/verification-detail/verification-detail.uvue b/rencaika_merchant/pages/verification-detail/verification-detail.uvue
index bee5b2e..ea08b6a 100644
--- a/rencaika_merchant/pages/verification-detail/verification-detail.uvue
+++ b/rencaika_merchant/pages/verification-detail/verification-detail.uvue
@@ -26,8 +26,6 @@
</template>
<script setup lang="uts">
- import { apiEnvironment } from '../../config/api.uts'
- import { getLocalOrderDetail } from '../../config/order-demo-data.uts'
import { confirmMerchantOrderVerification, loadMerchantOrderDetail, MerchantOrderDetail } from '../../services/orders.uts'
const orderId = ref('')
@@ -46,7 +44,6 @@
const submitting = ref(false)
const loadState = ref('loading')
const loadError = ref('订单详情加载失败')
- const useDemoData = (): boolean => apiEnvironment == 'development' && uni.getStorageSync('merchant-orders-demo-data') != false
const formatAmount = (amount: number): string => (amount / 100).toFixed(2)
const formatDateTime = (value: string): string => value.length > 0 ? value.replace('T', ' ') : '—'
@@ -70,11 +67,6 @@
const loadOrder = () => {
loadState.value = 'loading'
- if (useDemoData()) {
- const detail = getLocalOrderDetail(orderId.value)
- applyOrder({ id: orderId.value, orderNo: detail.orderNo, category: detail.category, status: detail.status, statusText: detail.status == 'completed' ? '已完成' : '待核销', title: '特色酸菜鱼', customerName: detail.customerName, customerMobile: detail.customerPhone, cardLevel: '卓越人才卡', orderAmount: Number(detail.productAmount.replace(/[^0-9.]/g, '')) * 100, payAmount: Number(detail.paidAmount.replace(/[^0-9.]/g, '')) * 100, subsidyAmount: Number(detail.subsidyAmount.replace(/[^0-9.]/g, '')) * 100, createdAt: detail.createdAt, verifyTime: detail.status == 'completed' ? detail.verificationAt : '', benefitName: '人才专属权益', merchantName: '', canRevoke: false, revokeDeadline: '', revokeReason: '' })
- return
- }
if (orderNo.value.length == 0) { loadError.value = '订单参数缺失'; loadState.value = 'error'; return }
loadMerchantOrderDetail(orderNo.value, (order: MerchantOrderDetail, _traceId: string) => applyOrder(order), (error: any) => { loadError.value = error.message != null ? error.message : '订单详情加载失败'; loadState.value = 'error' })
}
diff --git a/rencaika_merchant/pages/withdrawal-detail/withdrawal-detail.uvue b/rencaika_merchant/pages/withdrawal-detail/withdrawal-detail.uvue
index 879b320..837db3d 100644
--- a/rencaika_merchant/pages/withdrawal-detail/withdrawal-detail.uvue
+++ b/rencaika_merchant/pages/withdrawal-detail/withdrawal-detail.uvue
@@ -23,11 +23,12 @@
const selectedId = ref('')
const error = ref('')
const submitting = ref(false)
+ const requestKey = ref('')
const availableText = computed(() => (available.value / 100).toFixed(2))
const selected = computed(() => cards.value.find((item) => item.id == selectedId.value) || null)
const goBack = () => uni.navigateBack()
- const clearError = () => { error.value = '' }
- const chooseBank = () => uni.navigateTo({ url: '/pages/bank-card-select/bank-card-select?selected=' + encodeURIComponent(selectedId.value) })
+ const clearError = () => { error.value = ''; requestKey.value = '' }
+ const chooseBank = () => { requestKey.value = ''; uni.navigateTo({ url: '/pages/bank-card-select/bank-card-select?selected=' + encodeURIComponent(selectedId.value) }) }
const load = () => {
loadWithdrawalSummary((data: any) => { available.value = Number(data.availableAmountCents || 0) }, () => { error.value = '可提金额加载失败,请稍后重试' })
loadBankCards((items: Array<SettlementBankCard>) => { cards.value = items }, () => { error.value = '银行卡加载失败,请稍后重试' })
@@ -45,7 +46,8 @@
uni.showModal({ title: '确认提现', content: '确认提现 ¥' + (cents / 100).toFixed(2) + ' 至' + selectedCard.bankName + '吗?', confirmText: '确认提现', confirmColor: '#FF6B2C', success: (result: any) => {
if (!result.confirm) return
submitting.value = true
- createWithdrawal(selectedCard.id, cents, 'withdrawal-' + Date.now(), () => { submitting.value = false; uni.redirectTo({ url: '/pages/withdrawal-success/withdrawal-success' }) }, (requestError: any) => { submitting.value = false; error.value = requestError.message || '提现提交失败,请重试' })
+ if (requestKey.value.length == 0) requestKey.value = 'withdrawal-' + selectedCard.id + '-' + cents + '-' + Date.now()
+ createWithdrawal(selectedCard.id, cents, requestKey.value, (created: any) => { submitting.value = false; requestKey.value = ''; uni.redirectTo({ url: '/pages/withdrawal-success/withdrawal-success?withdrawalNo=' + encodeURIComponent(created.withdrawalNo) }) }, (requestError: any) => { submitting.value = false; error.value = requestError.message || '提现提交失败,请重试' })
} })
}
onShow(() => load())
diff --git a/rencaika_merchant/pages/withdrawal/withdrawal.uvue b/rencaika_merchant/pages/withdrawal/withdrawal.uvue
index ba93a8c..2e5636a 100644
--- a/rencaika_merchant/pages/withdrawal/withdrawal.uvue
+++ b/rencaika_merchant/pages/withdrawal/withdrawal.uvue
@@ -1,11 +1,12 @@
<template>
- <view class="page"><view class="nav"><view @tap="goBack">返回</view><text>商户提现</text><view></view></view><view class="balance"><text>当前可提</text><text class="amount">¥{{amount}}</text><view class="button" @tap="startWithdrawal">提现</view></view><view class="heading"><text>提现记录</text><text>共 {{records.length}} 条</text></view><scroll-view class="scroll" scroll-y="true"><view v-if="loading" class="state">正在加载</view><view v-else-if="records.length == 0" class="state">暂无提现记录</view><view v-for="item in records" :key="item.id" class="record"><view><text>提现</text><text class="money">¥{{format(item.amountCents)}}</text></view><view class="record-bottom"><text>{{formatTime(item.createdAt)}} {{item.cardMasked}}</text><text :class="'status ' + item.status">{{statusText(item.status)}}</text></view><text v-if="item.reason.length > 0" class="reason">{{item.reason}}</text></view></scroll-view></view>
+ <view class="page"><view class="nav"><view @tap="goBack">返回</view><text>商户提现</text><view></view></view><view class="balance"><text>当前可提</text><text class="amount">¥{{amount}}</text><view class="button" @tap="startWithdrawal">提现</view></view><view class="heading"><text>提现记录</text><text>共 {{records.length}} 条</text></view><scroll-view class="scroll" scroll-y="true"><view v-if="loading" class="state">正在加载</view><view v-else-if="records.length == 0" class="state">暂无提现记录</view><view v-for="item in records" :key="item.id" class="record"><view><text>提现</text><text class="money">¥{{format(item.amountCents)}}</text></view><view class="record-bottom"><text>{{displayTime(item)}} {{item.cardMasked}}</text><text :class="'status ' + item.status">{{statusText(item.status)}}</text></view><text v-if="item.reason.length > 0" class="reason">{{item.reason}}</text></view></scroll-view></view>
</template>
<script setup lang="uts">
import { loadOnboardingStatus, loadWithdrawalSummary, loadWithdrawals, SettlementWithdrawal } from '../../services/settlement.uts'
const loading = ref(true); const available = ref(0); const onboardingStatus = ref('unsubmitted'); const records = ref<Array<SettlementWithdrawal>>([])
const amount = computed(() => (available.value / 100).toFixed(2)); const format = (cents: number): string => (cents / 100).toFixed(2); const formatTime = (value: string): string => value.replace('T', ' ')
-const statusText = (value: string): string => value == 'paid' ? '已到账' : value == 'rejected' ? '已驳回' : value == 'failed' ? '提现失败' : '审核中'
+const statusText = (value: string): string => value == 'paid' ? '已到账' : value == 'processing' ? '到账处理中' : value == 'rejected' ? '已驳回' : value == 'failed' ? '提现失败' : '待审核'
+const displayTime = (item: SettlementWithdrawal): string => item.status == 'paid' && item.paidAt.length > 0 ? '到账 ' + formatTime(item.paidAt) : formatTime(item.createdAt)
const goBack = () => uni.navigateBack()
const startWithdrawal = () => { if (onboardingStatus.value == 'approved') { uni.navigateTo({ url: '/pages/withdrawal-detail/withdrawal-detail' }); return }; if (onboardingStatus.value == 'pending') { uni.navigateTo({ url: '/pages/merchant-onboarding-pending/merchant-onboarding-pending' }); return }; uni.navigateTo({ url: '/pages/merchant-onboarding/merchant-onboarding' }) }
const load = () => { loading.value = true; loadOnboardingStatus((item) => { onboardingStatus.value = item.status }, () => {}); loadWithdrawalSummary((data: any) => { available.value = Number(data.availableAmountCents || 0) }, () => {}); loadWithdrawals(1, 50, (data) => { records.value = data.records; loading.value = false }, (error: any) => { loading.value = false; uni.showToast({ title: error.message || '提现记录加载失败', icon: 'none' }) }) }
diff --git a/rencaika_merchant/services/settlement.uts b/rencaika_merchant/services/settlement.uts
index 204ff6e..f19b626 100644
--- a/rencaika_merchant/services/settlement.uts
+++ b/rencaika_merchant/services/settlement.uts
@@ -3,14 +3,14 @@
export type SettlementOnboarding = { id: string, applicationNo: string, status: string, reason: string, submittedAt: string }
export type SettlementBankCard = { id: string, bankName: string, branchName: string, cardMasked: string, status: string, reason: string, createdAt: string }
-export type SettlementWithdrawal = { id: string, withdrawalNo: string, amountCents: number, status: string, reason: string, bankName: string, cardMasked: string, createdAt: string }
+export type SettlementWithdrawal = { id: string, withdrawalNo: string, amountCents: number, status: string, reason: string, bankName: string, cardMasked: string, createdAt: string, reviewedAt: string, paidAt: string }
export type SettlementPage<T> = { records: Array<T>, page: number, size: number, total: number }
const stringValue = (value: any): string => value == null ? '' : '' + value
const numberValue = (value: any): number => value == null ? 0 : Number(value)
const onboarding = (value: any): SettlementOnboarding => ({ id: stringValue(value != null ? value.id : ''), applicationNo: stringValue(value != null ? value.applicationNo : ''), status: stringValue(value != null ? value.status : 'unsubmitted'), reason: stringValue(value != null ? value.reason : ''), submittedAt: stringValue(value != null ? value.submittedAt : '') })
const bankCard = (value: any): SettlementBankCard => ({ id: stringValue(value != null ? value.id : ''), bankName: stringValue(value != null ? value.bankName : ''), branchName: stringValue(value != null ? value.branchName : ''), cardMasked: stringValue(value != null ? value.cardMasked : ''), status: stringValue(value != null ? value.status : 'pending'), reason: stringValue(value != null ? value.reason : ''), createdAt: stringValue(value != null ? value.createdAt : '') })
-const withdrawal = (value: any): SettlementWithdrawal => ({ id: stringValue(value != null ? value.id : ''), withdrawalNo: stringValue(value != null ? value.withdrawalNo : ''), amountCents: numberValue(value != null ? value.amountCents : 0), status: stringValue(value != null ? value.status : 'pending'), reason: stringValue(value != null ? value.reason : ''), bankName: stringValue(value != null ? value.bankName : ''), cardMasked: stringValue(value != null ? value.cardMasked : ''), createdAt: stringValue(value != null ? value.createdAt : '') })
+const withdrawal = (value: any): SettlementWithdrawal => ({ id: stringValue(value != null ? value.id : ''), withdrawalNo: stringValue(value != null ? value.withdrawalNo : ''), amountCents: numberValue(value != null ? value.amountCents : 0), status: stringValue(value != null ? value.status : 'pending'), reason: stringValue(value != null ? value.reason : ''), bankName: stringValue(value != null ? value.bankName : ''), cardMasked: stringValue(value != null ? value.cardMasked : ''), createdAt: stringValue(value != null ? value.createdAt : ''), reviewedAt: stringValue(value != null ? value.reviewedAt : ''), paidAt: stringValue(value != null ? value.paidAt : '') })
export const loadOnboardingStatus = (success: (value: SettlementOnboarding) => void, error: (value: HttpError) => void): (() => void) => request<any>({ path: getApiEndpoint('merchantOnboardingStatus') }, (data: any) => success(onboarding(data)), error)
export const submitOnboarding = (payload: any, key: string, success: (value: SettlementOnboarding) => void, error: (value: HttpError) => void): (() => void) => request<any>({ path: getApiEndpoint('merchantOnboarding'), method: 'POST', data: payload, idempotencyKey: key }, (data: any) => success(onboarding(data)), error)
diff --git a/talent-card-admin/src/style.css b/talent-card-admin/src/style.css
index a19bb8b..dc33b48 100644
--- a/talent-card-admin/src/style.css
+++ b/talent-card-admin/src/style.css
@@ -1185,6 +1185,20 @@
.merchant-coupon-edit-form .el-select__wrapper,
.merchant-coupon-edit-form .el-range-editor.el-input__wrapper {
background: #ffffff;
+ box-shadow: 0 0 0 1px #d9dee8 inset;
+}
+
+.merchant-coupons-filter .el-input__wrapper:hover,
+.merchant-coupons-filter .el-input__wrapper.is-focus,
+.merchant-coupons-filter .el-select__wrapper:hover,
+.merchant-coupons-filter .el-select__wrapper.is-focused,
+.merchant-coupon-edit-form .el-input__wrapper:hover,
+.merchant-coupon-edit-form .el-input__wrapper.is-focus,
+.merchant-coupon-edit-form .el-select__wrapper:hover,
+.merchant-coupon-edit-form .el-select__wrapper.is-focused,
+.merchant-coupon-edit-form .el-date-editor.el-input__wrapper:hover,
+.merchant-coupon-edit-form .el-date-editor.el-input__wrapper.is-focus {
+ box-shadow: 0 0 0 1px #b8c2d1 inset;
}
.merchant-coupons-filter .coupon-created-time-picker {
@@ -1561,6 +1575,22 @@
width: 200px;
}
+.coupon-amount-control {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.coupon-amount-control .el-input-number {
+ width: 200px;
+}
+
+.coupon-amount-control > span {
+ color: #334155;
+ line-height: 28px;
+ white-space: nowrap;
+}
+
.merchant-coupon-edit-form .coupon-valid-time-picker {
width: 300px;
}
diff --git a/talent-card-admin/src/views/MerchantCreateView.vue b/talent-card-admin/src/views/MerchantCreateView.vue
index d8e2764..53d438a 100644
--- a/talent-card-admin/src/views/MerchantCreateView.vue
+++ b/talent-card-admin/src/views/MerchantCreateView.vue
@@ -959,6 +959,11 @@
ElMessage.warning('请输入优惠券名称')
return
}
+ const couponAmount = Number(couponEditForm.couponAmount)
+ if (couponEditForm.couponAmount === '' || !Number.isFinite(couponAmount) || couponAmount <= 0) {
+ ElMessage.warning('请输入大于0的优惠券金额')
+ return
+ }
if (couponEditForm.useRequirement === 'full' && couponEditForm.fullAmount === '') {
ElMessage.warning('请输入使用门槛')
return
@@ -1016,7 +1021,18 @@
ElMessage.warning('请输入排序')
return
}
- if (requiresMerchantOperator.value) {
+ const updatingMerchantPassword = isEditMode.value && accountForm.loginPassword.length > 0
+ if (updatingMerchantPassword) {
+ if (accountForm.loginPassword.length < 6 || accountForm.loginPassword.length > 64) {
+ ElMessage.warning('登录密码长度须为6至64位')
+ return
+ }
+ if (!accountForm.confirmPassword) {
+ ElMessage.warning('请输入确认密码')
+ return
+ }
+ }
+ if (requiresMerchantOperator.value) {
if (!accountForm.loginAccount.trim()) {
ElMessage.warning('请输入登录账号')
return
@@ -1039,7 +1055,7 @@
return
}
}
- if (accountForm.loginPassword !== accountForm.confirmPassword) {
+ if ((requiresMerchantOperator.value || updatingMerchantPassword) && accountForm.loginPassword !== accountForm.confirmPassword) {
ElMessage.warning('两次输入的密码不一致')
return
}
@@ -1058,7 +1074,7 @@
latitude: Number(form.latitude || 0) || undefined,
canLoginMerchantApp: requiresMerchantOperator.value ? 1 : Number(form.canLoginMerchantApp ?? 1),
loginAccount: !isEditMode.value ? accountForm.loginAccount : undefined,
- loginPassword: !isEditMode.value ? accountForm.loginPassword : undefined,
+ loginPassword: !isEditMode.value || updatingMerchantPassword ? accountForm.loginPassword : undefined,
groupBuyingEnabled: form.groupBuyingEnabled,
usageInstructions: form.usageInstructions,
rightsText: form.rightsText,
@@ -1279,13 +1295,13 @@
<el-switch v-model="form.canLoginMerchantApp" :active-value="1" :inactive-value="0" />
</el-form-item>
<el-form-item label="登录账号" :error="accountTaken ? '登录账号已存在,请更换' : ''">
- <el-input v-model="accountForm.loginAccount" maxlength="32" :readonly="isEditMode" :placeholder="isEditMode ? '未配置登录账号' : '仅支持英文和数字'" @input="normalizeLoginAccount" @blur="checkLoginAccountAvailable" />
+ <el-input v-model="accountForm.loginAccount" maxlength="32" :disabled="isEditMode" :placeholder="isEditMode ? '未配置登录账号' : '仅支持英文和数字'" @input="normalizeLoginAccount" @blur="checkLoginAccountAvailable" />
</el-form-item>
- <el-form-item v-if="!isEditMode" label="登录密码">
- <el-input v-model="accountForm.loginPassword" type="password" show-password />
+ <el-form-item label="登录密码">
+ <el-input v-model="accountForm.loginPassword" type="password" show-password :placeholder="isEditMode ? '留空则不修改,6至64位' : ''" />
</el-form-item>
- <el-form-item v-if="!isEditMode" label="确认密码">
- <el-input v-model="accountForm.confirmPassword" type="password" show-password />
+ <el-form-item label="确认密码">
+ <el-input v-model="accountForm.confirmPassword" type="password" show-password :placeholder="isEditMode ? '再次输入新密码' : ''" />
</el-form-item>
</el-form>
</section>
@@ -1762,6 +1778,11 @@
<el-input v-model="couponEditForm.couponName" placeholder="请输入优惠券名称" />
</div>
+ <div class="coupon-edit-row coupon-amount-row">
+ <label>优惠券金额</label>
+ <div class="coupon-amount-control"><el-input-number v-model="couponEditForm.couponAmount" :min="0.01" :precision="2" :step="1" controls-position="right" placeholder="请输入优惠券金额" /><span>元</span></div>
+ </div>
+
<div class="coupon-edit-row coupon-requirement-row">
<label>使用要求</label>
<el-radio-group v-model="couponEditForm.useRequirement">
diff --git a/talent-card-admin/src/views/MerchantSettlementView.vue b/talent-card-admin/src/views/MerchantSettlementView.vue
index e6a50cb..c01c918 100644
--- a/talent-card-admin/src/views/MerchantSettlementView.vue
+++ b/talent-card-admin/src/views/MerchantSettlementView.vue
@@ -197,7 +197,7 @@
<section class="table-panel">
<el-table v-loading="loading" :data="rows" border class="settlement-table" empty-text="暂无数据">
<template v-if="kind === 'withdrawals'">
- <el-table-column prop="withdrawalNo" label="提现Id" min-width="156" />
+ <el-table-column prop="withdrawalNo" label="提现id" min-width="156" />
<el-table-column prop="merchantName" label="商家名称" min-width="130" show-overflow-tooltip />
<el-table-column prop="contactMobile" label="联系方式" min-width="118" />
<el-table-column prop="merchantType" label="商家类型" min-width="110" />
@@ -211,7 +211,7 @@
<el-table-column prop="cardMasked" label="提现卡号" min-width="150" />
</template>
<template v-else-if="kind === 'onboardings'">
- <el-table-column prop="applicationNo" label="进件申请Id" min-width="156" />
+ <el-table-column prop="applicationNo" label="进件id" min-width="156" />
<el-table-column prop="merchantName" label="商家名称" min-width="150" show-overflow-tooltip />
<el-table-column prop="contactMobile" label="联系方式" min-width="118" />
<el-table-column prop="merchantType" label="商家类型" min-width="110" />
@@ -219,7 +219,7 @@
<el-table-column prop="subjectType" label="主体类型" min-width="105" />
</template>
<template v-else>
- <el-table-column prop="id" label="申请Id" min-width="100" />
+ <el-table-column prop="id" label="申请id" min-width="100" />
<el-table-column prop="merchantName" label="商家名称" min-width="150" show-overflow-tooltip />
<el-table-column prop="contactMobile" label="联系方式" min-width="118" />
<el-table-column prop="merchantType" label="商家类型" min-width="110" />
diff --git a/talent-card-server/src/main/java/com/talentcard/server/business/admin/AdminBusinessService.java b/talent-card-server/src/main/java/com/talentcard/server/business/admin/AdminBusinessService.java
index 43bdb0c..186a531 100644
--- a/talent-card-server/src/main/java/com/talentcard/server/business/admin/AdminBusinessService.java
+++ b/talent-card-server/src/main/java/com/talentcard/server/business/admin/AdminBusinessService.java
@@ -459,6 +459,17 @@
ensureUpdated(updated);
Long storeId = findOrCreateDefaultStore(id, request.merchantName(), request.address());
syncMerchantBusinessHours(id, storeId, request.businessHours());
+ if (request.loginPassword() != null && !request.loginPassword().isBlank()) {
+ if (request.loginPassword().length() < 6 || request.loginPassword().length() > 64) {
+ throw new BusinessException(ErrorCode.BAD_REQUEST, "商户登录密码长度须为6至64位");
+ }
+ int passwordUpdated = jdbcTemplate.update("""
+ update merchant_operator
+ set password_hash = ?
+ where merchant_id = ? and role_code = 'owner' and deleted = 0
+ """, MerchantPasswordHasher.encode(request.loginPassword()), id);
+ if (passwordUpdated == 0) throw new BusinessException(ErrorCode.CONFLICT, "该商家未配置商户端登录账号");
+ }
return merchant(id);
}
diff --git a/talent-card-server/src/main/java/com/talentcard/server/business/admin/CouponCreateRequest.java b/talent-card-server/src/main/java/com/talentcard/server/business/admin/CouponCreateRequest.java
index 36cf118..c450ad0 100644
--- a/talent-card-server/src/main/java/com/talentcard/server/business/admin/CouponCreateRequest.java
+++ b/talent-card-server/src/main/java/com/talentcard/server/business/admin/CouponCreateRequest.java
@@ -4,6 +4,7 @@
import java.util.List;
import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
@@ -16,6 +17,8 @@
@NotBlank
String conditionType,
BigDecimal conditionAmount,
+ @NotNull
+ @DecimalMin(value = "0.01")
BigDecimal couponAmount,
List<String> talentLevels,
String validStart,
diff --git a/talent-card-server/src/main/java/com/talentcard/server/business/admin/CouponUpdateRequest.java b/talent-card-server/src/main/java/com/talentcard/server/business/admin/CouponUpdateRequest.java
index 7bffa56..f68890a 100644
--- a/talent-card-server/src/main/java/com/talentcard/server/business/admin/CouponUpdateRequest.java
+++ b/talent-card-server/src/main/java/com/talentcard/server/business/admin/CouponUpdateRequest.java
@@ -4,6 +4,7 @@
import java.util.List;
import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
@@ -16,6 +17,8 @@
@NotBlank
String conditionType,
BigDecimal conditionAmount,
+ @NotNull
+ @DecimalMin(value = "0.01")
BigDecimal couponAmount,
List<String> talentLevels,
String validStart,
diff --git a/talent-card-server/src/main/java/com/talentcard/server/business/admin/MerchantUpdateRequest.java b/talent-card-server/src/main/java/com/talentcard/server/business/admin/MerchantUpdateRequest.java
index ea1b01d..9a4ab96 100644
--- a/talent-card-server/src/main/java/com/talentcard/server/business/admin/MerchantUpdateRequest.java
+++ b/talent-card-server/src/main/java/com/talentcard/server/business/admin/MerchantUpdateRequest.java
@@ -35,6 +35,7 @@
@Size(max = 64)
String serviceLabel,
List<@Valid MerchantBusinessHourRequest> businessHours,
- Byte canLoginMerchantApp
+ Byte canLoginMerchantApp,
+ @Size(max = 64) String loginPassword
) {
}
diff --git a/talent-card-server/src/main/java/com/talentcard/server/business/merchant/HuifuMerchantWithdrawalService.java b/talent-card-server/src/main/java/com/talentcard/server/business/merchant/HuifuMerchantWithdrawalService.java
index a95b015..411d0b7 100644
--- a/talent-card-server/src/main/java/com/talentcard/server/business/merchant/HuifuMerchantWithdrawalService.java
+++ b/talent-card-server/src/main/java/com/talentcard/server/business/merchant/HuifuMerchantWithdrawalService.java
@@ -1,6 +1,7 @@
package com.talentcard.server.business.merchant;
import com.talentcard.server.business.DatabaseRows;
+import com.alibaba.fastjson.JSON;
import com.talentcard.server.business.payment.huifu.HuifuPaymentGatewayException;
import com.talentcard.server.business.payment.huifu.HuifuWithdrawalCommand;
import com.talentcard.server.business.payment.huifu.HuifuWithdrawalGateway;
@@ -68,7 +69,19 @@
private long number(Object v) { return v instanceof Number n ? n.longValue() : 0; }
private record PreparedWithdrawal(String withdrawalNo, long amountCents, String huifuId, String tokenNo) {}
@SuppressWarnings("unchecked") private record ProviderResult(boolean success, boolean failed, String code, String message, String providerNo) {
- static ProviderResult from(Map<String, Object> response) { Map<String,Object> d=response.get("data") instanceof Map<?,?> map?(Map<String,Object>)map:response; String code=value(d,"sub_resp_code","resp_code","code"), status=value(d,"trans_status","trans_stat"), message=value(d,"sub_resp_desc","resp_desc","message"), no=value(d,"hf_seq_id"); boolean accepted="00000000".equals(code)||"10000".equals(code)||"0".equals(code); boolean ok=accepted&&("S".equalsIgnoreCase(status)||"SUCCESS".equalsIgnoreCase(status)); boolean pending="P".equalsIgnoreCase(status)||"PROCESSING".equalsIgnoreCase(status)||status.isBlank(); return new ProviderResult(ok,!ok&&!pending,code,message,no); }
+ static ProviderResult from(Map<String, Object> response) {
+ Object raw = response.get("data");
+ Map<String, Object> d = raw instanceof Map<?, ?> map ? (Map<String, Object>) map
+ : raw instanceof String json && !json.isBlank() ? JSON.parseObject(json, Map.class) : response;
+ String code = value(d, "sub_resp_code", "resp_code", "code");
+ String status = value(d, "trans_stat", "trans_status");
+ String message = value(d, "sub_resp_desc", "resp_desc", "message");
+ String no = value(d, "hf_seq_id", "bank_order_no");
+ boolean accepted = "00000000".equals(code) || "10000".equals(code) || "0".equals(code) || "SUCCESS".equalsIgnoreCase(code);
+ boolean ok = accepted && ("S".equalsIgnoreCase(status) || "SUCCESS".equalsIgnoreCase(status) || "PAID".equalsIgnoreCase(status));
+ boolean pending = "P".equalsIgnoreCase(status) || "PROCESSING".equalsIgnoreCase(status) || "PENDING".equalsIgnoreCase(status) || status.isBlank();
+ return new ProviderResult(ok, !ok && !pending, code, message, no);
+ }
static String value(Map<String,Object> data,String...keys){for(String key:keys)if(data.get(key)!=null)return String.valueOf(data.get(key)).trim();return "";}
}
}
diff --git a/talent-card-server/src/main/java/com/talentcard/server/business/merchant/MerchantSettlementService.java b/talent-card-server/src/main/java/com/talentcard/server/business/merchant/MerchantSettlementService.java
index 66ef75b..50f2981 100644
--- a/talent-card-server/src/main/java/com/talentcard/server/business/merchant/MerchantSettlementService.java
+++ b/talent-card-server/src/main/java/com/talentcard/server/business/merchant/MerchantSettlementService.java
@@ -112,7 +112,7 @@
private void store(MerchantPrincipal principal) { if (rows("select id from merchant_store where id = ? and merchant_id = ? and status = 1 and deleted = 0", principal.storeId(), principal.merchantId()).isEmpty()) throw new BusinessException(ErrorCode.FORBIDDEN, "当前门店不可操作"); }
private Map<String, Object> onboarding(Map<String, Object> row) { return Map.of("id", String.valueOf(row.get("id")), "applicationNo", row.get("applicationNo"), "status", row.get("status"), "reason", clean(row.get("rejectReason")), "submittedAt", String.valueOf(row.get("createdAt"))); }
private Map<String, Object> bankCard(Map<String, Object> row) { return Map.of("id", String.valueOf(row.get("id")), "bankName", row.get("bankName"), "branchName", row.get("branchName"), "cardMasked", row.get("cardMasked"), "status", row.get("status"), "reason", clean(row.get("rejectReason")), "createdAt", String.valueOf(row.get("createdAt"))); }
- private Map<String, Object> withdrawal(Map<String, Object> row) { Map<String, Object> value = new LinkedHashMap<>(); value.put("id", String.valueOf(row.get("id"))); value.put("withdrawalNo", row.get("withdrawalNo")); value.put("amountCents", row.get("amountCents")); value.put("status", row.get("status")); value.put("reason", clean(row.get("failureReason"))); value.put("bankName", clean(row.get("bankName"))); value.put("cardMasked", clean(row.get("cardMasked"))); value.put("createdAt", String.valueOf(row.get("createdAt"))); return value; }
+ private Map<String, Object> withdrawal(Map<String, Object> row) { Map<String, Object> value = new LinkedHashMap<>(); value.put("id", String.valueOf(row.get("id"))); value.put("withdrawalNo", row.get("withdrawalNo")); value.put("amountCents", row.get("amountCents")); value.put("status", row.get("status")); value.put("reason", clean(row.get("failureReason"))); value.put("bankName", clean(row.get("bankName"))); value.put("cardMasked", clean(row.get("cardMasked"))); value.put("createdAt", String.valueOf(row.get("createdAt"))); value.put("reviewedAt", clean(row.get("reviewedAt"))); value.put("paidAt", clean(row.get("paidAt"))); return value; }
private Map<String, Object> branch(String id, String branchName, String bankName) { return Map.of("id", id, "branchName", branchName, "bankName", bankName); }
private List<Map<String, Object>> rows(String sql, Object... args) { return DatabaseRows.normalize(jdbc.queryForList(sql, args)); }
private Long number(String value) { try { return Long.valueOf(value); } catch (Exception ignored) { throw new BusinessException(ErrorCode.BAD_REQUEST, "编号无效"); } }
diff --git a/talent-card-server/src/main/resources/application.yml b/talent-card-server/src/main/resources/application.yml
index e702e92..9516b0e 100644
--- a/talent-card-server/src/main/resources/application.yml
+++ b/talent-card-server/src/main/resources/application.yml
@@ -52,29 +52,24 @@
payment:
huifu:
# Keep disabled until the Huifu test/production product, merchant and keys are provisioned.
- enabled: ${HUIFU_ENABLED:false}
- environment: ${HUIFU_ENVIRONMENT:test}
- system-id: ${HUIFU_SYSTEM_ID:}
- product-id: ${HUIFU_PRODUCT_ID:}
- merchant-id: ${HUIFU_MERCHANT_ID:}
- mini-program-app-id: ${HUIFU_MINI_PROGRAM_APP_ID:}
- private-key: ${HUIFU_PRIVATE_KEY:}
- platform-public-key: ${HUIFU_PLATFORM_PUBLIC_KEY:}
- notify-url: ${HUIFU_NOTIFY_URL:}
- withdrawal-product-id: ${HUIFU_WITHDRAWAL_PRODUCT_ID:}
- withdrawal-notify-url: ${HUIFU_WITHDRAWAL_NOTIFY_URL:}
- withdrawal-into-account-date-type: ${HUIFU_WITHDRAWAL_INTO_ACCOUNT_DATE_TYPE:D0}
+ enabled: true
+ environment: prod
+ system-id: 6666000212191071
+ product-id: KAZX
+ merchant-id: 6666000212191071
+ mini-program-app-id: wx6ae2cadfea28fce1
+ private-key: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCcsR3R3nUFCv/4l6vczGk6j4UgZhuMm/fTs2MQQqFkIuoISd38TOiIMaLS3KLYL+D+qhUCps56svOK/3M/kdyXfPsBphJyXB5Ar7il8pJ6BDa38StzTrlRm+IXX+4diJvqxUimetjpx0OfnqRkNYQWc/ZVeoEdQbITqLyxogy0R3rkR/wnOTwxHBbkCYsDCxSon4W0SoM5fLs3o947INbPUbUsf0vL5l6NiqlV9YsZ3qAWvvPoPX4HI1s5maMiVKfUwe27P3ARW9n6n3tadBzbB6lrmAEcFoihXpZjNN9UaRKjYfYjTS9JRkDqRyYGhz5cHp9GthEksMvhLP84oEanAgMBAAECggEAKV6d5+Ya8TzpPiIVTxNtCPgRhu9OgL5BHVS0Z/ufjselEJEoGc/YaPTp71AvcnKZrcMMDwopY73RjTZKoFMLcbeesd296xGlLyD12GKGB88K/1+eQDBdv00v0UoTnRqPR9nUh5fRo09n7EG/V3HFsQPm2sSgSDArKwOW5jNJtWLsGIVCW8gg3RQ9YlZ5HW85neBnjBFnrMA9S/hxwZD336M8CiViIa0v9o5CS7qxAqXMzzoOC7Vr3hZ/X0i/5GrSH9DRO7ougSmP4F/zCEc2dViA0rOJ6m3OuS0osh5FulihOBOjI5uLETEt4K/7PuV9xOd4oAz0OBpJ2bhPf3YpGQKBgQDQdEcmnlULa3jTSrbr0vYA3iMpcls/8F8+I/QymMXHAUeVyc8XKuGzblx+/n3x8ZoAvdVg1ZAO5wgLh/P0BEL74iWhfHedjt8xcdk2R5U0RuOnx1TUTKrmLKRZ5gStNFnGlK4yVwpiNqHn/cr+gYYeZSPARFVi00WdXbqVbEEf2wKBgQDAbmo4uHrmHN5GYvt8lVmT9aha1IpKOOLenjd+Y/vKGJcvHzOVCHnOh8OGO/4p7CxOq0sshER713jW2cFGO3xpFEFpIc45pDQ8Ut0XuSoOjKtwPzid8vKX7A1If6FDLMNy7bYR1qqnIJNJnN/f3lsksX+es7QTuRShhA0X0mnEJQKBgBE5OlPHi1FZPO+AYeLUWH0z3YXkLAP5cXUp9Vhk5RqPUYHf3duVMHbp7HyVvIRMKSRciw/oNXH1xuVlA1QrMfmr8t4H8b4z+t8UABWLDpn2LYNEZg7wksJX9/Bxn/tLswtEZ6xDItmAmBoaDza+Lv2975lITCHRQRRClVMZUbw3AoGAVFrSs6SjM2aqRZ60ccuipgrFT3PmX10Kjt46o2w+/zvCYoTB/u3lP5UiMCDybPw7/Ax4+r3Mq6EsNrrkT95ZzlJzzhtkpL4Hq/4DL5gbCJ+/s4fzUMIkeFahCAvy7FieIS51ZuJ8kqDkEBv1w9RcjfxFXpOWOCMlGRyjBLNXbAUCgYEAjcTEuXctlGEjtO1MBaHc4hkkpiPG7gxV/Bq1XXLszv4LTaOfDEpjGGBpVYtb3URYrif/HzX8T1KHELvPziuqP2ghaJQzbztep1TGQ48A5VpOvHdA/w/+k4JJl0PyKBPrFXiHIw1if7rnvOLt27TymwXSaJquSz7Sz2+wXFxzKJk=
+ platform-public-key: ${HUIFU_PLATFORM_PUBLIC_KEY:MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgxOuXt1UEdZNPk6sntqiIlcs7e+0fZ3CVgKMV2Qv4ixwKmha5etL5WyIONmjoGEHePHpanYkXvm0kHkbbDkjpyHDFdEshuP1eGx5tRUTISRvXY/iFuD3xyeWSsvvjW6hS29OvqVCPcnBUC7jcRj15dJS2IpYR0iDGNS+/M0TyOFwRQME07At+qO+MSjKu4aRIjlglukjD7YbE2s35uV066nsBUCZUNpGPuju1pieUW4zyoIW6K70dyXCQy61oRI6JmA4JRCOJV1BE80N3GrLI4Pbq1C3ZBTgOPi5RWYNq1hfu807BDkPLc3dJKYt22Ea1jPJsg6e/B9Xbzh6JaUNAQIDAQAB}
+ notify-url: ${HUIFU_NOTIFY_URL:http://221.182.45.100:11451/api/payments/huifu/notify}
+ withdrawal-product-id: ${HUIFU_WITHDRAWAL_PRODUCT_ID:KAZX}
+ withdrawal-notify-url: ${HUIFU_WITHDRAWAL_NOTIFY_URL:http://221.182.45.100:11451/api/payments/huifu/withdrawal-notify}
delay-account: ${HUIFU_DELAY_ACCOUNT:false}
- reconciliation:
- enabled: ${HUIFU_RECONCILIATION_ENABLED:false}
- fixed-delay-ms: ${HUIFU_RECONCILIATION_FIXED_DELAY_MS:300000}
- pending-after-minutes: ${HUIFU_RECONCILIATION_PENDING_AFTER_MINUTES:5}
wechat:
mini-program:
# The app id must be the same mini-program that invokes uni.login and uni.requestPayment.
- app-id: ${WECHAT_MINI_PROGRAM_APP_ID:}
+ app-id: ${WECHAT_MINI_PROGRAM_APP_ID:wx6ae2cadfea28fce1}
# Keep this only in the deployment environment. It is required to exchange a wx.login code for an OpenID.
- app-secret: ${WECHAT_MINI_PROGRAM_APP_SECRET:}
+ app-secret: ${WECHAT_MINI_PROGRAM_APP_SECRET:7bf743b066523bbb2d6de80decabc15b}
management:
endpoints:
--
Gitblit v1.7.1