Jelajahi Sumber

feat:新增对外接口 资金商户余额查询

alphah 2 minggu lalu
induk
melakukan
e8862a1297

+ 19 - 2
backend/app/plugin/module_payment/openapi/controller.py

@@ -9,10 +9,11 @@ from app.common.response import ResponseSchema, SuccessResponse, ErrorResponse
 from app.core.apikey import TenantApiKeyAuth
 from app.api.v1.module_system.auth.schema import AuthSchema
 
+from app.plugin.module_payment.account.service import AccountService
 from app.plugin.module_payment.apikey.schema import ApiKeyPayload
 from app.plugin.module_payment.openapi.service import OpenConfService, OpenTransferService
-from app.plugin.module_payment.openapi.schema import OpenConfOutSchema, OpenConfUpdateSchema, OpenTransferSchema, \
-    OpenTransferOutSchema, OpenTransferQuerySchema
+from app.plugin.module_payment.openapi.schema import OpenBalanceQuerySchema, OpenConfOutSchema, OpenConfUpdateSchema, \
+    OpenTransferSchema, OpenTransferOutSchema, OpenTransferQuerySchema
 
 OpenapiRouter = APIRouter(
     route_class=OperationLogRoute,
@@ -98,3 +99,19 @@ async def save_open_conf_controller(
     """
     result = await OpenConfService.save_conf_service(auth=auth, data=data)
     return SuccessResponse(data=result, msg="配置更新成功")
+
+@OpenapiRouter.post(
+    "/account/balance/query",
+    summary="查询资金专户",
+    description="根据企业ID查询资金专户(调用支付宝接口)",
+)
+async def open_balance_query_controller(
+    apikey: Annotated[ApiKeyPayload[OpenBalanceQuerySchema], Depends(TenantApiKeyAuth(OpenBalanceQuerySchema))],
+) -> JSONResponse:
+    auth = apikey.auth
+    query_data = apikey.data
+    if query_data is None:
+        return ErrorResponse(msg="缺少必需参数")
+
+    result = await AccountService.query_account_service(auth=auth, data=query_data)
+    return SuccessResponse(data=result, msg="查询成功")

+ 6 - 2
backend/app/plugin/module_payment/openapi/schema.py

@@ -2,7 +2,7 @@ from typing import Optional
 from datetime import datetime
 from pydantic import BaseModel, Field, ConfigDict
 
-from app.plugin.module_payment.account.schema import AccountTransferSchema
+from app.plugin.module_payment.account.schema import AccountQuerySchema, AccountTransferSchema
 
 
 class OpenTransferSchema(AccountTransferSchema):
@@ -39,4 +39,8 @@ class OpenTransferOutSchema(BaseModel):
 
 
 class OpenTransferQuerySchema(BaseModel):
-    third_biz_no: str = Field(description="三方订单号")
+    third_biz_no: str = Field(description="三方订单号")
+
+
+class OpenBalanceQuerySchema(AccountQuerySchema):
+    """余额查询请求(继承资金专户查询参数)"""

+ 135 - 32
frontend/src/views/module_payment/apikey/index.vue

@@ -337,7 +337,7 @@
                       <el-icon><CopyDocument /></el-icon>
                     </el-button>
                   </template>
-                </el-input>        
+                </el-input>
               </el-form-item>
               <!-- <el-form-item label="异步通知地址">
                 <el-input
@@ -399,6 +399,7 @@
                 <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>
                 </ul>
               </div>
               <div class="sidebar-section">
@@ -412,11 +413,11 @@
                 <ul>
                   <li @click="activeSection = 'errors'" :class="{ active: activeSection === 'errors' }">常见错误</li>
                   <li @click="activeSection = 'php'" :class="{ active: activeSection === 'php' }">PHP示例代码</li>
-                </ul>
+                </ul> identity_type
               </div>
             </el-scrollbar>
           </div>
-          
+
           <!-- 右侧内容 -->
           <div class="docs-content">
             <el-card>
@@ -425,7 +426,7 @@
                   <span>{{ getSectionTitle() }}</span>
                 </div>
               </template>
-              
+
               <div v-if="activeSection === 'auth'" class="section-content">
                 <h2>1. 认证方式</h2>
                 <p>使用API Key进行认证时,需要在请求头中添加以下信息:</p>
@@ -437,7 +438,7 @@ Signature: {signature}</code></pre>
                   <li><strong>Signature</strong>:请求签名(必填),用于验证请求数据的完整性</li>
                 </ul>
               </div>
-              
+
               <div v-else-if="activeSection === 'signature'" class="section-content">
                 <h2>2. 签名验证</h2>
                 <p>签名用于验证请求数据的完整性,防止数据被篡改。签名生成步骤:</p>
@@ -494,7 +495,7 @@ signature = HMAC-SHA256(api_secret, sign_str)
 # 7. 请求头中添加签名
 Signature: {signature}</code></pre>
               </div>
-              
+
               <div v-else-if="activeSection === 'notes'" class="section-content">
                 <h2>3. 注意事项</h2>
                 <ul>
@@ -509,15 +510,15 @@ Signature: {signature}</code></pre>
                   <li>API Key有过期时间,请在过期前及时更新</li>
                 </ul>
               </div>
-              
+
               <div v-else-if="activeSection === 'transfer'" class="section-content">
                 <h2>4. 发起转账</h2>
                 <h3>4.1 接口说明</h3>
                 <p>从资金账户转账到支付宝账户/银行卡</p>
-                
+
                 <h4>API接口地址</h4>
                 <p><code>POST https://api.qcsj88888.com/payment/openapi/account/transfer</code></p>
-                
+
                 <h4>请求参数</h4>
                 <table class="api-table">
                   <thead>
@@ -667,7 +668,7 @@ Signature: {signature}</code></pre>
                     </tr>
                   </tbody>
                 </table>
-                
+
                 <h4>请求示例</h4>
                 <pre><code>curl -X POST 'https://api.qcsj88888.com/payment/openapi/account/transfer' \
   -H 'Authorization: ApiKey your_api_key' \
@@ -684,19 +685,19 @@ Signature: {signature}</code></pre>
       "identity": "收款人支付宝账号"
     }
   }'</code></pre>
-                
+
                 <h4>响应示例</h4>
                 <pre><code>{"code": 200, "message": "转账申请已提交", "data": {"status": "DEALING", "order_no": "2026042711122334455", "third_biz_no": "商户订单号202604270001"}}</code></pre>
               </div>
-              
+
               <div v-else-if="activeSection === 'transfer_query'" class="section-content">
                 <h2>5. 查询转账</h2>
                 <h3>5.1 接口说明</h3>
                 <p>根据三方订单号查询转账状态和详情</p>
-                
+
                 <h4>API接口地址</h4>
                 <p><code>POST https://api.qcsj88888.com/payment/openapi/account/transfer/query</code></p>
-                
+
                 <h4>请求参数</h4>
                 <table class="api-table">
                   <thead>
@@ -716,7 +717,7 @@ Signature: {signature}</code></pre>
                     </tr>
                   </tbody>
                 </table>
-                
+
                 <h4>请求示例</h4>
                 <pre><code>curl -X POST 'https://api.qcsj88888.com/payment/openapi/account/transfer/query' \
   -H 'Authorization: ApiKey your_api_key' \
@@ -725,7 +726,7 @@ Signature: {signature}</code></pre>
   -d '{
     "third_biz_no": "商户订单号202604270001"
   }'</code></pre>
-                
+
                 <h4>响应示例</h4>
                 <pre><code>{
   "code": 200,
@@ -772,12 +773,113 @@ Signature: {signature}</code></pre>
                   </tbody>
                 </table>
               </div>
-              
-              <div v-else-if="activeSection === 'callback'" class="section-content">
-                <h2>6. 回调通知</h2>
+
+              <div v-else-if="activeSection === 'balance'" class="section-content">
+                <h2>6. 账户余额</h2>
                 <h3>6.1 接口说明</h3>
+                <p>查询指定企业资金专户的余额信息</p>
+
+                <h4>API接口地址</h4>
+                <p><code>POST https://api.qcsj88888.com/payment/openapi/account/balance/query</code></p>
+
+                <h4>请求参数</h4>
+                <table class="api-table">
+                  <thead>
+                    <tr>
+                      <th>参数名</th>
+                      <th>类型</th>
+                      <th>是否必填</th>
+                      <th>描述</th>
+                    </tr>
+                  </thead>
+                  <tbody>
+                    <tr>
+                      <td>enterprise_id</td>
+                      <td>string</td>
+                      <td>是</td>
+                      <td>企业ID(在支付宝企业码平台注册的企业唯一标识)</td>
+                    </tr>
+                  </tbody>
+                </table>
+
+                <h4>请求示例</h4>
+                <pre><code>curl -X POST 'https://api.qcsj88888.com/payment/openapi/account/balance/query' \
+  -H 'Authorization: ApiKey your_api_key' \
+  -H 'Signature: your_signature' \
+  -H 'Content-Type: application/json' \
+  -d '{
+    "enterprise_id": "2088480777900000"
+  }'</code></pre>
+
+                <h4>响应示例</h4>
+                <pre><code>{
+  "code": 200,
+  "message": "查询成功",
+  "data": [
+    {
+      "account_book_id": "2088480770900000",
+      "available_amount": "50000.00",
+      "enable_status": "ENABLE",
+      "scene": "B2B_TRANS",
+      "account_card_info": {
+        "card_no": "xxxx",
+        "bank_name": "招商银行"
+      }
+    }
+  ]
+}</code></pre>
+
+                <h4>响应字段说明</h4>
+                <table class="api-table">
+                  <thead>
+                    <tr>
+                      <th>字段名</th>
+                      <th>类型</th>
+                      <th>描述</th>
+                    </tr>
+                  </thead>
+                  <tbody>
+                    <tr>
+                      <td>account_book_id</td>
+                      <td>string</td>
+                      <td>资金专户号</td>
+                    </tr>
+                    <tr>
+                      <td>available_amount</td>
+                      <td>string</td>
+                      <td>可用余额(单位:元,精确到小数点后两位)</td>
+                    </tr>
+                    <tr>
+                      <td>enable_status</td>
+                      <td>string</td>
+                      <td>启用状态:ENABLE(启用)/ DISABLE(禁用)</td>
+                    </tr>
+                    <tr>
+                      <td>scene</td>
+                      <td>string</td>
+                      <td>场景类型:B2B_TRANS(B2B转账)</td>
+                    </tr>
+                    <tr>
+                      <td>account_card_info</td>
+                      <td>object</td>
+                      <td>账户卡信息(银行卡号、银行名称等)</td>
+                    </tr>
+                  </tbody>
+                </table>
+
+                <h4>注意事项</h4>
+                <ul>
+                  <li>返回结果为<strong>数组</strong>,一个企业可能有多个资金专户</li>
+                  <li>余额单位为<strong>元</strong>,精确到小数点后两位</li>
+                  <li>仅返回 <code>scene</code> 为 <code>B2B_TRANS</code> 的资金专户</li>
+                </ul>
+              </div>
+
+              <div v-else-if="activeSection === 'callback'" class="section-content">
+                <h2>7. 回调通知</h2>
+                <h3>7.1 接口说明</h3>
                 <p>当转账状态发生变化时,系统会主动向商户配置的回调地址发送通知。</p>
-                
+
                 <h4>回调地址配置</h4>
                 <p>系统按照以下优先级获取回调地址:</p>
                 <ol style="margin-left: 20px;">
@@ -785,14 +887,14 @@ Signature: {signature}</code></pre>
                   <li><strong>开放平台配置</strong>:在 <strong>应用配置</strong> 页面设置默认回调地址</li>
                 </ol>
                 <p style="color: #909399; margin-top: 8px;">说明:如果 API Key 已配置回调地址,则优先使用;否则使用开放平台配置中的回调地址。</p>
-                
+
                 <h4>通知方式</h4>
                 <ul>
                   <li><strong>POST 请求</strong>:系统通过 HTTP POST 方式将通知数据发送到商户的回调地址</li>
                   <li><strong>表单形式</strong>:通知参数以表单形式提交(Content-Type: multipart/form-data)</li>
                   <li><strong>重试机制</strong>:如果通知失败,系统会自动重试(最多2次,间隔1秒、2秒)</li>
                 </ul>
-                
+
                 <h4>请求参数</h4>
                 <table class="api-table">
                   <thead>
@@ -820,7 +922,7 @@ Signature: {signature}</code></pre>
                     </tr>
                   </tbody>
                 </table>
-                
+
                 <h4>content 字段说明</h4>
                 <table class="api-table">
                   <thead>
@@ -863,7 +965,7 @@ Signature: {signature}</code></pre>
                     </tr>
                   </tbody>
                 </table>
-                
+
                 <h4>通知示例</h4>
                 <pre><code>POST /your/callback/url HTTP/1.1
 Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
@@ -888,10 +990,10 @@ Content-Disposition: form-data; name="content"
   "updated_time": "2026-04-27 11:25:45"
 }
 ------WebKitFormBoundary--</code></pre>
-                
+
                 <h4>响应要求</h4>
                 <p>商户服务端收到通知后,需要返回 HTTP 200 状态码表示成功接收。如果返回非 200 状态码或超时,系统会进行重试。</p>
-                
+
                 <h4>注意事项</h4>
                 <ul>
                   <li>回调地址需要支持 <strong>HTTPS</strong> 协议</li>
@@ -901,9 +1003,9 @@ Content-Disposition: form-data; name="content"
                   <li>系统最多重试 <strong>2次</strong>,重试间隔为 1 秒和 2 秒</li>
                 </ul>
               </div>
-              
+
               <div v-else-if="activeSection === 'errors'" class="section-content">
-                <h2>7. 常见错误</h2>
+                <h2>8. 常见错误</h2>
                 <ul>
                   <li><strong>401 Invalid API Key</strong>:API Key无效或已过期</li>
                   <li><strong>401 Signature header required</strong>:未提供Signature请求头</li>
@@ -913,10 +1015,10 @@ Content-Disposition: form-data; name="content"
                   <li><strong>500 Internal Server Error</strong>:服务器内部错误</li>
                 </ul>
               </div>
-              
+
               <div v-else-if="activeSection === 'php'" class="section-content">
-                <h2>8. PHP 示例代码</h2>
-                <h3>8.1 以下是签名生成的 PHP 示例代码:</h3>
+                <h2>9. PHP 示例代码</h2>
+                <h3>9.1 以下是签名生成的 PHP 示例代码:</h3>
                 <pre class="code-block"><code class="language-php">&lt;?php
 
 class SignatureGenerator
@@ -1129,6 +1231,7 @@ function getSectionTitle() {
     notes: "注意事项",
     transfer: "发起转账",
     transfer_query: "查询转账",
+    balance: "账户余额",
     callback: "回调通知",
     errors: "常见错误",
     php: "PHP示例代码"
@@ -1606,4 +1709,4 @@ function downloadApiKeyCsv() {
   font-weight: bold;
   color: #303133;
 }
-</style>
+</style>