添加文件: intelligence_finance_v1/generate.md

This commit is contained in:
renzhiyuan 2026-08-17 14:46:42 +08:00
parent fad24de28f
commit 6a61cfc009
1 changed files with 981 additions and 0 deletions

View File

@ -0,0 +1,981 @@
现在我将根据文档生成完整的 Go SDK 代码工程。
## 分析
**认证机制**:客户应用使用 `HmacSHA256` 算法,密钥为 `client-secret`,签名数据为 `timestamp + nonce`,结果 Base64 编码。需要生成 timestamp 和 nonce。
**接口列表**
1. 订单开票POST
2. 开票状态查询POST
3. 创建付款单据POST
4. 支付完成通知(回调,客户提供接口)
5. 支付状态查询POST
**通知机制**:客户提供接口,平台调用,响应直接返回字符串 `SUCCESS``FAILED`
现在开始生成所有文件:
---
// File: intelligence_finance_v1/go.mod
```go
module intelligence_finance_v1
go 1.21
```
---
// File: intelligence_finance_v1/errors.go
```go
package intelligence_finance_v1
import "fmt"
// Error 表示 SDK 返回的错误类型。
type Error struct {
// Code 错误码
Code int
// Msg 错误信息
Msg string
// Err 底层错误
Err error
}
// Error 实现 error 接口。
func (e *Error) Error() string {
if e.Err != nil {
return fmt.Sprintf("code=%d, msg=%s, err=%v", e.Code, e.Msg, e.Err)
}
return fmt.Sprintf("code=%d, msg=%s", e.Code, e.Msg)
}
// Unwrap 返回底层错误,支持 errors.Is/As。
func (e *Error) Unwrap() error {
return e.Err
}
// NewError 创建一个新的 Error。
func NewError(code int, msg string) *Error {
return &Error{Code: code, Msg: msg}
}
// WrapError 包装底层错误。
func WrapError(code int, msg string, err error) *Error {
return &Error{Code: code, Msg: msg, Err: err}
}
// 预定义错误
var (
// ErrInvalidConfig 配置无效
ErrInvalidConfig = NewError(1001, "invalid config")
// ErrNetwork 网络请求失败
ErrNetwork = NewError(1002, "network error")
// ErrSignature 签名失败
ErrSignature = NewError(1003, "signature error")
// ErrResponse 响应解析失败
ErrResponse = NewError(1004, "response error")
// ErrBusiness 业务错误code != 0
ErrBusiness = NewError(1005, "business error")
)
```
---
// File: intelligence_finance_v1/crypto.go
```go
package intelligence_finance_v1
import (
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"fmt"
"math/big"
"time"
)
// GenerateTimestamp 生成秒级时间戳字符串。
func GenerateTimestamp() string {
return fmt.Sprintf("%d", time.Now().Unix())
}
// GenerateTimestampMillis 生成毫秒级时间戳字符串。
func GenerateTimestampMillis() string {
return fmt.Sprintf("%d", time.Now().UnixMilli())
}
// GenerateNonce 生成指定长度的加密安全随机字符串。
func GenerateNonce(length int) (string, error) {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
b := make([]byte, length)
for i := range b {
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
if err != nil {
return "", err
}
b[i] = charset[num.Int64()]
}
return string(b), nil
}
// HmacSHA256Base64 使用 HmacSHA256 算法对 data 进行签名,密钥为 secret结果以 Base64 编码返回。
//
// 对应文档认证机制:客户应用使用 client-secret 对 (timestamp + nonce) 进行签名。
func HmacSHA256Base64(secret, data string) string {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(data))
return base64.StdEncoding.EncodeToString(mac.Sum(nil))
}
// BuildSignature 根据文档认证机制生成签名。
//
// 签名算法HmacSHA256
// 密钥client-secret
// 签名数据x-bfl-signature-timestamp + x-bfl-signature-nonce
// 结果编码Base64
func BuildSignature(clientSecret, timestamp, nonce string) string {
return HmacSHA256Base64(clientSecret, timestamp+nonce)
}
```
---
// File: intelligence_finance_v1/types.go
```go
package intelligence_finance_v1
// 发票类型枚举
const (
InvoiceTypeSpecial = 1 // 专用发票
InvoiceTypeNormal = 2 // 普通发票
InvoiceTypeNormalElectronic = 3 // 普通发票(电子)
InvoiceTypeSpecialElec = 4 // 专用发票(电子)
InvoiceTypeDigitalSpecial = 8 // 数电专票
InvoiceTypeDigitalNormal = 9 // 数电普票
)
// 开票状态枚举
const (
InvoiceStatusNotInvoiced = 0 // 未开票
InvoiceStatusInvoicing = 1 // 开票中
InvoiceStatusPartialFailed = 2 // 部分失败
InvoiceStatusSuccess = 3 // 开票成功
InvoiceStatusFailed = 4 // 开票失败
InvoiceStatusPartialNotDone = 5 // 部分未开
InvoiceStatusNoDigitalAccount = 6 // 未配置数电账号
InvoiceStatusNoAutoConfig = 7 // 未配置自动开票配置
)
// 支付状态枚举
const (
PaymentStatusSuccess = "SUCCESS" // 支付成功
PaymentStatusFail = "FAIL" // 支付失败
PaymentStatusTerminate = "TERMINATE" // 支付取消
PaymentStatusWaitPay = "WAIT_PAY" // 待支付
PaymentStatusPaying = "PAYING" // 支付中
PaymentStatusPartSuccess = "PART_SUCCESS" // 部分支付成功
PaymentStatusRefund = "REFUND" // 退款
)
// 来源枚举
const (
SourceApproval = "approval" // 审批单
SourceOpenAPI = "openapi" // 开发接口
)
// 账户类型枚举
const (
AccountTypeAlipay = "ALIPAY" // 支付宝
AccountTypeBankCard = "BANKCARD" // 银行卡
AccountTypeCorpBankCard = "CORP_BANK_CARD" // 对公银行卡
AccountTypePersonalBankCard = "PERSONAL_BANK_CARD" // 对私银行卡
)
// CommonResponse 通用响应结构。
type CommonResponse struct {
Code int `json:"code"` // 响应码
Msg string `json:"msg"` // 响应信息
Data any `json:"data"` // 响应数据
}
// InvoiceRequest 订单开票请求参数。
type InvoiceRequest struct {
CompanyCode string `json:"companyCode"` // 开票的企业主体编码
OrderID string `json:"orderId"` // 订单唯一标识
InvoiceType int `json:"invoiceType"` // 发票类型枚举
Products []InvoiceProduct `json:"products"` // 货物/服务明细列表
Remark string `json:"remark,omitempty"` // 订单备注
Purchaser string `json:"purchaser"` // 购方企业名称
TaxNum string `json:"taxnum,omitempty"` // 购方纳税人识别号
PurchaserAddr string `json:"purchaserAddress,omitempty"` // 购方地址
PurchaserTel string `json:"purchaserTel,omitempty"` // 购方电话
BankName string `json:"bankName,omitempty"` // 购方开户行名称
BankAccount string `json:"bankAccount,omitempty"` // 购方银行账号
Phone string `json:"phone,omitempty"` // 收票人手机号
Email string `json:"email,omitempty"` // 收票人邮箱
ApplyPerson string `json:"applyPerson,omitempty"` // 开票申请人名称
Payee string `json:"payee,omitempty"` // 收款人
Reviewer string `json:"reviewer,omitempty"` // 复核人
InvoiceRemark string `json:"invoiceRemark,omitempty"` // 发票备注栏内容
NaturalPerson string `json:"naturalPerson,omitempty"` // 购买方自然人标识
AdditionInfo string `json:"additionInfo,omitempty"` // 附加信息JSON数组字符串
}
// InvoiceProduct 订单开票货物/服务明细项。
type InvoiceProduct struct {
ProductName string `json:"productName"` // 货物或服务名称
RevenueCode string `json:"revenueCode"` // 19位税收分类编码
AmountIncludeTax float64 `json:"amountIncludeTax"` // 单条明细含税总金额(元)
Specs string `json:"specs,omitempty"` // 规格型号
Unit string `json:"unit,omitempty"` // 计量单位
Quantity float64 `json:"quantity"` // 数量
Discount float64 `json:"discount,omitempty"` // 折扣金额
TaxSign int `json:"taxSign,omitempty"` // 是否含税0-不含税1-含税
TaxRate float64 `json:"taxRate,omitempty"` // 税率(小数形式)
}
// InvoiceResponse 订单开票响应参数。
type InvoiceResponse struct {
Status int `json:"status"` // 开票状态
ErrorMsg string `json:"errorMsg"` // 错误信息
DataList []InvoiceData `json:"dataList"` // 发票数据列表
}
// InvoiceData 发票数据。
type InvoiceData struct {
DeviceCode string `json:"deviceCode"` // 税控设备号
Drawer string `json:"drawer"` // 开票人
Email string `json:"email"` // 邮箱
InvoiceType string `json:"invoiceType"` // 发票类型
IssueType string `json:"issueType"` // 开票类型
ListFlag string `json:"listFlag"` // 清单标识
Mobile string `json:"mobile"` // 手机号
OriginalInvCode string `json:"originalInvCode,omitempty"` // 原蓝票代码
OriginalInvNo string `json:"originalInvNo,omitempty"` // 原蓝票号码
AdditionInfo string `json:"additionInfo,omitempty"` // 附加信息
Payee string `json:"payee"` // 收款人
PurchaserAddress string `json:"purchaserAddress"` // 购方地址
PurchaserBankAccount string `json:"purchaserBankAccount"` // 购方银行账号
PurchaserBankName string `json:"purchaserBankName"` // 购方开户行
PurchaserName string `json:"purchaserName"` // 购方名称
PurchaserTaxNo string `json:"purchaserTaxNo"` // 购方税号
PurchaserTel string `json:"purchaserTel"` // 购方电话
NaturalPerson string `json:"naturalPerson,omitempty"` // 自然人标识
Remark string `json:"remark"` // 备注
Reviewer string `json:"reviewer"` // 复核人
SellerAddress string `json:"sellerAddress,omitempty"` // 销方地址
SellerBankAccount string `json:"sellerBankAccount"` // 销方开户账号
SellerBankName string `json:"sellerBankName"` // 销方开户行
SellerName string `json:"sellerName"` // 销方名称
CheckCode string `json:"checkCode"` // 校验码
CipherText string `json:"cipherText"` // 密码区
DrewDate string `json:"drewDate,omitempty"` // 开票日期
InvoiceCode string `json:"invoiceCode"` // 发票代码
InvoiceNo string `json:"invoiceNo"` // 发票号码
InvoiceStatus string `json:"invoiceStatus"` // 发票状态
LayoutFileURL string `json:"layoutFileUrl,omitempty"` // 电子发票地址
PdfURL string `json:"pdfUrl,omitempty"` // 电子发票PDF地址
OfdURL string `json:"ofdUrl,omitempty"` // 电子发票OFD地址
XmlURL string `json:"xmlUrl,omitempty"` // 电子发票XML地址
TotalExcludeTax string `json:"totalExcludeTax"` // 合计金额(不含税)
TotalIncludeTax string `json:"totalIncludeTax"` // 合计金额(含税)
TotalTaxAmount string `json:"totalTaxAmount"` // 合计税额
LevyingType string `json:"levyingType"` // 征税方式
Details []InvoiceDetail `json:"details"` // 商品明细列表
}
// InvoiceDetail 发票商品明细。
type InvoiceDetail struct {
Amount string `json:"amount,omitempty"` // 金额
Quantity string `json:"quantity,omitempty"` // 数量
DeductionAmount string `json:"deductionAmount,omitempty"` // 扣除金额
TaxAmount string `json:"taxAmount,omitempty"` // 税额
ItemTitle string `json:"itemTitle"` // 商品合并显示名称
TaxCode string `json:"taxCode"` // 税收分类编码
ItemType string `json:"itemType"` // 商品行性质
ItemName string `json:"itemName"` // 商品简称
Specs string `json:"specs,omitempty"` // 商品规格型号
TaxFreePolicy string `json:"taxFreePolicy"` // 免税政策
PreferentialPolicy string `json:"preferentialPolicy"` // 优惠政策类型
TaxRate string `json:"taxRate"` // 税率
TaxSign string `json:"taxSign"` // 是否含税
Unit string `json:"unit,omitempty"` // 计量单位
UnitPrice string `json:"unitPrice,omitempty"` // 单价
}
// InvoiceStatusQueryRequest 开票状态查询请求参数。
type InvoiceStatusQueryRequest struct {
OrderID string `json:"orderId"` // 订单唯一标识
}
// InvoiceStatusQueryResponse 开票状态查询响应参数。
type InvoiceStatusQueryResponse struct {
Status int `json:"status"` // 开票状态
Message string `json:"message"` // 状态描述
Data []InvoiceData `json:"data"` // 发票详细列表
}
// PaymentOrderRequest 创建付款单据请求参数。
type PaymentOrderRequest struct {
Code string `json:"code"` // 单据编码
YidaAppType string `json:"yidaAppType,omitempty"` // 宜搭应用类型
EmpAccountUserID string `json:"empAccountUserId,omitempty"` // 员工账户用户ID
Department *Department `json:"department,omitempty"` // 部门信息
Usage string `json:"usage,omitempty"` // 用途
PaymentUserID string `json:"paymentUserId,omitempty"` // 付款用户ID
Customer *Customer `json:"customer,omitempty"` // 客户信息
PrincipalID string `json:"principalId,omitempty"` // 负责人ID
Remark string `json:"remark,omitempty"` // 备注
Supplier *Supplier `json:"supplier,omitempty"` // 供应商信息
Title string `json:"title,omitempty"` // 标题
Project *Project `json:"project,omitempty"` // 项目信息
PaymentUserIDListStr string `json:"paymentUserIdListStr,omitempty"` // 付款用户ID列表字符串
NeedPayment bool `json:"needPayment,omitempty"` // 是否需要付款
PaymentDetailListJSONStr string `json:"paymentDetailListJsonStr,omitempty"` // 付款明细JSON字符串
PaymentDetailList []PaymentDetail `json:"paymentDetailList,omitempty"` // 付款明细列表
Company *Company `json:"company,omitempty"` // 企业主体信息
Amount string `json:"amount,omitempty"` // 金额
RecipientAccountInfo *RecipientAccount `json:"recipientAccountInfo,omitempty"` // 收款账户信息
EnterpriseAccount *EnterpriseAccount `json:"enterpriseAccount,omitempty"` // 企业账号信息
Category []Category `json:"category,omitempty"` // 收支类别信息
UserID string `json:"userId"` // 用户ID
OccurDate int64 `json:"occurDate,omitempty"` // 发生日期(时间戳)
Product *Product `json:"product,omitempty"` // 商品信息
YidaFormUUID string `json:"yidaFormUuid,omitempty"` // 宜搭表单UUID
CanEditPaymentInfo bool `json:"canEditPaymentInfo,omitempty"` // 是否可编辑付款信息
PaymentUserIDList []string `json:"paymentUserIdList,omitempty"` // 付款用户ID列表
YidaProcInsID string `json:"yidaProcInsId,omitempty"` // 宜搭流程实例ID
SyncPaymentOrder bool `json:"syncPaymentOrder,omitempty"` // 是否同步付款单据
}
// Department 部门信息。
type Department struct {
Code string `json:"code,omitempty"` // 部门编码
Name string `json:"name"` // 部门名称
}
// Customer 客户信息。
type Customer struct {
Code string `json:"code,omitempty"` // 客户编码
Name string `json:"name"` // 客户名称
}
// Supplier 供应商信息。
type Supplier struct {
Code string `json:"code,omitempty"` // 供应商编码
Name string `json:"name"` // 供应商名称
}
// Project 项目信息。
type Project struct {
Code string `json:"code,omitempty"` // 项目编码
Name string `json:"name"` // 项目名称
}
// Category 收支类别信息。
type Category struct {
Code string `json:"code,omitempty"` // 类别编码
Name string `json:"name"` // 类别名称
}
// Product 商品信息。
type Product struct {
Code string `json:"code,omitempty"` // 商品编码
Name string `json:"name"` // 商品名称
}
// Company 企业主体信息。
type Company struct {
Code string `json:"code,omitempty"` // 企业编码
Name string `json:"name"` // 企业名称
}
// EnterpriseAccount 企业账号信息。
type EnterpriseAccount struct {
EnterpriseAccountCode string `json:"enterpriseAccountCode,omitempty"` // 企业账号编码
AccountCategory string `json:"accountCategory"` // 账户类别
AccountType string `json:"accountType,omitempty"` // 账户类型
CardNo string `json:"cardNo,omitempty"` // 卡号
AccountName string `json:"accountName,omitempty"` // 账户名称
OfficialNumber string `json:"officialNumber,omitempty"` // 对公账号
OfficialName string `json:"officialName,omitempty"` // 对公账户名称
Name string `json:"name,omitempty"` // 名称
Code string `json:"code,omitempty"` // 编码
City string `json:"city,omitempty"` // 城市
Province string `json:"province,omitempty"` // 省份
}
// RecipientAccount 收款账户信息。
type RecipientAccount struct {
AccountCategory string `json:"accountCategory"` // 账户类别
AccountType string `json:"accountType,omitempty"` // 账户类型
CardNo string `json:"cardNo,omitempty"` // 卡号
AccountName string `json:"accountName,omitempty"` // 账户名称
}
// PaymentDetail 付款明细。
type PaymentDetail struct {
Amount string `json:"amount,omitempty"` // 金额
InvoiceInfo *InvoiceInfo `json:"invoiceInfo,omitempty"` // 发票信息
ProductCode string `json:"productCode,omitempty"` // 商品编码
ProjectCode string `json:"projectCode,omitempty"` // 项目编码
Remark string `json:"remark,omitempty"` // 备注
PrincipalID string `json:"principalId,omitempty"` // 负责人ID
Tax string `json:"tax,omitempty"` // 税额
}
// InvoiceInfo 付款明细发票信息。
type InvoiceInfo struct {
InvoiceNo string `json:"invoiceNo,omitempty"` // 发票号码
InvoiceCode string `json:"invoiceCode,omitempty"` // 发票代码
}
// PaymentOrderResponse 创建付款单据响应参数。
type PaymentOrderResponse struct {
Code string `json:"code"` // 单据唯一编码
}
// PaymentNotifyRequest 支付完成通知数据接口4
type PaymentNotifyRequest struct {
Code string `json:"code"` // 单据编码
InstanceID string `json:"instanceId"` // 实例ID
CorpID string `json:"corpId"` // 企业ID
PaymentStatus string `json:"paymentStatus"` // 支付状态
PaymentTime string `json:"paymentTime"` // 支付时间
UserID string `json:"userId"` // 用户ID
FailReason string `json:"failReason,omitempty"` // 失败原因
PayerAccountInfo *PayerAccountInfo `json:"payerAccountInfo,omitempty"` // 付款账户信息
PayeeAccountInfo *PayeeAccountInfo `json:"payeeAccountInfo,omitempty"` // 收款账户信息
RelatedRowNumberList []string `json:"relatedRowNumberList,omitempty"` // 关联行号列表
Source string `json:"source,omitempty"` // 来源
Template string `json:"template,omitempty"` // 模板
Amount string `json:"amount,omitempty"` // 金额
}
// PayerAccountInfo 付款账户信息。
type PayerAccountInfo struct {
BankOpenDTO *BankOpenDTO `json:"bankOpenDTO,omitempty"` // 银行信息
EnterpriseAccountCode string `json:"enterpriseAccountCode,omitempty"` // 企业账号编码
AccountType string `json:"accountType,omitempty"` // 账户类型
}
// PayeeAccountInfo 收款账户信息。
type PayeeAccountInfo struct {
BankOpenDTO *BankOpenDTO `json:"bankOpenDTO,omitempty"` // 银行信息
}
// BankOpenDTO 银行信息。
type BankOpenDTO struct {
BankCode string `json:"bankCode,omitempty"` // 银行编码
BankName string `json:"bankName,omitempty"` // 银行名称
BankBranchCode string `json:"bankBranchCode,omitempty"` // 银行支行编码
BankBranchName string `json:"bankBranchName,omitempty"` // 银行支行名称
AccountName string `json:"accountName,omitempty"` // 账户名称
BankCardNo string `json:"bankCardNo,omitempty"` // 银行卡号
Type string `json:"type,omitempty"` // 账户类型
}
// PaymentStatusQueryRequest 支付状态查询请求参数。
type PaymentStatusQueryRequest struct {
Code string `json:"code"` // 单据编码
UserID string `json:"userId"` // 用户ID
}
// PaymentStatusQueryResponse 支付状态查询响应参数(与支付通知数据结构一致)。
type PaymentStatusQueryResponse struct {
Code string `json:"code"` // 单据编码
InstanceID string `json:"instanceId"` // 实例ID
CorpID string `json:"corpId"` // 企业ID
PaymentStatus string `json:"paymentStatus"` // 支付状态
PaymentTime string `json:"paymentTime"` // 支付时间
UserID string `json:"userId"` // 用户ID
FailReason string `json:"failReason,omitempty"` // 失败原因
PayerAccountInfo *PayerAccountInfo `json:"payerAccountInfo,omitempty"` // 付款账户信息
PayeeAccountInfo *PayeeAccountInfo `json:"payeeAccountInfo,omitempty"` // 收款账户信息
RelatedRowNumberList []string `json:"relatedRowNumberList,omitempty"` // 关联行号列表
Source string `json:"source,omitempty"` // 来源
Template string `json:"template,omitempty"` // 模板
Amount string `json:"amount,omitempty"` // 金额
}
// Notification 通用通知数据bizType/bizId/data
type Notification struct {
BizType string `json:"bizType"` // 业务类型
BizID string `json:"bizId"` // 业务ID
Data string `json:"data"` // 业务数据
}
```
---
// File: intelligence_finance_v1/client.go
```go
package intelligence_finance_v1
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
// Client 是智能财务业财连接 SDK 的客户端。
type Client struct {
// baseURL 平台接口基础地址
baseURL string
// tenantID 平台分配的租户唯一标识
tenantID string
// clientID 平台分配的应用标识
clientID string
// clientSecret 平台分配的密钥
clientSecret string
// httpClient HTTP 客户端
httpClient *http.Client
// signEnabled 是否启用签名钉钉AI表格无需配置签名可关闭
signEnabled bool
}
// ClientOption 客户端配置选项。
type ClientOption func(*Client)
// WithHTTPClient 自定义 HTTP 客户端。
func WithHTTPClient(hc *http.Client) ClientOption {
return func(c *Client) {
c.httpClient = hc
}
}
// WithSignEnabled 设置是否启用签名(默认启用)。
func WithSignEnabled(enabled bool) ClientOption {
return func(c *Client) {
c.signEnabled = enabled
}
}
// NewClient 创建一个新的智能财务业财连接客户端。
//
// 参数:
// - baseURL平台接口基础地址
// - tenantID平台分配的租户唯一标识
// - clientID平台分配的应用标识钉钉AI表格固定为 dd-ai-table
// - clientSecret平台分配的密钥
func NewClient(baseURL, tenantID, clientID, clientSecret string, opts ...ClientOption) *Client {
c := &Client{
baseURL: baseURL,
tenantID: tenantID,
clientID: clientID,
clientSecret: clientSecret,
httpClient: &http.Client{Timeout: 30 * time.Second},
signEnabled: true,
}
for _, opt := range opts {
opt(c)
}
return c
}
// doRequest 发送 HTTP 请求并解析响应。
func (c *Client) doRequest(ctx context.Context, path string, reqBody any, respBody any) error {
bodyBytes, err := json.Marshal(reqBody)
if err != nil {
return WrapError(ErrSignature.Code, "marshal request body failed", err)
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.baseURL+path, bytes.NewReader(bodyBytes))
if err != nil {
return WrapError(ErrNetwork.Code, "create request failed", err)
}
// 设置公共请求头
req.Header.Set("Content-Type", "application/json")
req.Header.Set("tenant-id", c.tenantID)
req.Header.Set("client-id", c.clientID)
// 签名钉钉AI表格无需配置可通过 WithSignEnabled(false) 关闭)
if c.signEnabled {
timestamp := GenerateTimestamp()
nonce, err := GenerateNonce(16)
if err != nil {
return WrapError(ErrSignature.Code, "generate nonce failed", err)
}
req.Header.Set("x-bfl-signature-timestamp", timestamp)
req.Header.Set("x-bfl-signature-nonce", nonce)
req.Header.Set("x-bfl-signature", BuildSignature(c.clientSecret, timestamp, nonce))
}
resp, err := c.httpClient.Do(req)
if err != nil {
return WrapError(ErrNetwork.Code, "do request failed", err)
}
defer resp.Body.Close()
respBytes, err := io.ReadAll(resp.Body)
if err != nil {
return WrapError(ErrResponse.Code, "read response body failed", err)
}
if resp.StatusCode != http.StatusOK {
return WrapError(ErrResponse.Code, fmt.Sprintf("unexpected http status: %d", resp.StatusCode), nil)
}
if err := json.Unmarshal(respBytes, respBody); err != nil {
return WrapError(ErrResponse.Code, "unmarshal response body failed", err)
}
return nil
}
// doRequestCommon 发送请求并解析通用响应结构code/msg/data
func (c *Client) doRequestCommon(ctx context.Context, path string, reqBody any, data any) error {
var common CommonResponse
if err := c.doRequest(ctx, path, reqBody, &common); err != nil {
return err
}
if common.Code != 0 {
return WrapError(common.Code, common.Msg, nil)
}
if data != nil && common.Data != nil {
// 将 data 重新序列化后反序列化到目标结构
dataBytes, err := json.Marshal(common.Data)
if err != nil {
return WrapError(ErrResponse.Code, "marshal data failed", err)
}
if err := json.Unmarshal(dataBytes, data); err != nil {
return WrapError(ErrResponse.Code, "unmarshal data failed", err)
}
}
return nil
}
// CreateInvoice 提交订单开票请求接口1
//
// 请求方法POST
// 描述:提交订单开票请求。
func (c *Client) CreateInvoice(ctx context.Context, req *InvoiceRequest) (*InvoiceResponse, error) {
var resp InvoiceResponse
if err := c.doRequestCommon(ctx, "/invoice/create", req, &resp); err != nil {
return nil, err
}
return &resp, nil
}
// QueryInvoiceStatus 查询订单开票状态接口2
//
// 请求方法POST
// 描述:查询订单开票状态。
func (c *Client) QueryInvoiceStatus(ctx context.Context, req *InvoiceStatusQueryRequest) (*InvoiceStatusQueryResponse, error) {
var resp InvoiceStatusQueryResponse
if err := c.doRequestCommon(ctx, "/invoice/status", req, &resp); err != nil {
return nil, err
}
return &resp, nil
}
// CreatePaymentOrder 创建付款单据接口3
//
// 请求方法POST
// 描述:创建付款单据。
func (c *Client) CreatePaymentOrder(ctx context.Context, req *PaymentOrderRequest) (*PaymentOrderResponse, error) {
var resp PaymentOrderResponse
if err := c.doRequestCommon(ctx, "/payment/order/create", req, &resp); err != nil {
return nil, err
}
return &resp, nil
}
// QueryPaymentStatus 查询支付状态接口5
//
// 请求方法POST
// 描述:查询支付状态,响应数据与支付通知一致。
func (c *Client) QueryPaymentStatus(ctx context.Context, req *PaymentStatusQueryRequest) (*PaymentStatusQueryResponse, error) {
var resp PaymentStatusQueryResponse
if err := c.doRequestCommon(ctx, "/payment/status", req, &resp); err != nil {
return nil, err
}
return &resp, nil
}
// HandlePaymentNotify 处理支付完成通知接口4平台回调客户接口
//
// 该方法用于客户服务端接收平台回调,解析通知数据并返回响应字符串。
// 响应 BODY 直接返回字符串SUCCESS 表示成功FAILED 表示失败。
func HandlePaymentNotify(body []byte) (*PaymentNotifyRequest, error) {
var req PaymentNotifyRequest
if err := json.Unmarshal(body, &req); err != nil {
return nil, WrapError(ErrResponse.Code, "unmarshal payment notify failed", err)
}
return &req, nil
}
// HandleNotification 处理通用通知bizType/bizId/data
//
// 该方法用于客户服务端接收平台通用通知,解析通知数据。
func HandleNotification(body []byte) (*Notification, error) {
var n Notification
if err := json.Unmarshal(body, &n); err != nil {
return nil, WrapError(ErrResponse.Code, "unmarshal notification failed", err)
}
return &n, nil
}
```
---
// File: intelligence_finance_v1/example_test.go
```go
package intelligence_finance_v1
import (
"context"
"encoding/json"
"fmt"
"net/http"
"testing"
)
// ExampleNewClient 展示如何创建客户端并提交订单开票请求。
func ExampleNewClient() {
// 创建客户端
client := NewClient(
"https://api.example.com",
"your-tenant-id",
"dd-ai-table",
"your-client-secret",
)
// 构建订单开票请求
req := &InvoiceRequest{
OrderID: "ORDER20240101001",
InvoiceType: InvoiceTypeDigitalSpecial, // 数电专票
Purchaser: "示例科技有限公司",
TaxNum: "91110000XXXXXXXXXX",
Email: "finance@example.com",
Phone: "13800000000",
Products: []InvoiceProduct{
{
ProductName: "软件开发服务",
RevenueCode: "1090101000000000000",
AmountIncludeTax: 11300,
Quantity: 1,
TaxSign: 1,
TaxRate: 0.13,
},
},
}
resp, err := client.CreateInvoice(context.Background(), req)
if err != nil {
fmt.Printf("create invoice failed: %v\n", err)
return
}
fmt.Printf("invoice status: %d\n", resp.Status)
}
// ExampleClient_QueryInvoiceStatus 展示如何查询开票状态。
func ExampleClient_QueryInvoiceStatus() {
client := NewClient(
"https://api.example.com",
"your-tenant-id",
"dd-ai-table",
"your-client-secret",
)
resp, err := client.QueryInvoiceStatus(context.Background(), &InvoiceStatusQueryRequest{
OrderID: "ORDER20240101001",
})
if err != nil {
fmt.Printf("query invoice status failed: %v\n", err)
return
}
fmt.Printf("status: %d, message: %s\n", resp.Status, resp.Message)
}
// ExampleClient_CreatePaymentOrder 展示如何创建付款单据。
func ExampleClient_CreatePaymentOrder() {
client := NewClient(
"https://api.example.com",
"your-tenant-id",
"dd-ai-table",
"your-client-secret",
)
req := &PaymentOrderRequest{
Code: "PAY20240101001",
UserID: "user-001",
Title: "供应商货款",
Amount: "11300.00",
Supplier: &Supplier{
Name: "示例供应商有限公司",
},
Company: &Company{
Name: "示例科技有限公司",
},
PaymentDetailList: []PaymentDetail{
{
Amount: "11300.00",
InvoiceInfo: &InvoiceInfo{
InvoiceNo: "12345678",
InvoiceCode: "011002000111",
},
},
},
}
resp, err := client.CreatePaymentOrder(context.Background(), req)
if err != nil {
fmt.Printf("create payment order failed: %v\n", err)
return
}
fmt.Printf("payment order code: %s\n", resp.Code)
}
// ExampleClient_QueryPaymentStatus 展示如何查询支付状态。
func ExampleClient_QueryPaymentStatus() {
client := NewClient(
"https://api.example.com",
"your-tenant-id",
"dd-ai-table",
"your-client-secret",
)
resp, err := client.QueryPaymentStatus(context.Background(), &PaymentStatusQueryRequest{
Code: "PAY20240101001",
UserID: "user-001",
})
if err != nil {
fmt.Printf("query payment status failed: %v\n", err)
return
}
fmt.Printf("payment status: %s\n", resp.PaymentStatus)
}
// ExampleHandlePaymentNotify 展示如何在客户服务端处理支付完成通知。
func ExampleHandlePaymentNotify() {
// 模拟平台回调的请求体
body := []byte(`{
"code": "PAY20240101001",
"instanceId": "inst-001",
"corpId": "corp-001",
"paymentStatus": "SUCCESS",
"paymentTime": "2024-01-01 12:00:00",
"userId": "user-001",
"amount": "11300.00",
"source": "openapi"
}`)
notify, err := HandlePaymentNotify(body)
if err != nil {
fmt.Printf("handle payment notify failed: %v\n", err)
return
}
// 处理业务逻辑...
// 返回响应字符串
if notify.PaymentStatus == PaymentStatusSuccess {
fmt.Println("SUCCESS")
} else {
fmt.Println("FAILED")
}
}
// TestSignature 验证签名算法。
func TestSignature(t *testing.T) {
secret := "test-secret"
timestamp := "1700000000"
nonce := "abc123"
sig := BuildSignature(secret, timestamp, nonce)
if sig == "" {
t.Fatal("signature should not be empty")
}
t.Logf("signature: %s", sig)
}
// TestHandlePaymentNotify 测试支付通知解析。
func TestHandlePaymentNotify(t *testing.T) {
body := []byte(`{"code":"PAY001","instanceId":"i1","corpId":"c1","paymentStatus":"SUCCESS","paymentTime":"2024-01-01 12:00:00","userId":"u1"}`)
n, err := HandlePaymentNotify(body)
if err != nil {
t.Fatalf("handle payment notify failed: %v", err)
}
if n.Code != "PAY001" {
t.Fatalf("unexpected code: %s", n.Code)
}
}
// TestHandleNotification 测试通用通知解析。
func TestHandleNotification(t *testing.T) {
body := []byte(`{"bizType":"invoice","bizId":"ORDER001","data":"{\"status\":3}"}`)
n, err := HandleNotification(body)
if err != nil {
t.Fatalf("handle notification failed: %v", err)
}
if n.BizType != "invoice" {
t.Fatalf("unexpected bizType: %s", n.BizType)
}
}
// TestJSONMarshal 测试请求体 JSON 序列化。
func TestJSONMarshal(t *testing.T) {
req := &InvoiceRequest{
OrderID: "ORDER001",
InvoiceType: 9,
Purchaser: "测试公司",
Products: []InvoiceProduct{
{ProductName: "服务", RevenueCode: "1090101000000000000", AmountIncludeTax: 100, Quantity: 1},
},
}
b, err := json.Marshal(req)
if err != nil {
t.Fatalf("marshal failed: %v", err)
}
t.Logf("request json: %s", string(b))
}
// TestHTTPHandler 展示如何将通知处理接入 HTTP 服务。
func TestHTTPHandler(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var body []byte
_, _ = r.Body.Read(body)
notify, err := HandlePaymentNotify(body)
if err != nil {
w.Write([]byte("FAILED"))
return
}
_ = notify
w.Write([]byte("SUCCESS"))
})
_ = handler
}
```
---
=== SDK 生成完成 ===
## 加密实现
以下是从加密工具获取的完整实现:
### 加密实现 1
{
"success": true,
"data": "\n### 参数拼接规范\n\n**常见拼接方式**\n\n1. **字典序排序**按key字典序排序后拼接 `key=value\u0026key2=value2`\n2. **固定顺序**:按文档指定顺序拼接\n3. **JSON字符串**:整个请求体作为签名串\n\n**代码模板**\n\n```go\npackage crypto\n\nimport (\n \"fmt\"\n \"sort\"\n \"strings\"\n)\n\n// BuildSignString 方式1字典序排序拼接\nfunc BuildSignString(params map[string]string) string {\n keys := make([]string, 0, len(params))\n for k, v := range params {\n if v != \"\" \u0026\u0026 k != \"sign\" \u0026\u0026 k != \"signature\" {\n keys = append(keys, k)\n }\n }\n sort.Strings(keys)\n \n var parts []string\n for _, k := range keys {\n parts = append(parts, fmt.Sprintf(\"%s=%s\", k, params[k]))\n }\n return strings.Join(parts, \"\u0026\")\n}\n\n// BuildSignStringOrdered 方式2固定顺序拼接\nfunc BuildSignStringOrdered(params map[string]string, orderedKeys []string) string {\n var parts []string\n for _, k := range orderedKeys {\n if v, ok := params[k]; ok \u0026\u0026 v != \"\" {\n parts = append(parts, fmt.Sprintf(\"%s=%s\", k, v))\n }\n }\n return strings.Join(parts, \"\u0026\")\n}\n```\n\n**注意事项**\n- 确认文档指定的排序规则(字典序/固定顺序)\n- 确认空值是否要包含(通常排除空值)\n- 确认是否需要URL编码\n- 注意排除签名字段本身\n",
"tool": "param_concat"
}
### 加密实现 2
{
"success": true,
"data": "\n### 时间戳和随机数生成规范\n\n**代码模板**\n\n```go\npackage crypto\n\nimport (\n \"crypto/rand\"\n \"fmt\"\n \"math/big\"\n \"time\"\n)\n\n// GenerateTimestamp 生成秒级时间戳\nfunc GenerateTimestamp() string {\n return fmt.Sprintf(\"%d\", time.Now().Unix())\n}\n\n// GenerateTimestampMillis 生成毫秒级时间戳\nfunc GenerateTimestampMillis() string {\n return fmt.Sprintf(\"%d\", time.Now().UnixMilli())\n}\n\n// GenerateNonce 生成指定长度的随机字符串(加密安全)\nfunc GenerateNonce(length int) (string, error) {\n const charset = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\"\n b := make([]byte, length)\n for i := range b {\n num, err := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))\n if err != nil {\n return \"\", err\n }\n b[i] = charset[num.Int64()]\n }\n return string(b), nil\n}\n```\n\n**注意事项**\n- 确认文档要求的是秒级还是毫秒级时间戳\n- 确认nonce的长度要求通常16-32位\n- 生产环境建议使用加密安全的随机数生成器\n",
"tool": "nonce_timestamp"
}