|
|
@@ -46,16 +46,6 @@
|
|
|
min-width="55"
|
|
|
align="center"
|
|
|
/>
|
|
|
- <el-table-column
|
|
|
- v-if="contentCols.find((col) => col.prop === 'index')?.show"
|
|
|
- fixed
|
|
|
- label="序号"
|
|
|
- min-width="60"
|
|
|
- >
|
|
|
- <template #default="scope">
|
|
|
- {{ (pagination.currentPage - 1) * pagination.pageSize + scope.$index + 1 }}
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
<el-table-column
|
|
|
v-if="contentCols.find((col) => col.prop === 'api_key')?.show"
|
|
|
key="api_key"
|
|
|
@@ -233,7 +223,7 @@
|
|
|
<el-descriptions-item label="API Secret">
|
|
|
<div class="api-key-detail">
|
|
|
<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>
|
|
|
</div>
|
|
|
@@ -275,51 +265,39 @@
|
|
|
<div class="docs-content">
|
|
|
<h2>1. 认证方式</h2>
|
|
|
<p>使用API Key进行认证时,需要在请求头中添加以下信息:</p>
|
|
|
- <pre><code>Authorization: ApiKey {api_key}:{signature}</code></pre>
|
|
|
+ <pre><code>Authorization: ApiKey {api_key}
|
|
|
+Signature: {signature}</code></pre>
|
|
|
<p>其中:</p>
|
|
|
<ul>
|
|
|
- <li><strong>api_key</strong>:从管理界面获取的API Key</li>
|
|
|
- <li><strong>signature</strong>:可选,请求签名(详见签名验证部分)</li>
|
|
|
+ <li><strong>Authorization</strong>:API Key认证头,格式为 <code>ApiKey {api_key}</code></li>
|
|
|
+ <li><strong>Signature</strong>:请求签名(必填),用于验证请求数据的完整性</li>
|
|
|
</ul>
|
|
|
|
|
|
<h2>2. 签名验证</h2>
|
|
|
- <p>为了增强安全性,建议在请求中添加签名。签名生成步骤:</p>
|
|
|
+ <p>签名用于验证请求数据的完整性,防止数据被篡改。签名生成步骤:</p>
|
|
|
<ol>
|
|
|
<li>将请求数据(JSON格式)按参数名升序排序</li>
|
|
|
- <li>将排序后的参数拼接为字符串:<code>key1=value1&key2=value2</code></li>
|
|
|
+ <li>将排序后的参数拼接为字符串:<code>key1=value1&key2=value2</code></li>
|
|
|
<li>使用API Secret作为密钥,通过HMAC-SHA256算法生成签名</li>
|
|
|
- <li>将签名添加到Authorization头中</li>
|
|
|
+ <li>将签名添加到请求头 <code>Signature</code> 中</li>
|
|
|
</ol>
|
|
|
|
|
|
<h2>3. 注意事项</h2>
|
|
|
<ul>
|
|
|
<li>API Key和Secret请妥善保管,不要泄露给他人</li>
|
|
|
+ <li>签名验证是<strong>必填</strong>的,未带签名或签名错误将返回401</li>
|
|
|
+ <li>签名使用HMAC-SHA256算法,密钥为API Secret</li>
|
|
|
+ <li>签名对象是请求体字典排序后的键值对字符串(key1=value1&key2=value2)</li>
|
|
|
<li>定期更新API Key,建议每3-6个月更换一次</li>
|
|
|
<li>如发现API Key泄露,请立即禁用并重新生成</li>
|
|
|
- <li>签名验证可选,但建议在生产环境中使用</li>
|
|
|
<li>API Key有过期时间,请在过期前及时更新</li>
|
|
|
</ul>
|
|
|
|
|
|
<h2>4. cURL示例</h2>
|
|
|
<h3>4.1 租户转账接口</h3>
|
|
|
- <pre><code># 基础认证(不带签名)
|
|
|
-curl -X POST 'https://api.example.com/payment/openapi/account/transfer' \
|
|
|
+ <pre><code>curl -X POST 'https://api.example.com/payment/openapi/account/transfer' \
|
|
|
-H 'Authorization: ApiKey your_api_key' \
|
|
|
- -H 'Content-Type: application/json' \
|
|
|
- -d '{
|
|
|
- "account_book_id": "资金账号",
|
|
|
- "amount": 100.00,
|
|
|
- "order_title": "转账标题",
|
|
|
- "payee_info": {
|
|
|
- "identity_type": "ALIPAY_ACCOUNT",
|
|
|
- "name": "收款人姓名",
|
|
|
- "identity": "收款人支付宝账号"
|
|
|
- }
|
|
|
- }'
|
|
|
-
|
|
|
-# 带签名认证
|
|
|
-curl -X POST 'https://api.example.com/payment/openapi/account/transfer' \
|
|
|
- -H 'Authorization: ApiKey your_api_key:your_signature' \
|
|
|
+ -H 'Signature: your_signature' \
|
|
|
-H 'Content-Type: application/json' \
|
|
|
-d '{
|
|
|
"account_book_id": "资金账号",
|
|
|
@@ -339,14 +317,14 @@ import hashlib
|
|
|
import hmac
|
|
|
|
|
|
def calculate_signature(api_secret, request_data):
|
|
|
- \"\"\"
|
|
|
+ """
|
|
|
对请求体字典进行签名
|
|
|
request_data: dict 请求体数据
|
|
|
- \"\"\"
|
|
|
+ """
|
|
|
# 按参数名升序排序
|
|
|
sorted_data = sorted(request_data.items(), key=lambda x: x[0])
|
|
|
- # 拼接为 key1=value1&key2=value2 格式
|
|
|
- sign_str = "&".join([f"{k}={v}" for k, v in sorted_data])
|
|
|
+ # 拼接为 key1=value1&key2=value2 格式
|
|
|
+ sign_str = "&".join([f"{k}={v}" for k, v in sorted_data])
|
|
|
# HMAC-SHA256签名
|
|
|
signature = hmac.new(
|
|
|
api_secret.encode('utf-8'),
|
|
|
@@ -362,13 +340,15 @@ request_data = {
|
|
|
"amount": 100.00
|
|
|
}
|
|
|
signature = calculate_signature(api_secret, request_data)
|
|
|
-# Authorization: ApiKey your_api_key:signature</code></pre>
|
|
|
+# 发送请求时设置请求头:
|
|
|
+# Authorization: ApiKey your_api_key
|
|
|
+# Signature: signature</code></pre>
|
|
|
|
|
|
- <h3>4.3 注意事项</h3>
|
|
|
+ <h3>4.3 常见错误</h3>
|
|
|
<ul>
|
|
|
- <li>签名使用HMAC-SHA256算法,密钥为API Secret</li>
|
|
|
- <li>签名对象是请求体字典排序后的键值对字符串(key1=value1&key2=value2)</li>
|
|
|
- <li>注意是字典排序后拼接,不是JSON字符串</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>
|
|
|
</ul>
|
|
|
</div>
|
|
|
</el-card>
|
|
|
@@ -382,10 +362,8 @@ signature = calculate_signature(api_secret, request_data)
|
|
|
import { ref, reactive } from "vue";
|
|
|
import ApiKeyAPI, {
|
|
|
ApiKeyCreateForm,
|
|
|
- ApiKeyUpdateForm,
|
|
|
ApiKeyPageQuery,
|
|
|
ApiKeyResponse,
|
|
|
- ApiKeyTable,
|
|
|
} from "@/api/module_payment/apikey";
|
|
|
import CrudToolbarLeft from "@/components/CURD/CrudToolbarLeft.vue";
|
|
|
import CrudToolbarRight from "@/components/CURD/CrudToolbarRight.vue";
|
|
|
@@ -423,12 +401,6 @@ const searchConfig = reactive<ISearchConfig>({
|
|
|
showNumber: 2,
|
|
|
form: { labelWidth: "auto" },
|
|
|
formItems: [
|
|
|
- // {
|
|
|
- // prop: "tenant_id",
|
|
|
- // label: "租户ID",
|
|
|
- // type: "input",
|
|
|
- // attrs: { placeholder: "请输入租户ID", clearable: true },
|
|
|
- // },
|
|
|
{
|
|
|
prop: "status",
|
|
|
label: "状态",
|
|
|
@@ -454,7 +426,7 @@ const contentCols = reactive<
|
|
|
}>
|
|
|
>([
|
|
|
{ prop: "selection", label: "选择框", show: true },
|
|
|
- { prop: "index", label: "序号", show: true },
|
|
|
+ { prop: "id", label: "ID", show: false },
|
|
|
{ prop: "api_key", label: "API Key", show: true },
|
|
|
{ prop: "status", label: "状态", show: true },
|
|
|
{ prop: "expired_at", label: "过期时间", show: true },
|
|
|
@@ -521,7 +493,7 @@ const initialFormData: ApiKeyCreateForm = {
|
|
|
description: "",
|
|
|
};
|
|
|
|
|
|
-function handleRowDelete(id: number) {
|
|
|
+async function handleRowDelete(id: number) {
|
|
|
contentRef.value?.handleDelete(id);
|
|
|
}
|
|
|
|
|
|
@@ -599,7 +571,7 @@ function downloadApiKeyCsv() {
|
|
|
const link = document.createElement('a');
|
|
|
const url = URL.createObjectURL(blob);
|
|
|
link.setAttribute('href', url);
|
|
|
- link.setAttribute('download', `api-key-${apiKeyDetail.id || Date.now()}.csv`);
|
|
|
+ link.setAttribute('download', `api-key-${apiKeyDetail.api_key || Date.now()}.csv`);
|
|
|
link.style.visibility = 'hidden';
|
|
|
document.body.appendChild(link);
|
|
|
link.click();
|