feat(merchant): 商户端展示完成条件并新增收款能力页
- 进件页:渲染 completionChecks 里未通过的那两项,用「完成条件核实」单独一段与六项阶段分开——
它们不是进件阶段,混在一列里会被读成第七、第八个阶段。pending 标题由「收款能力开通中」改为
「进件办理中」,面板底部「缺失能力」改为「缺失步骤」,与术语表的三层词义对齐。
- 商户中心新增「收款能力」入口与只读页:显示能否收款、原因、最近核实时间,配「立即核实」
(冷却中置灰并显示下次可核实时间)。
- settlement.uts 补类型与映射,路径仍集中维护在 config/api.uts;接口说明文档补两行。
| | |
| | | if (name == 'orderRevokeVerification') return '/api/merchant/orders/revoke-verification' |
| | | if (name == 'merchantOnboardingStatus') return '/api/merchant/onboarding/status' |
| | | if (name == 'merchantOnboarding') return '/api/merchant/onboarding' |
| | | if (name == 'merchantPaymentReadiness') return '/api/merchant/payment-readiness' |
| | | if (name == 'merchantPaymentReadinessVerify') return '/api/merchant/payment-readiness/verify' |
| | | if (name == 'merchantBankCards') return '/api/merchant/bank-cards' |
| | | if (name == 'merchantBankBranches') return '/api/merchant/bank-branches' |
| | | return '' |
| | |
| | | | 本月总览 | `/api/merchant/settlement-statistics/current-month` | GET | 当前会话 | 本月汇总口径 | 已接入 | |
| | | | 首页结算卡 | `/api/merchant/settlement-statistics/dashboard-cards` | GET | 当前会话 | 交易总额、今日核销、本周核销、补贴总额 | 已接入 | |
| | | | 结算明细导出 | `/api/merchant/settlement-statistics/export` | GET | 状态、日期范围 | Excel 文件 | 已接入 | |
| | | | 商户进件状态 | `/api/merchant/onboarding/status` | GET | 当前门店会话 | `unsubmitted`、`pending`、`approved`、`rejected`、驳回原因 | 已接入 | |
| | | | 商户进件状态 | `/api/merchant/onboarding/status` | GET | 当前门店会话 | `unsubmitted`、`pending`、`approved`、`rejected`、驳回原因、六项阶段、两项完成条件核实 | 已接入 | |
| | | | 提交商户进件 | `/api/merchant/onboarding` | POST | 主体类型、企业/个人资料、联系人、幂等键 | 进件单 ID、审核状态、提交时间 | 已接入 | |
| | | | 收款能力自查 | `/api/merchant/payment-readiness` | GET | 当前门店会话 | `ready`(能否收款)、`reason`(与下单被拒同一句文案)、`verifiedAt`(最近核实时间)、`nextVerifyAt`、`verifyAvailable` | 已接入 | |
| | | | 手动核实收款能力 | `/api/merchant/payment-readiness/verify` | POST | 当前门店会话 | 同上,另加 `refreshed`(本次是否真的查了通道);服务端有冷却,冷却期内直接返回当前快照 | 已接入 | |
| | | | 提现概览 | `/api/merchant/withdrawals/summary` | GET | 当前门店会话 | 可提现金额、冻结金额,单位分 | 已接入 | |
| | | | 提现记录 | `/api/merchant/withdrawals` | GET | 当前门店会话、分页 | 提现记录(状态只展示处理中/已到账/提现失败)、分页信息 | 已接入 | |
| | | | 银行卡列表 | `/api/merchant/bank-cards` | GET | 当前门店会话 | 银行卡 ID、银行名称、脱敏卡号、审核状态 | 已接入 | |
| | |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/payment-capability/payment-capability", |
| | | "style": { |
| | | "navigationStyle": "custom", |
| | | "backgroundColor": "#F4F6F9" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/bank-card-add/bank-card-add", |
| | | "style": { |
| | | "navigationStyle": "custom", |
| | |
| | | <text class="qr-hint">请本人用微信扫码完成实名授权</text> |
| | | </view> |
| | | </view> |
| | | <text v-if="canRefresh" class="panel-foot">刷新会查询进度并继续办理已满足条件的缺失能力。本人实名或授权办理后,请再次刷新。</text> |
| | | <!-- 完成判定还要求结算银行卡核实与通道商户状态,它们不是进件阶段,所以单独一段: |
| | | 六项全绿却停在「办理中」时,商户必须能看到究竟缺哪一项。 --> |
| | | <view v-if="failedChecks.length > 0" class="progress-check"> |
| | | <text class="progress-check-head">完成条件核实</text> |
| | | <view v-for="(check, index) in failedChecks" :key="check.key" class="progress-stage" :class="index == failedChecks.length - 1 ? 'progress-stage-last' : ''"> |
| | | <view class="stage-head"> |
| | | <text class="stage-label">{{check.label}}</text> |
| | | <text class="stage-pill" :class="pillClass(check.status)">{{stageLabel(check.status)}}</text> |
| | | </view> |
| | | <text v-if="check.reason.length > 0" class="stage-reason">{{check.reason}}</text> |
| | | </view> |
| | | </view> |
| | | <text v-if="canRefresh" class="panel-foot">刷新会查询进度并继续办理已满足条件的缺失步骤。本人实名或授权办理后,请再次刷新。</text> |
| | | </view> |
| | | |
| | | <view class="action-area"> |
| | |
| | | </template> |
| | | |
| | | <script setup lang="uts"> |
| | | import { loadOnboardingStatus, refreshOnboardingProgress, submitOnboardingRealname, SettlementOnboarding, OnboardingStage } from '../../services/settlement.uts' |
| | | import { loadOnboardingStatus, refreshOnboardingProgress, submitOnboardingRealname, SettlementOnboarding, OnboardingStage, OnboardingCheck } from '../../services/settlement.uts' |
| | | import { getMerchantOnboardingDraftStorageKey } from '../../services/onboarding-draft.uts' |
| | | |
| | | const onboardingStatus = ref('pending') |
| | |
| | | const lastSyncedAt = ref('') |
| | | const syncError = ref('') |
| | | const stages = ref<Array<OnboardingStage>>([]) |
| | | const completionChecks = ref<Array<OnboardingCheck>>([]) |
| | | // 已核实通过的不显示:那两项是完成条件,不是要商户办理的步骤,全绿时多说一行只会添噪音。 |
| | | const failedChecks = computed((): Array<OnboardingCheck> => completionChecks.value.filter((check: OnboardingCheck): boolean => check.status != 'confirmed')) |
| | | const stageLabel = (status: string): string => { |
| | | if (status == 'confirmed') return '已确认' |
| | | if (status == 'pending') return '处理中' |
| | |
| | | lastSyncedAt.value = item.lastSyncedAt |
| | | syncError.value = item.syncError |
| | | stages.value = item.stages |
| | | completionChecks.value = item.completionChecks |
| | | } |
| | | const refreshProgress = (retry: boolean) => { |
| | | if (syncing.value) return |
| | |
| | | uni.showToast({ title: error.message != null ? error.message : '发起实名失败,请稍后重试', icon: 'none' }) |
| | | }) |
| | | } |
| | | // 「收款能力」一词只指通道侧的事实,进件阶段还在推进时不该说成「收款能力开通中」。 |
| | | const statusTitle = computed((): string => { |
| | | if (onboardingStatus.value == 'approved') return '商户进件已完成' |
| | | if (onboardingStatus.value == 'rejected') return '进件审核未通过' |
| | | if (refreshable.value) return '收款能力开通中' |
| | | if (refreshable.value) return '进件办理中' |
| | | return '进件信息审核中' |
| | | }) |
| | | const statusIcon = computed((): string => { |
| | |
| | | .stage-pill-failed { color: #F06A66; background-color: #FEF3F2; } |
| | | .stage-reason { display: block; margin-top: 10rpx; color: #9AA4B3; font-size: 24rpx; line-height: 36rpx; } |
| | | |
| | | /* 完成条件核实:与六项阶段分开一段,避免把通道侧的事实读成第七个进件阶段。 */ |
| | | .progress-check { margin-top: 8rpx; padding-top: 22rpx; border-top: 1rpx solid #F0F3F7; } |
| | | .progress-check-head { display: block; color: #2E394A; font-size: 27rpx; line-height: 38rpx; font-weight: 700; } |
| | | |
| | | .qr-block { display: flex; flex-direction: column; align-items: center; margin-top: 24rpx; } |
| | | .qr-frame { display: flex; padding: 22rpx; justify-content: center; align-items: center; border-radius: 24rpx; background-color: #F7F9FC; } |
| | | .qr-image { width: 340rpx; height: 340rpx; } |
| | |
| | | <view class="setting-row" @tap="handleMenuAction('商户权益')"><view class="setting-leading"><image class="setting-icon" src="/static/icons/lucide/badge-check.svg" mode="aspectFit"></image><text class="setting-label">商户权益</text></view><image class="setting-chevron" src="/static/icons/lucide/chevron-right.svg" mode="aspectFit"></image></view> |
| | | <view class="setting-row" @tap="handleMenuAction('商户协议')"><view class="setting-leading"><image class="setting-icon" src="/static/icons/lucide/file-text.svg" mode="aspectFit"></image><text class="setting-label">商户协议</text></view><image class="setting-chevron" src="/static/icons/lucide/chevron-right.svg" mode="aspectFit"></image></view> |
| | | <view class="setting-row" @tap="handleMenuAction('商户进件')"><view class="setting-leading"><image class="setting-icon" src="/static/icons/lucide/wallet-cards-gray.svg" mode="aspectFit"></image><text class="setting-label">商户进件</text></view><image class="setting-chevron" src="/static/icons/lucide/chevron-right.svg" mode="aspectFit"></image></view> |
| | | <view class="setting-row" @tap="handleMenuAction('收款能力')"><view class="setting-leading"><image class="setting-icon" src="/static/icons/lucide/hand-coins.svg" mode="aspectFit"></image><text class="setting-label">收款能力</text></view><image class="setting-chevron" src="/static/icons/lucide/chevron-right.svg" mode="aspectFit"></image></view> |
| | | <view class="setting-row" @tap="handleMenuAction('设置')"><view class="setting-leading"><image class="setting-icon" src="/static/icons/lucide/settings.svg" mode="aspectFit"></image><text class="setting-label">设置</text></view><image class="setting-chevron" src="/static/icons/lucide/chevron-right.svg" mode="aspectFit"></image></view> |
| | | </view> |
| | | </view> |
| | |
| | | }) |
| | | return |
| | | } |
| | | if (name === '收款能力') { |
| | | uni.navigateTo({ url: '/pages/payment-capability/payment-capability' }) |
| | | return |
| | | } |
| | | if (name === '营业时间') { |
| | | uni.navigateTo({ url: '/pages/business-hours/business-hours' }) |
| | | return |
| New file |
| | |
| | | <template> |
| | | <view class="page"> |
| | | <view class="navigation-bar"> |
| | | <view class="back-button" @tap="goBack"><image class="back-icon" src="/static/icons/lucide/back-left.svg" mode="aspectFit"></image></view> |
| | | <text class="page-title">收款能力</text> |
| | | </view> |
| | | |
| | | <scroll-view scroll-y="true" class="result-content"> |
| | | <page-feedback v-if="loadState != 'success'" :state="loadState" loading-text="收款能力加载中" error-text="收款能力加载失败,点击重试" @retry="loadReadiness"></page-feedback> |
| | | <view v-else> |
| | | <view class="status-hero"> |
| | | <view class="status-icon-wrap"><image class="status-icon" :src="statusIcon" mode="aspectFit"></image></view> |
| | | <text class="status-title">{{statusTitle}}</text> |
| | | <text v-if="readiness.reason.length > 0" class="status-reason">{{readiness.reason}}</text> |
| | | </view> |
| | | |
| | | <view class="info-panel"> |
| | | <view class="info-row"> |
| | | <text class="info-label">最近核实时间</text> |
| | | <text class="info-value">{{verifiedText}}</text> |
| | | </view> |
| | | <view v-if="nextVerifyText.length > 0" class="info-row"> |
| | | <text class="info-label">下次可核实</text> |
| | | <text class="info-value">{{nextVerifyText}}</text> |
| | | </view> |
| | | <text class="info-hint">收款状态以通道最近一次核实的结果为准。平台会定时自动核实,也可以手动核实一次;刚核实过的短时间内不会重复查询通道。</text> |
| | | </view> |
| | | |
| | | <view class="action-area"> |
| | | <view class="action-primary" :class="verifyDisabled ? 'button-disabled' : ''" @tap="verifyNow"><text>{{verifyButtonLabel}}</text></view> |
| | | <view class="action-secondary" @tap="goBack"><text>返回商户中心</text></view> |
| | | </view> |
| | | </view> |
| | | <view class="safe-space"></view> |
| | | </scroll-view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup lang="uts"> |
| | | import { hasActiveSession } from '../../services/session.uts' |
| | | import { loadPaymentReadiness, verifyPaymentReadiness, MerchantPaymentReadiness } from '../../services/settlement.uts' |
| | | |
| | | const readiness = ref<MerchantPaymentReadiness>({ ready: false, reason: '', verifiedAt: '', nextVerifyAt: '', verifyAvailable: false, refreshed: false }) |
| | | const loadState = ref('loading') |
| | | const syncing = ref(false) |
| | | |
| | | /** 通道回的是数据库时间戳(形如 2026-09-17 12:34:56.0),去掉毫秒再展示。 */ |
| | | const displayTime = (value: string): string => { |
| | | if (value.length == 0) return '' |
| | | const dotIndex = value.indexOf('.') |
| | | const trimmed = dotIndex > 0 ? value.substring(0, dotIndex) : value |
| | | return trimmed.replace('T', ' ') |
| | | } |
| | | const verifiedText = computed((): string => readiness.value.verifiedAt.length > 0 ? displayTime(readiness.value.verifiedAt) : '尚未核实') |
| | | // 冷却中才显示「下次可核实」:能点的时候显示这行只会让人以为还有别的限制。 |
| | | const nextVerifyText = computed((): string => readiness.value.verifyAvailable ? '' : displayTime(readiness.value.nextVerifyAt)) |
| | | const verifyDisabled = computed((): boolean => syncing.value || !readiness.value.verifyAvailable) |
| | | const verifyButtonLabel = computed((): string => { |
| | | if (syncing.value) return '核实中...' |
| | | return readiness.value.verifyAvailable ? '立即核实' : '稍后可再次核实' |
| | | }) |
| | | // 还没通过进件、或通道商户状态异常时用「等待核实」的图标,而不是打一个红叉—— |
| | | // 这两种情况都不是商户做错了什么。 |
| | | const statusIcon = computed((): string => readiness.value.ready ? '/static/icons/lucide/badge-check-orange.svg' : '/static/icons/lucide/clock-3.svg') |
| | | const statusTitle = computed((): string => readiness.value.ready ? '现在可以收款' : '暂时无法收款') |
| | | |
| | | const applyReadiness = (item: MerchantPaymentReadiness) => { |
| | | readiness.value = item |
| | | loadState.value = 'success' |
| | | } |
| | | |
| | | const loadReadiness = () => { |
| | | if (!hasActiveSession()) { |
| | | loadState.value = 'error' |
| | | return |
| | | } |
| | | loadState.value = 'loading' |
| | | loadPaymentReadiness((item: MerchantPaymentReadiness) => { |
| | | applyReadiness(item) |
| | | }, (error: any) => { |
| | | loadState.value = 'error' |
| | | uni.showToast({ title: error.message != null ? error.message : '收款能力加载失败', icon: 'none' }) |
| | | }) |
| | | } |
| | | |
| | | const verifyNow = () => { |
| | | if (verifyDisabled.value) { |
| | | if (!readiness.value.verifyAvailable) uni.showToast({ title: '刚核实过,请稍后再试', icon: 'none' }) |
| | | return |
| | | } |
| | | syncing.value = true |
| | | verifyPaymentReadiness((item: MerchantPaymentReadiness) => { |
| | | syncing.value = false |
| | | applyReadiness(item) |
| | | uni.showToast({ title: item.refreshed ? '已重新核实通道状态' : '刚核实过,这是最近一次的结果', icon: 'none' }) |
| | | }, (error: any) => { |
| | | syncing.value = false |
| | | uni.showToast({ title: error.message != null ? error.message : '核实失败,请稍后重试', icon: 'none' }) |
| | | }) |
| | | } |
| | | |
| | | const goBack = () => { |
| | | uni.navigateBack() |
| | | } |
| | | |
| | | onShow(() => { |
| | | loadReadiness() |
| | | }) |
| | | </script> |
| | | |
| | | <style> |
| | | /* 与商户端其它页面同一套视觉:底 #F4F6F9、白卡大圆角 + 极淡弥散阴影、橙色渐变做主操作。 */ |
| | | page { background-color: #F4F6F9; } |
| | | .page { display: flex; flex: 1; width: 100%; height: 100vh; flex-direction: column; overflow: hidden; background-color: #F4F6F9; } |
| | | .navigation-bar { display: flex; height: 174rpx; padding: 68rpx 38rpx 0; flex: none; flex-direction: row; align-items: center; position: relative; background-color: #F4F6F9; box-sizing: border-box; } |
| | | .back-button { display: flex; width: 80rpx; height: 80rpx; justify-content: center; align-items: center; position: absolute; left: 18rpx; top: 79rpx; z-index: 2; } |
| | | .back-icon { width: 44rpx; height: 44rpx; } |
| | | .page-title { width: 100%; color: #202B3E; font-size: 34rpx; line-height: 48rpx; text-align: center; font-weight: 700; } |
| | | |
| | | .result-content { display: flex; flex: 1; height: 0; padding: 24rpx 38rpx 0; flex-direction: column; box-sizing: border-box; } |
| | | |
| | | .status-hero { display: flex; flex-direction: column; align-items: center; padding: 30rpx 0 8rpx; } |
| | | .status-icon-wrap { display: flex; width: 176rpx; height: 176rpx; justify-content: center; align-items: center; border-radius: 88rpx; background-image: linear-gradient(145deg, #FFF0E6 0%, #FFF8F3 100%); box-shadow: 0 18rpx 38rpx rgba(255, 107, 44, 0.12); } |
| | | .status-icon { width: 82rpx; height: 82rpx; } |
| | | .status-title { margin-top: 32rpx; color: #202B3E; font-size: 36rpx; line-height: 50rpx; font-weight: 700; text-align: center; } |
| | | .status-reason { display: block; margin-top: 16rpx; padding: 0 20rpx; color: #7D8796; font-size: 25rpx; line-height: 38rpx; text-align: center; } |
| | | |
| | | .info-panel { margin-top: 34rpx; padding: 12rpx 30rpx 22rpx; border-radius: 34rpx; background-color: #FFFFFF; box-shadow: 0 14rpx 34rpx rgba(49, 55, 66, 0.06); box-sizing: border-box; } |
| | | .info-row { display: flex; height: 88rpx; flex-direction: row; align-items: center; justify-content: space-between; border-bottom: 1rpx solid #F0F3F7; } |
| | | .info-label { color: #313744; font-size: 29rpx; line-height: 40rpx; font-weight: 700; } |
| | | .info-value { color: #7D8796; font-size: 26rpx; line-height: 38rpx; } |
| | | .info-hint { display: block; margin-top: 20rpx; color: #9AA4B3; font-size: 22rpx; line-height: 34rpx; } |
| | | |
| | | .action-area { display: flex; margin-top: 34rpx; flex-direction: column; } |
| | | .action-primary { display: flex; height: 100rpx; margin-bottom: 22rpx; justify-content: center; align-items: center; border-radius: 50rpx; color: #FFFFFF; font-size: 30rpx; line-height: 42rpx; font-weight: 700; background-image: linear-gradient(90deg, #FF8A24 0%, #FF4B36 100%); box-shadow: 0 14rpx 26rpx rgba(255, 101, 48, 0.22); } |
| | | .action-secondary { display: flex; height: 100rpx; margin-bottom: 22rpx; justify-content: center; align-items: center; border-radius: 50rpx; color: #313744; font-size: 28rpx; line-height: 38rpx; font-weight: 700; background-color: #EDF0F4; } |
| | | .button-disabled { opacity: 0.58; } |
| | | .safe-space { height: 60rpx; } |
| | | </style> |
| | |
| | | import { HttpError, request } from './http.uts' |
| | | |
| | | export type OnboardingStage = { key: string, label: string, status: string, reason: string, qrCode: string } |
| | | export type SettlementOnboarding = { id: string, applicationNo: string, status: string, reason: string, retryable: boolean, submittedAt: string, refreshable: boolean, lastSyncedAt: string, syncError: string, stages: Array<OnboardingStage> } |
| | | /** 完成判定里不在六项阶段中的另外两项核实:结算银行卡核实、通道商户状态。 */ |
| | | export type OnboardingCheck = { key: string, label: string, status: string, reason: string } |
| | | export type SettlementOnboarding = { id: string, applicationNo: string, status: string, reason: string, retryable: boolean, submittedAt: string, refreshable: boolean, lastSyncedAt: string, syncError: string, stages: Array<OnboardingStage>, completionChecks: Array<OnboardingCheck> } |
| | | export type SettlementBankCard = { id: string, bankName: string, branchName: string, cardMasked: string, status: string, reason: string, createdAt: string } |
| | | export type SettlementPage<T> = { records: Array<T>, page: number, size: number, total: number } |
| | | /** 收款能力自查:判定与下单闸门同源同文案,只返回派生结果。 */ |
| | | export type MerchantPaymentReadiness = { ready: boolean, reason: string, verifiedAt: string, nextVerifyAt: string, verifyAvailable: boolean, refreshed: boolean } |
| | | |
| | | const stringValue = (value: any): string => value == null ? '' : '' + value |
| | | const numberValue = (value: any): number => value == null ? 0 : Number(value) |
| | | /** retryable:通道侧配置已明确失败,商户可以重新提交;缺省按 false,宁可保守。 */ |
| | | 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 : ''), retryable: value != null && value.retryable == true, submittedAt: stringValue(value != null ? value.submittedAt : ''), refreshable: value != null && value.refreshable == true, lastSyncedAt: stringValue(value != null ? value.lastSyncedAt : ''), syncError: stringValue(value != null ? value.syncError : ''), stages: (value != null && value.stages instanceof Array ? value.stages : []).map((stage: any): OnboardingStage => ({ key: stringValue(stage.key), label: stringValue(stage.label), status: stringValue(stage.status), reason: stringValue(stage.reason), qrCode: stringValue(stage.qrCode) })) }) |
| | | 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 : ''), retryable: value != null && value.retryable == true, submittedAt: stringValue(value != null ? value.submittedAt : ''), refreshable: value != null && value.refreshable == true, lastSyncedAt: stringValue(value != null ? value.lastSyncedAt : ''), syncError: stringValue(value != null ? value.syncError : ''), stages: (value != null && value.stages instanceof Array ? value.stages : []).map((stage: any): OnboardingStage => ({ key: stringValue(stage.key), label: stringValue(stage.label), status: stringValue(stage.status), reason: stringValue(stage.reason), qrCode: stringValue(stage.qrCode) })), completionChecks: (value != null && value.completionChecks instanceof Array ? value.completionChecks : []).map((check: any): OnboardingCheck => ({ key: stringValue(check.key), label: stringValue(check.label), status: stringValue(check.status), reason: stringValue(check.reason) })) }) |
| | | const paymentReadiness = (value: any): MerchantPaymentReadiness => ({ ready: value != null && value.ready == true, reason: stringValue(value != null ? value.reason : ''), verifiedAt: stringValue(value != null ? value.verifiedAt : ''), nextVerifyAt: stringValue(value != null ? value.nextVerifyAt : ''), verifyAvailable: value != null && value.verifyAvailable == true, refreshed: value != null && value.refreshed == true }) |
| | | export const refreshOnboardingProgress = (retry: boolean, success: (value: SettlementOnboarding) => void, error: (value: HttpError) => void): (() => void) => request<any>({ path: '/api/merchant/onboarding/refresh?retry=' + (retry ? 'true' : 'false'), method: 'POST' }, (data: any) => success(onboarding(data)), error) |
| | | // 微信实名认证:业务开通确认生效后由商户主动推进的下一步。未决申请不会重复提交, |
| | | // retry 只对「通道已明确驳回」有意义。 |
| | |
| | | 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 : '') }) |
| | | |
| | | 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 loadPaymentReadiness = (success: (value: MerchantPaymentReadiness) => void, error: (value: HttpError) => void): (() => void) => request<any>({ path: getApiEndpoint('merchantPaymentReadiness') }, (data: any) => success(paymentReadiness(data)), error) |
| | | // 立即核实:主动查一次通道刷新能力快照,服务端有冷却,冷却期内直接回当前快照。 |
| | | export const verifyPaymentReadiness = (success: (value: MerchantPaymentReadiness) => void, error: (value: HttpError) => void): (() => void) => request<any>({ path: getApiEndpoint('merchantPaymentReadinessVerify'), method: 'POST' }, (data: any) => success(paymentReadiness(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) |
| | | export const loadBankCards = (success: (value: Array<SettlementBankCard>) => void, error: (value: HttpError) => void): (() => void) => request<any>({ path: getApiEndpoint('merchantBankCards') }, (data: any) => success((data instanceof Array ? data : []).map((item: any) => bankCard(item))), error) |
| | | export const addBankCard = (payload: any, key: string, success: (value: SettlementBankCard) => void, error: (value: HttpError) => void): (() => void) => request<any>({ path: getApiEndpoint('merchantBankCards'), method: 'POST', data: payload, idempotencyKey: key }, (data: any) => success(bankCard(data)), error) |