|
@@ -138,9 +138,11 @@
|
|
|
type="primary"
|
|
type="primary"
|
|
|
size="small"
|
|
size="small"
|
|
|
link
|
|
link
|
|
|
- @click="handleUpdateStatus(scope.row.id, scope.row.status === '0' ? '1' : '0')"
|
|
|
|
|
|
|
+ @click="
|
|
|
|
|
+ handleUpdateStatus(scope.row.id, scope.row.status === '0' ? '1' : '0')
|
|
|
|
|
+ "
|
|
|
>
|
|
>
|
|
|
- {{ scope.row.status === '0' ? '禁用' : '启用' }}
|
|
|
|
|
|
|
+ {{ scope.row.status === "0" ? "禁用" : "启用" }}
|
|
|
</el-button>
|
|
</el-button>
|
|
|
<el-button
|
|
<el-button
|
|
|
v-hasPerm="['module_system:tenant:api-key:delete']"
|
|
v-hasPerm="['module_system:tenant:api-key:delete']"
|
|
@@ -176,7 +178,13 @@
|
|
|
<el-input v-model="formData.tenant_id" placeholder="可选,默认使用当前租户" :maxlength="20" />
|
|
<el-input v-model="formData.tenant_id" placeholder="可选,默认使用当前租户" :maxlength="20" />
|
|
|
</el-form-item> -->
|
|
</el-form-item> -->
|
|
|
<el-form-item label="过期天数" prop="expired_days">
|
|
<el-form-item label="过期天数" prop="expired_days">
|
|
|
- <el-input v-model.number="formData.expired_days" type="number" placeholder="请输入过期天数" min="1" :maxlength="4" />
|
|
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model.number="formData.expired_days"
|
|
|
|
|
+ type="number"
|
|
|
|
|
+ placeholder="请输入过期天数"
|
|
|
|
|
+ min="1"
|
|
|
|
|
+ :maxlength="4"
|
|
|
|
|
+ />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
<el-form-item label="描述" prop="description">
|
|
<el-form-item label="描述" prop="description">
|
|
|
<el-input
|
|
<el-input
|
|
@@ -240,7 +248,11 @@
|
|
|
<el-descriptions-item label="API Secret">
|
|
<el-descriptions-item label="API Secret">
|
|
|
<div class="api-key-detail">
|
|
<div class="api-key-detail">
|
|
|
<span>{{ apiKeyDetail.api_secret }}</span>
|
|
<span>{{ apiKeyDetail.api_secret }}</span>
|
|
|
- <el-button type="text" size="small" @click="copyToClipboard(apiKeyDetail.api_secret || '')">
|
|
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ type="text"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ @click="copyToClipboard(apiKeyDetail.api_secret || '')"
|
|
|
|
|
+ >
|
|
|
复制
|
|
复制
|
|
|
</el-button>
|
|
</el-button>
|
|
|
</div>
|
|
</div>
|
|
@@ -294,14 +306,10 @@
|
|
|
</template>
|
|
</template>
|
|
|
<div class="config-empty">
|
|
<div class="config-empty">
|
|
|
<el-empty description="暂无开放平台配置" />
|
|
<el-empty description="暂无开放平台配置" />
|
|
|
- <p style="margin: 16px 0; color: #606266;">
|
|
|
|
|
|
|
+ <p style="margin: 16px 0; color: #606266">
|
|
|
请先创建开放平台配置,才能收到平台回调通知。
|
|
请先创建开放平台配置,才能收到平台回调通知。
|
|
|
</p>
|
|
</p>
|
|
|
- <el-button
|
|
|
|
|
- type="primary"
|
|
|
|
|
- :loading="configLoading"
|
|
|
|
|
- @click="handleCreateConfig"
|
|
|
|
|
- >
|
|
|
|
|
|
|
+ <el-button type="primary" :loading="configLoading" @click="handleCreateConfig">
|
|
|
创建配置
|
|
创建配置
|
|
|
</el-button>
|
|
</el-button>
|
|
|
</div>
|
|
</div>
|
|
@@ -347,15 +355,11 @@
|
|
|
/>
|
|
/>
|
|
|
</el-form-item> -->
|
|
</el-form-item> -->
|
|
|
<el-form-item label="回调地址">
|
|
<el-form-item label="回调地址">
|
|
|
- <el-input
|
|
|
|
|
- v-model="configForm.return_url"
|
|
|
|
|
- placeholder="请输入回调地址"
|
|
|
|
|
- clearable
|
|
|
|
|
- />
|
|
|
|
|
|
|
+ <el-input v-model="configForm.return_url" placeholder="请输入回调地址" clearable />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
<el-form-item label="状态">
|
|
<el-form-item label="状态">
|
|
|
<el-tag :type="configForm.status === 'ENABLED' ? 'success' : 'info'">
|
|
<el-tag :type="configForm.status === 'ENABLED' ? 'success' : 'info'">
|
|
|
- {{ configForm.status === 'ENABLED' ? '启用' : '禁用' }}
|
|
|
|
|
|
|
+ {{ configForm.status === "ENABLED" ? "启用" : "禁用" }}
|
|
|
</el-tag>
|
|
</el-tag>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
<!-- <el-form-item label="描述">
|
|
<!-- <el-form-item label="描述">
|
|
@@ -367,11 +371,7 @@
|
|
|
/>
|
|
/>
|
|
|
</el-form-item> -->
|
|
</el-form-item> -->
|
|
|
<el-form-item>
|
|
<el-form-item>
|
|
|
- <el-button
|
|
|
|
|
- type="primary"
|
|
|
|
|
- :loading="configLoading"
|
|
|
|
|
- @click="handleSaveConfig"
|
|
|
|
|
- >
|
|
|
|
|
|
|
+ <el-button type="primary" :loading="configLoading" @click="handleSaveConfig">
|
|
|
保存配置
|
|
保存配置
|
|
|
</el-button>
|
|
</el-button>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
@@ -389,31 +389,71 @@
|
|
|
<div class="sidebar-section">
|
|
<div class="sidebar-section">
|
|
|
<h3>认证</h3>
|
|
<h3>认证</h3>
|
|
|
<ul>
|
|
<ul>
|
|
|
- <li @click="activeSection = 'auth'" :class="{ active: activeSection === 'auth' }">认证方式</li>
|
|
|
|
|
- <li @click="activeSection = 'signature'" :class="{ active: activeSection === 'signature' }">签名验证</li>
|
|
|
|
|
- <li @click="activeSection = 'notes'" :class="{ active: activeSection === 'notes' }">注意事项</li>
|
|
|
|
|
|
|
+ <li @click="activeSection = 'auth'" :class="{ active: activeSection === 'auth' }">
|
|
|
|
|
+ 认证方式
|
|
|
|
|
+ </li>
|
|
|
|
|
+ <li
|
|
|
|
|
+ @click="activeSection = 'signature'"
|
|
|
|
|
+ :class="{ active: activeSection === 'signature' }"
|
|
|
|
|
+ >
|
|
|
|
|
+ 签名验证
|
|
|
|
|
+ </li>
|
|
|
|
|
+ <li
|
|
|
|
|
+ @click="activeSection = 'notes'"
|
|
|
|
|
+ :class="{ active: activeSection === 'notes' }"
|
|
|
|
|
+ >
|
|
|
|
|
+ 注意事项
|
|
|
|
|
+ </li>
|
|
|
</ul>
|
|
</ul>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="sidebar-section">
|
|
<div class="sidebar-section">
|
|
|
<h3>账户接口</h3>
|
|
<h3>账户接口</h3>
|
|
|
<ul>
|
|
<ul>
|
|
|
- <li @click="activeSection = 'transfer'" :class="{ active: activeSection === 'transfer' }">发起转账</li>
|
|
|
|
|
- <li @click="activeSection = 'transfer_query'" :class="{ active: activeSection === 'transfer_query' }">查询转账</li>
|
|
|
|
|
- <li @click="activeSection = 'balance'" :class="{ active: activeSection === 'balance' }">账户余额</li>
|
|
|
|
|
|
|
+ <li
|
|
|
|
|
+ @click="activeSection = 'transfer'"
|
|
|
|
|
+ :class="{ active: activeSection === 'transfer' }"
|
|
|
|
|
+ >
|
|
|
|
|
+ 发起转账
|
|
|
|
|
+ </li>
|
|
|
|
|
+ <li
|
|
|
|
|
+ @click="activeSection = 'transfer_query'"
|
|
|
|
|
+ :class="{ active: activeSection === 'transfer_query' }"
|
|
|
|
|
+ >
|
|
|
|
|
+ 查询转账
|
|
|
|
|
+ </li>
|
|
|
|
|
+ <li
|
|
|
|
|
+ @click="activeSection = 'balance'"
|
|
|
|
|
+ :class="{ active: activeSection === 'balance' }"
|
|
|
|
|
+ >
|
|
|
|
|
+ 账户余额
|
|
|
|
|
+ </li>
|
|
|
</ul>
|
|
</ul>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="sidebar-section">
|
|
<div class="sidebar-section">
|
|
|
<h3>回调通知</h3>
|
|
<h3>回调通知</h3>
|
|
|
<ul>
|
|
<ul>
|
|
|
- <li @click="activeSection = 'callback'" :class="{ active: activeSection === 'callback' }">通知接口</li>
|
|
|
|
|
|
|
+ <li
|
|
|
|
|
+ @click="activeSection = 'callback'"
|
|
|
|
|
+ :class="{ active: activeSection === 'callback' }"
|
|
|
|
|
+ >
|
|
|
|
|
+ 通知接口
|
|
|
|
|
+ </li>
|
|
|
</ul>
|
|
</ul>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="sidebar-section">
|
|
<div class="sidebar-section">
|
|
|
<h3>其他</h3>
|
|
<h3>其他</h3>
|
|
|
<ul>
|
|
<ul>
|
|
|
- <li @click="activeSection = 'errors'" :class="{ active: activeSection === 'errors' }">常见错误</li>
|
|
|
|
|
- <li @click="activeSection = 'php'" :class="{ active: activeSection === 'php' }">PHP示例代码</li>
|
|
|
|
|
- </ul> identity_type
|
|
|
|
|
|
|
+ <li
|
|
|
|
|
+ @click="activeSection = 'errors'"
|
|
|
|
|
+ :class="{ active: activeSection === 'errors' }"
|
|
|
|
|
+ >
|
|
|
|
|
+ 常见错误
|
|
|
|
|
+ </li>
|
|
|
|
|
+ <li @click="activeSection = 'php'" :class="{ active: activeSection === 'php' }">
|
|
|
|
|
+ PHP示例代码
|
|
|
|
|
+ </li>
|
|
|
|
|
+ </ul>
|
|
|
|
|
+ identity_type
|
|
|
</div>
|
|
</div>
|
|
|
</el-scrollbar>
|
|
</el-scrollbar>
|
|
|
</div>
|
|
</div>
|
|
@@ -434,8 +474,15 @@
|
|
|
Signature: {signature}</code></pre>
|
|
Signature: {signature}</code></pre>
|
|
|
<p>其中:</p>
|
|
<p>其中:</p>
|
|
|
<ul>
|
|
<ul>
|
|
|
- <li><strong>Authorization</strong>:API Key认证头,格式为 <code>ApiKey {api_key}</code></li>
|
|
|
|
|
- <li><strong>Signature</strong>:请求签名(必填),用于验证请求数据的完整性</li>
|
|
|
|
|
|
|
+ <li>
|
|
|
|
|
+ <strong>Authorization</strong>
|
|
|
|
|
+ :API Key认证头,格式为
|
|
|
|
|
+ <code>ApiKey {api_key}</code>
|
|
|
|
|
+ </li>
|
|
|
|
|
+ <li>
|
|
|
|
|
+ <strong>Signature</strong>
|
|
|
|
|
+ :请求签名(必填),用于验证请求数据的完整性
|
|
|
|
|
+ </li>
|
|
|
</ul>
|
|
</ul>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
@@ -443,13 +490,36 @@ Signature: {signature}</code></pre>
|
|
|
<h2>2. 签名验证</h2>
|
|
<h2>2. 签名验证</h2>
|
|
|
<p>签名用于验证请求数据的完整性,防止数据被篡改。签名生成步骤:</p>
|
|
<p>签名用于验证请求数据的完整性,防止数据被篡改。签名生成步骤:</p>
|
|
|
<ol>
|
|
<ol>
|
|
|
- <li>过滤请求参数:排除 <code>sign</code> 参数、<code>null</code> 值、空字符串、空数组、空对象</li>
|
|
|
|
|
|
|
+ <li>
|
|
|
|
|
+ 过滤请求参数:排除
|
|
|
|
|
+ <code>sign</code>
|
|
|
|
|
+ 参数、
|
|
|
|
|
+ <code>null</code>
|
|
|
|
|
+ 值、空字符串、空数组、空对象
|
|
|
|
|
+ </li>
|
|
|
<li>将过滤后的参数按参数名ASCII码升序排序</li>
|
|
<li>将过滤后的参数按参数名ASCII码升序排序</li>
|
|
|
- <li>对字典或列表类型的值进行JSON序列化(<code>sort_keys=true</code>,<code>separators=(',', ':')</code>)</li>
|
|
|
|
|
- <li>对每个参数值进行URL编码(<code>UTF-8</code>编码)</li>
|
|
|
|
|
- <li>将排序后的参数拼接为字符串:<code>key1=value1&key2=value2</code></li>
|
|
|
|
|
|
|
+ <li>
|
|
|
|
|
+ 对字典或列表类型的值进行JSON序列化(
|
|
|
|
|
+ <code>sort_keys=true</code>
|
|
|
|
|
+ ,
|
|
|
|
|
+ <code>separators=(',', ':')</code>
|
|
|
|
|
+ )
|
|
|
|
|
+ </li>
|
|
|
|
|
+ <li>
|
|
|
|
|
+ 对每个参数值进行URL编码(
|
|
|
|
|
+ <code>UTF-8</code>
|
|
|
|
|
+ 编码)
|
|
|
|
|
+ </li>
|
|
|
|
|
+ <li>
|
|
|
|
|
+ 将排序后的参数拼接为字符串:
|
|
|
|
|
+ <code>key1=value1&key2=value2</code>
|
|
|
|
|
+ </li>
|
|
|
<li>使用API Secret作为密钥,通过HMAC-SHA256算法生成签名</li>
|
|
<li>使用API Secret作为密钥,通过HMAC-SHA256算法生成签名</li>
|
|
|
- <li>将签名添加到请求头 <code>Signature</code> 中</li>
|
|
|
|
|
|
|
+ <li>
|
|
|
|
|
+ 将签名添加到请求头
|
|
|
|
|
+ <code>Signature</code>
|
|
|
|
|
+ 中
|
|
|
|
|
+ </li>
|
|
|
</ol>
|
|
</ol>
|
|
|
<h3>2.1 签名计算示例</h3>
|
|
<h3>2.1 签名计算示例</h3>
|
|
|
<pre><code># 原始请求数据
|
|
<pre><code># 原始请求数据
|
|
@@ -500,10 +570,24 @@ Signature: {signature}</code></pre>
|
|
|
<h2>3. 注意事项</h2>
|
|
<h2>3. 注意事项</h2>
|
|
|
<ul>
|
|
<ul>
|
|
|
<li>API Key和Secret请妥善保管,不要泄露给他人</li>
|
|
<li>API Key和Secret请妥善保管,不要泄露给他人</li>
|
|
|
- <li>签名验证是<strong>必填</strong>的,未带签名或签名错误将返回401</li>
|
|
|
|
|
|
|
+ <li>
|
|
|
|
|
+ 签名验证是
|
|
|
|
|
+ <strong>必填</strong>
|
|
|
|
|
+ 的,未带签名或签名错误将返回401
|
|
|
|
|
+ </li>
|
|
|
<li>签名使用HMAC-SHA256算法,密钥为API Secret</li>
|
|
<li>签名使用HMAC-SHA256算法,密钥为API Secret</li>
|
|
|
- <li>签名计算前会自动过滤:<code>sign</code>参数、<code>null</code>值、空字符串、空数组、空对象</li>
|
|
|
|
|
- <li>嵌套对象(如<code>payee_info</code>)会先进行JSON序列化再参与签名</li>
|
|
|
|
|
|
|
+ <li>
|
|
|
|
|
+ 签名计算前会自动过滤:
|
|
|
|
|
+ <code>sign</code>
|
|
|
|
|
+ 参数、
|
|
|
|
|
+ <code>null</code>
|
|
|
|
|
+ 值、空字符串、空数组、空对象
|
|
|
|
|
+ </li>
|
|
|
|
|
+ <li>
|
|
|
|
|
+ 嵌套对象(如
|
|
|
|
|
+ <code>payee_info</code>
|
|
|
|
|
+ )会先进行JSON序列化再参与签名
|
|
|
|
|
+ </li>
|
|
|
<li>参数值会进行URL编码(UTF-8),确保中文字符正确处理</li>
|
|
<li>参数值会进行URL编码(UTF-8),确保中文字符正确处理</li>
|
|
|
<li>定期更新API Key,建议每3-6个月更换一次</li>
|
|
<li>定期更新API Key,建议每3-6个月更换一次</li>
|
|
|
<li>如发现API Key泄露,请立即禁用并重新生成</li>
|
|
<li>如发现API Key泄露,请立即禁用并重新生成</li>
|
|
@@ -567,7 +651,7 @@ Signature: {signature}</code></pre>
|
|
|
<td>
|
|
<td>
|
|
|
<div class="expandable-section">
|
|
<div class="expandable-section">
|
|
|
<span @click="toggleExpand('payee_info')" class="expand-btn">
|
|
<span @click="toggleExpand('payee_info')" class="expand-btn">
|
|
|
- {{ expandedSections.payee_info ? '▼' : '▶' }} 收款方信息
|
|
|
|
|
|
|
+ {{ expandedSections.payee_info ? "▼" : "▶" }} 收款方信息
|
|
|
</span>
|
|
</span>
|
|
|
<div v-if="expandedSections.payee_info" class="expandable-content">
|
|
<div v-if="expandedSections.payee_info" class="expandable-content">
|
|
|
<table class="api-table nested-table">
|
|
<table class="api-table nested-table">
|
|
@@ -604,10 +688,17 @@ Signature: {signature}</code></pre>
|
|
|
<td>否</td>
|
|
<td>否</td>
|
|
|
<td>
|
|
<td>
|
|
|
<div class="expandable-section">
|
|
<div class="expandable-section">
|
|
|
- <span @click="toggleExpand('bankcard_ext_info')" class="expand-btn">
|
|
|
|
|
- {{ expandedSections.bankcard_ext_info ? '▼' : '▶' }} 银行卡信息(当 identity_type 为 bank 时必填)
|
|
|
|
|
|
|
+ <span
|
|
|
|
|
+ @click="toggleExpand('bankcard_ext_info')"
|
|
|
|
|
+ class="expand-btn"
|
|
|
|
|
+ >
|
|
|
|
|
+ {{ expandedSections.bankcard_ext_info ? "▼" : "▶" }}
|
|
|
|
|
+ 银行卡信息(当 identity_type 为 bank 时必填)
|
|
|
</span>
|
|
</span>
|
|
|
- <div v-if="expandedSections.bankcard_ext_info" class="expandable-content">
|
|
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-if="expandedSections.bankcard_ext_info"
|
|
|
|
|
+ class="expandable-content"
|
|
|
|
|
+ >
|
|
|
<table class="api-table nested-table">
|
|
<table class="api-table nested-table">
|
|
|
<thead>
|
|
<thead>
|
|
|
<tr>
|
|
<tr>
|
|
@@ -696,7 +787,9 @@ Signature: {signature}</code></pre>
|
|
|
<p>根据三方订单号查询转账状态和详情</p>
|
|
<p>根据三方订单号查询转账状态和详情</p>
|
|
|
|
|
|
|
|
<h4>API接口地址</h4>
|
|
<h4>API接口地址</h4>
|
|
|
- <p><code>POST https://api.qcsj88888.com/payment/openapi/account/transfer/query</code></p>
|
|
|
|
|
|
|
+ <p>
|
|
|
|
|
+ <code>POST https://api.qcsj88888.com/payment/openapi/account/transfer/query</code>
|
|
|
|
|
+ </p>
|
|
|
|
|
|
|
|
<h4>请求参数</h4>
|
|
<h4>请求参数</h4>
|
|
|
<table class="api-table">
|
|
<table class="api-table">
|
|
@@ -780,7 +873,9 @@ Signature: {signature}</code></pre>
|
|
|
<p>查询指定企业资金专户的余额信息</p>
|
|
<p>查询指定企业资金专户的余额信息</p>
|
|
|
|
|
|
|
|
<h4>API接口地址</h4>
|
|
<h4>API接口地址</h4>
|
|
|
- <p><code>POST https://api.qcsj88888.com/payment/openapi/account/balance/query</code></p>
|
|
|
|
|
|
|
+ <p>
|
|
|
|
|
+ <code>POST https://api.qcsj88888.com/payment/openapi/account/balance/query</code>
|
|
|
|
|
+ </p>
|
|
|
|
|
|
|
|
<h4>请求参数</h4>
|
|
<h4>请求参数</h4>
|
|
|
<table class="api-table">
|
|
<table class="api-table">
|
|
@@ -869,9 +964,23 @@ Signature: {signature}</code></pre>
|
|
|
|
|
|
|
|
<h4>注意事项</h4>
|
|
<h4>注意事项</h4>
|
|
|
<ul>
|
|
<ul>
|
|
|
- <li>返回结果为<strong>数组</strong>,一个企业可能有多个资金专户</li>
|
|
|
|
|
- <li>余额单位为<strong>元</strong>,精确到小数点后两位</li>
|
|
|
|
|
- <li>仅返回 <code>scene</code> 为 <code>B2B_TRANS</code> 的资金专户</li>
|
|
|
|
|
|
|
+ <li>
|
|
|
|
|
+ 返回结果为
|
|
|
|
|
+ <strong>数组</strong>
|
|
|
|
|
+ ,一个企业可能有多个资金专户
|
|
|
|
|
+ </li>
|
|
|
|
|
+ <li>
|
|
|
|
|
+ 余额单位为
|
|
|
|
|
+ <strong>元</strong>
|
|
|
|
|
+ ,精确到小数点后两位
|
|
|
|
|
+ </li>
|
|
|
|
|
+ <li>
|
|
|
|
|
+ 仅返回
|
|
|
|
|
+ <code>scene</code>
|
|
|
|
|
+ 为
|
|
|
|
|
+ <code>B2B_TRANS</code>
|
|
|
|
|
+ 的资金专户
|
|
|
|
|
+ </li>
|
|
|
</ul>
|
|
</ul>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
@@ -882,17 +991,36 @@ Signature: {signature}</code></pre>
|
|
|
|
|
|
|
|
<h4>回调地址配置</h4>
|
|
<h4>回调地址配置</h4>
|
|
|
<p>系统按照以下优先级获取回调地址:</p>
|
|
<p>系统按照以下优先级获取回调地址:</p>
|
|
|
- <ol style="margin-left: 20px;">
|
|
|
|
|
- <li><strong>API Key 级别</strong>:在创建/编辑 API Key 时配置回调地址(优先级最高)</li>
|
|
|
|
|
- <li><strong>开放平台配置</strong>:在 <strong>应用配置</strong> 页面设置默认回调地址</li>
|
|
|
|
|
|
|
+ <ol style="margin-left: 20px">
|
|
|
|
|
+ <li>
|
|
|
|
|
+ <strong>API Key 级别</strong>
|
|
|
|
|
+ :在创建/编辑 API Key 时配置回调地址(优先级最高)
|
|
|
|
|
+ </li>
|
|
|
|
|
+ <li>
|
|
|
|
|
+ <strong>开放平台配置</strong>
|
|
|
|
|
+ :在
|
|
|
|
|
+ <strong>应用配置</strong>
|
|
|
|
|
+ 页面设置默认回调地址
|
|
|
|
|
+ </li>
|
|
|
</ol>
|
|
</ol>
|
|
|
- <p style="color: #909399; margin-top: 8px;">说明:如果 API Key 已配置回调地址,则优先使用;否则使用开放平台配置中的回调地址。</p>
|
|
|
|
|
|
|
+ <p style="color: #909399; margin-top: 8px">
|
|
|
|
|
+ 说明:如果 API Key 已配置回调地址,则优先使用;否则使用开放平台配置中的回调地址。
|
|
|
|
|
+ </p>
|
|
|
|
|
|
|
|
<h4>通知方式</h4>
|
|
<h4>通知方式</h4>
|
|
|
<ul>
|
|
<ul>
|
|
|
- <li><strong>POST 请求</strong>:系统通过 HTTP POST 方式将通知数据发送到商户的回调地址</li>
|
|
|
|
|
- <li><strong>表单形式</strong>:通知参数以表单形式提交(Content-Type: multipart/form-data)</li>
|
|
|
|
|
- <li><strong>重试机制</strong>:如果通知失败,系统会自动重试(最多2次,间隔1秒、2秒)</li>
|
|
|
|
|
|
|
+ <li>
|
|
|
|
|
+ <strong>POST 请求</strong>
|
|
|
|
|
+ :系统通过 HTTP POST 方式将通知数据发送到商户的回调地址
|
|
|
|
|
+ </li>
|
|
|
|
|
+ <li>
|
|
|
|
|
+ <strong>表单形式</strong>
|
|
|
|
|
+ :通知参数以表单形式提交(Content-Type: multipart/form-data)
|
|
|
|
|
+ </li>
|
|
|
|
|
+ <li>
|
|
|
|
|
+ <strong>重试机制</strong>
|
|
|
|
|
+ :如果通知失败,系统会自动重试(最多2次,间隔1秒、2秒)
|
|
|
|
|
+ </li>
|
|
|
</ul>
|
|
</ul>
|
|
|
|
|
|
|
|
<h4>请求参数</h4>
|
|
<h4>请求参数</h4>
|
|
@@ -936,7 +1064,9 @@ Signature: {signature}</code></pre>
|
|
|
<tr>
|
|
<tr>
|
|
|
<td>status</td>
|
|
<td>status</td>
|
|
|
<td>string</td>
|
|
<td>string</td>
|
|
|
- <td>转账状态:DEALING(处理中)、SUCCESS(成功)、FAIL(失败)、REFUND(已退款)</td>
|
|
|
|
|
|
|
+ <td>
|
|
|
|
|
+ 转账状态:DEALING(处理中)、SUCCESS(成功)、FAIL(失败)、REFUND(已退款)
|
|
|
|
|
+ </td>
|
|
|
</tr>
|
|
</tr>
|
|
|
<tr>
|
|
<tr>
|
|
|
<td>order_no</td>
|
|
<td>order_no</td>
|
|
@@ -967,7 +1097,8 @@ Signature: {signature}</code></pre>
|
|
|
</table>
|
|
</table>
|
|
|
|
|
|
|
|
<h4>通知示例</h4>
|
|
<h4>通知示例</h4>
|
|
|
- <pre><code>POST /your/callback/url HTTP/1.1
|
|
|
|
|
|
|
+ 成功示例
|
|
|
|
|
+ <pre v-pre><code>POST /your/callback/url HTTP/1.1
|
|
|
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
|
|
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
|
|
|
|
|
|
|
|
------WebKitFormBoundary
|
|
------WebKitFormBoundary
|
|
@@ -989,30 +1120,95 @@ Content-Disposition: form-data; name="content"
|
|
|
"created_time": "2026-04-27 11:22:33",
|
|
"created_time": "2026-04-27 11:22:33",
|
|
|
"updated_time": "2026-04-27 11:25:45"
|
|
"updated_time": "2026-04-27 11:25:45"
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+------WebKitFormBoundary--</code></pre>
|
|
|
|
|
+ 失败示例
|
|
|
|
|
+ <pre><code>POST /your/callback/url HTTP/1.1
|
|
|
|
|
+Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
|
|
|
|
|
+
|
|
|
|
|
+------WebKitFormBoundary
|
|
|
|
|
+Content-Disposition: form-data; name="notify_id"
|
|
|
|
|
+
|
|
|
|
|
+n12535554089713704963
|
|
|
|
|
+------WebKitFormBoundary
|
|
|
|
|
+Content-Disposition: form-data; name="timestamp"
|
|
|
|
|
+
|
|
|
|
|
+1779037365774
|
|
|
|
|
+------WebKitFormBoundary
|
|
|
|
|
+Content-Disposition: form-data; name="content"
|
|
|
|
|
+
|
|
|
|
|
+{
|
|
|
|
|
+ "status": "FAIL",
|
|
|
|
|
+ "third_biz_no": "商户订单号202604270001",
|
|
|
|
|
+ "amount": "1.00",
|
|
|
|
|
+ "error_msg": "收款账号不存在或姓名有误,建议核实账号和姓名是否准确"
|
|
|
|
|
+}
|
|
|
|
|
+------WebKitFormBoundary--
|
|
|
------WebKitFormBoundary--</code></pre>
|
|
------WebKitFormBoundary--</code></pre>
|
|
|
|
|
|
|
|
<h4>响应要求</h4>
|
|
<h4>响应要求</h4>
|
|
|
- <p>商户服务端收到通知后,需要返回 HTTP 200 状态码表示成功接收。如果返回非 200 状态码或超时,系统会进行重试。</p>
|
|
|
|
|
|
|
+ <p>
|
|
|
|
|
+ 商户服务端收到通知后,需要返回 HTTP 200 状态码表示成功接收。如果返回非 200
|
|
|
|
|
+ 状态码或超时,系统会进行重试。
|
|
|
|
|
+ </p>
|
|
|
|
|
|
|
|
<h4>注意事项</h4>
|
|
<h4>注意事项</h4>
|
|
|
<ul>
|
|
<ul>
|
|
|
- <li>回调地址需要支持 <strong>HTTPS</strong> 协议</li>
|
|
|
|
|
- <li>确保回调接口能够在 <strong>5秒内</strong> 返回响应</li>
|
|
|
|
|
- <li><span style="color: #f56c6c;"><strong>重要</strong>:回调通知不包含签名验证,收到通知后请主动调用查询接口确认订单状态,以确保数据真实性</span></li>
|
|
|
|
|
- <li>通知可能会重复发送,请确保业务逻辑支持 <strong>幂等性</strong></li>
|
|
|
|
|
- <li>系统最多重试 <strong>2次</strong>,重试间隔为 1 秒和 2 秒</li>
|
|
|
|
|
|
|
+ <li>
|
|
|
|
|
+ 回调地址需要支持
|
|
|
|
|
+ <strong>HTTPS</strong>
|
|
|
|
|
+ 协议
|
|
|
|
|
+ </li>
|
|
|
|
|
+ <li>
|
|
|
|
|
+ 确保回调接口能够在
|
|
|
|
|
+ <strong>5秒内</strong>
|
|
|
|
|
+ 返回响应
|
|
|
|
|
+ </li>
|
|
|
|
|
+ <li>
|
|
|
|
|
+ <span style="color: #f56c6c">
|
|
|
|
|
+ <strong>重要</strong>
|
|
|
|
|
+ :回调通知不包含签名验证,收到通知后请主动调用查询接口确认订单状态,以确保数据真实性
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </li>
|
|
|
|
|
+ <li>
|
|
|
|
|
+ 通知可能会重复发送,请确保业务逻辑支持
|
|
|
|
|
+ <strong>幂等性</strong>
|
|
|
|
|
+ </li>
|
|
|
|
|
+ <li>
|
|
|
|
|
+ 系统最多重试
|
|
|
|
|
+ <strong>2次</strong>
|
|
|
|
|
+ ,重试间隔为 1 秒和 2 秒
|
|
|
|
|
+ </li>
|
|
|
</ul>
|
|
</ul>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div v-else-if="activeSection === 'errors'" class="section-content">
|
|
<div v-else-if="activeSection === 'errors'" class="section-content">
|
|
|
<h2>8. 常见错误</h2>
|
|
<h2>8. 常见错误</h2>
|
|
|
<ul>
|
|
<ul>
|
|
|
- <li><strong>401 Invalid API Key</strong>:API Key无效或已过期</li>
|
|
|
|
|
- <li><strong>401 Signature header required</strong>:未提供Signature请求头</li>
|
|
|
|
|
- <li><strong>401 Invalid Signature</strong>:签名验证失败,请检查签名计算方式</li>
|
|
|
|
|
- <li><strong>400 Bad Request</strong>:请求参数错误</li>
|
|
|
|
|
- <li><strong>403 Forbidden</strong>:无权限访问</li>
|
|
|
|
|
- <li><strong>500 Internal Server Error</strong>:服务器内部错误</li>
|
|
|
|
|
|
|
+ <li>
|
|
|
|
|
+ <strong>401 Invalid API Key</strong>
|
|
|
|
|
+ :API Key无效或已过期
|
|
|
|
|
+ </li>
|
|
|
|
|
+ <li>
|
|
|
|
|
+ <strong>401 Signature header required</strong>
|
|
|
|
|
+ :未提供Signature请求头
|
|
|
|
|
+ </li>
|
|
|
|
|
+ <li>
|
|
|
|
|
+ <strong>401 Invalid Signature</strong>
|
|
|
|
|
+ :签名验证失败,请检查签名计算方式
|
|
|
|
|
+ </li>
|
|
|
|
|
+ <li>
|
|
|
|
|
+ <strong>400 Bad Request</strong>
|
|
|
|
|
+ :请求参数错误
|
|
|
|
|
+ </li>
|
|
|
|
|
+ <li>
|
|
|
|
|
+ <strong>403 Forbidden</strong>
|
|
|
|
|
+ :无权限访问
|
|
|
|
|
+ </li>
|
|
|
|
|
+ <li>
|
|
|
|
|
+ <strong>500 Internal Server Error</strong>
|
|
|
|
|
+ :服务器内部错误
|
|
|
|
|
+ </li>
|
|
|
</ul>
|
|
</ul>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
@@ -1141,7 +1337,7 @@ const configFormRef = ref();
|
|
|
const configLoading = ref(true);
|
|
const configLoading = ref(true);
|
|
|
const expandedSections = ref({
|
|
const expandedSections = ref({
|
|
|
payee_info: false,
|
|
payee_info: false,
|
|
|
- bankcard_ext_info: false
|
|
|
|
|
|
|
+ bankcard_ext_info: false,
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
const configExists = ref(false);
|
|
const configExists = ref(false);
|
|
@@ -1234,7 +1430,7 @@ function getSectionTitle() {
|
|
|
balance: "账户余额",
|
|
balance: "账户余额",
|
|
|
callback: "回调通知",
|
|
callback: "回调通知",
|
|
|
errors: "常见错误",
|
|
errors: "常见错误",
|
|
|
- php: "PHP示例代码"
|
|
|
|
|
|
|
+ php: "PHP示例代码",
|
|
|
};
|
|
};
|
|
|
return titles[activeSection.value] || "API文档";
|
|
return titles[activeSection.value] || "API文档";
|
|
|
}
|
|
}
|
|
@@ -1396,44 +1592,47 @@ async function handleSubmit() {
|
|
|
async function handleUpdateStatus(id: number, status: string) {
|
|
async function handleUpdateStatus(id: number, status: string) {
|
|
|
try {
|
|
try {
|
|
|
await ApiKeyAPI.updateApiKeyStatus(id, { status });
|
|
await ApiKeyAPI.updateApiKeyStatus(id, { status });
|
|
|
- ElMessage.success(`API Key已${status === '0' ? '启用' : '禁用'}`);
|
|
|
|
|
|
|
+ ElMessage.success(`API Key已${status === "0" ? "启用" : "禁用"}`);
|
|
|
refreshList();
|
|
refreshList();
|
|
|
} catch (error: unknown) {
|
|
} catch (error: unknown) {
|
|
|
console.error(error);
|
|
console.error(error);
|
|
|
- ElMessage.error('操作失败');
|
|
|
|
|
|
|
+ ElMessage.error("操作失败");
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function copyToClipboard(text: string) {
|
|
function copyToClipboard(text: string) {
|
|
|
- navigator.clipboard.writeText(text).then(() => {
|
|
|
|
|
- ElMessage.success('复制成功');
|
|
|
|
|
- }).catch(() => {
|
|
|
|
|
- ElMessage.error('复制失败');
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ navigator.clipboard
|
|
|
|
|
+ .writeText(text)
|
|
|
|
|
+ .then(() => {
|
|
|
|
|
+ ElMessage.success("复制成功");
|
|
|
|
|
+ })
|
|
|
|
|
+ .catch(() => {
|
|
|
|
|
+ ElMessage.error("复制失败");
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function handleCloseApiKeyDetail() {
|
|
function handleCloseApiKeyDetail() {
|
|
|
- ElMessage.warning('请务必已保存API Key和Secret,关闭后将无法再次查看');
|
|
|
|
|
|
|
+ ElMessage.warning("请务必已保存API Key和Secret,关闭后将无法再次查看");
|
|
|
apiKeyDetailVisible.value = false;
|
|
apiKeyDetailVisible.value = false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function handleConfirmApiKeyDetail() {
|
|
function handleConfirmApiKeyDetail() {
|
|
|
- ElMessage.success('已确认保存');
|
|
|
|
|
|
|
+ ElMessage.success("已确认保存");
|
|
|
apiKeyDetailVisible.value = false;
|
|
apiKeyDetailVisible.value = false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function downloadApiKeyCsv() {
|
|
function downloadApiKeyCsv() {
|
|
|
- const csvContent = `API Key,API Secret,状态,过期时间,创建时间,描述\n"${apiKeyDetail.api_key}","${apiKeyDetail.api_secret}","${apiKeyDetail.status === '0' ? '正常' : '禁用'}","${apiKeyDetail.expired_at}","${apiKeyDetail.created_time}","${apiKeyDetail.description || ''}"`;
|
|
|
|
|
- const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
|
|
|
|
- const link = document.createElement('a');
|
|
|
|
|
|
|
+ const csvContent = `API Key,API Secret,状态,过期时间,创建时间,描述\n"${apiKeyDetail.api_key}","${apiKeyDetail.api_secret}","${apiKeyDetail.status === "0" ? "正常" : "禁用"}","${apiKeyDetail.expired_at}","${apiKeyDetail.created_time}","${apiKeyDetail.description || ""}"`;
|
|
|
|
|
+ const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
|
|
|
|
|
+ const link = document.createElement("a");
|
|
|
const url = URL.createObjectURL(blob);
|
|
const url = URL.createObjectURL(blob);
|
|
|
- link.setAttribute('href', url);
|
|
|
|
|
- link.setAttribute('download', `api-key-${apiKeyDetail.api_key || Date.now()}.csv`);
|
|
|
|
|
- link.style.visibility = 'hidden';
|
|
|
|
|
|
|
+ link.setAttribute("href", url);
|
|
|
|
|
+ link.setAttribute("download", `api-key-${apiKeyDetail.api_key || Date.now()}.csv`);
|
|
|
|
|
+ link.style.visibility = "hidden";
|
|
|
document.body.appendChild(link);
|
|
document.body.appendChild(link);
|
|
|
link.click();
|
|
link.click();
|
|
|
document.body.removeChild(link);
|
|
document.body.removeChild(link);
|
|
|
- ElMessage.success('CSV已下载');
|
|
|
|
|
|
|
+ ElMessage.success("CSV已下载");
|
|
|
}
|
|
}
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
@@ -1621,7 +1820,7 @@ function downloadApiKeyCsv() {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.section-content code {
|
|
.section-content code {
|
|
|
- font-family: 'Courier New', Courier, monospace;
|
|
|
|
|
|
|
+ font-family: "Courier New", Courier, monospace;
|
|
|
font-size: 14px;
|
|
font-size: 14px;
|
|
|
color: #303133;
|
|
color: #303133;
|
|
|
}
|
|
}
|