// File: intelligence_finance_v1/go.mod module intelligence_finance_v1 go 1.21 // File: intelligence_finance_v1/crypto.go package intelligence_finance_v1 import ( "crypto/hmac" "crypto/rand" "crypto/sha256" "encoding/base64" "fmt" "math/big" "sort" "strings" "time" ) // GenerateTimestamp 生成秒级时间戳字符串 func GenerateTimestamp() string { return fmt.Sprintf("%d", time.Now().Unix()) } // 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算法对数据进行签名,返回Base64编码结果 func HmacSHA256Base64(secret, data string) string { mac := hmac.New(sha256.New, []byte(secret)) mac.Write([]byte(data)) return base64.StdEncoding.EncodeToString(mac.Sum(nil)) } // BuildSignString 按字典序排序拼接参数(排除空值和签名字段) func BuildSignString(params map[string]string) string { keys := make([]string, 0, len(params)) for k, v := range params { if v != "" && k != "sign" && k != "signature" && k != "x-bfl-signature" { keys = append(keys, k) } } sort.Strings(keys) var parts []string for _, k := range keys { parts = append(parts, fmt.Sprintf("%s=%s", k, params[k])) } return strings.Join(parts, "&") } // BuildSignStringOrdered 按固定顺序拼接参数 func BuildSignStringOrdered(params map[string]string, orderedKeys []string) string { var parts []string for _, k := range orderedKeys { if v, ok := params[k]; ok && v != "" { parts = append(parts, fmt.Sprintf("%s=%s", k, v)) } } return strings.Join(parts, "&") } // File: intelligence_finance_v1/types.go package intelligence_finance_v1 import "encoding/json" // ============================================================ // 通用请求/响应结构 // ============================================================ // CommonResponse 通用API响应结构 type CommonResponse struct { Code int `json:"code"` Msg string `json:"msg,omitempty"` Data json.RawMessage `json:"data,omitempty"` } // NotificationRequest 回调通知请求结构 type NotificationRequest struct { BizType string `json:"bizType"` BizID string `json:"bizId"` Data string `json:"data,omitempty"` } // ============================================================ // 接口1:订单开票请求/响应 // ============================================================ // CreateInvoiceRequest 提交订单开票申请请求 type CreateInvoiceRequest struct { CompanyCode string `json:"companyCode,omitempty"` 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"` } // InvoiceProduct 货物/服务明细 type InvoiceProduct struct { ProductName string `json:"productName"` RevenueCode string `json:"revenueCode"` 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"` TaxRate float64 `json:"taxRate,omitempty"` } // CreateInvoiceResponse 提交订单开票申请响应 type CreateInvoiceResponse struct { Status int `json:"status"` ErrorMsg string `json:"errorMsg,omitempty"` 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"` OfdURL string `json:"ofdUrl,omitempty"` XmlURL string `json:"xmlUrl,omitempty"` 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"` } // ============================================================ // 接口2:开票状态查询请求/响应 // ============================================================ // QueryInvoiceStatusRequest 查询订单开票状态请求 type QueryInvoiceStatusRequest struct { OrderID string `json:"orderId"` } // QueryInvoiceStatusResponse 查询订单开票状态响应 type QueryInvoiceStatusResponse struct { Status int `json:"status"` Message string `json:"message"` Data []InvoiceData `json:"data"` } // ============================================================ // 接口3:创建付款单据请求/响应 // ============================================================ // CreatePaymentRequest 创建付款单据请求 type CreatePaymentRequest struct { Code string `json:"code"` YidaAppType string `json:"yidaAppType,omitempty"` EmpAccountUserID string `json:"empAccountUserId,omitempty"` Department *Department `json:"department,omitempty"` Usage string `json:"usage,omitempty"` PaymentUserID string `json:"paymentUserId,omitempty"` Customer *Customer `json:"customer,omitempty"` PrincipalID string `json:"principalId,omitempty"` 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"` NeedPayment bool `json:"needPayment,omitempty"` PaymentDetailListJsonStr string `json:"paymentDetailListJsonStr,omitempty"` 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"` OccurDate int64 `json:"occurDate,omitempty"` Product *Product `json:"product,omitempty"` YidaFormUUID string `json:"yidaFormUuid,omitempty"` CanEditPaymentInfo bool `json:"canEditPaymentInfo,omitempty"` PaymentUserIDList []string `json:"paymentUserIdList,omitempty"` YidaProcInsID string `json:"yidaProcInsId,omitempty"` 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"` Tax string `json:"tax,omitempty"` } // InvoiceInfo 付款明细发票信息 type InvoiceInfo struct { InvoiceNo string `json:"invoiceNo,omitempty"` InvoiceCode string `json:"invoiceCode,omitempty"` } // CreatePaymentResponse 创建付款单据响应 type CreatePaymentResponse struct { Code string `json:"code"` } // ============================================================ // 接口4:支付完成通知(平台回调) // ============================================================ // PaymentNotification 支付完成通知数据结构 type PaymentNotification struct { Code string `json:"code"` InstanceID string `json:"instanceId"` CorpID string `json:"corpId"` PaymentStatus string `json:"paymentStatus"` PaymentTime string `json:"paymentTime"` UserID string `json:"userId"` 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"` } // ============================================================ // 接口5:支付状态查询请求/响应 // ============================================================ // QueryPaymentStatusRequest 查询支付状态请求 type QueryPaymentStatusRequest struct { Code string `json:"code"` UserID string `json:"userId"` } // QueryPaymentStatusResponse 查询支付状态响应(与PaymentNotification结构一致) type QueryPaymentStatusResponse struct { PaymentNotification } // ============================================================ // 接口6:创建供应商请求/响应 // ============================================================ // CreateSupplierRequest 创建供应商请求 type CreateSupplierRequest struct { InvokeID string `json:"invokeId,omitempty"` UserID string `json:"userId"` Data string `json:"data,omitempty"` BizType string `json:"bizType"` } // CreateSupplierBizData 创建供应商业务数据 type CreateSupplierBizData struct { CorpID string `json:"corpId"` Creator string `json:"creator"` SupplierInfo *SupplierInfo `json:"supplierInfo,omitempty"` } // SupplierInfo 供应商数据 type SupplierInfo struct { Name string `json:"name"` CorpID string `json:"corpId"` UserDefineCode string `json:"userDefineCode,omitempty"` Description string `json:"description,omitempty"` ContactAddress string `json:"contactAddress,omitempty"` ContactCompanyTelephone string `json:"contactCompanyTelephone,omitempty"` ContactEmail string `json:"contactEmail,omitempty"` ContactName string `json:"contactName,omitempty"` ContactTelephone string `json:"contactTelephone,omitempty"` Creator string `json:"creator"` InvoiceAccount string `json:"invoiceAccount,omitempty"` InvoiceAddress string `json:"invoiceAddress,omitempty"` InvoiceBankName string `json:"invoiceBankName,omitempty"` InvoiceName string `json:"invoiceName,omitempty"` InvoiceTaxNo string `json:"invoiceTaxNo,omitempty"` PurchaserTel string `json:"purchaserTel,omitempty"` BankName string `json:"bankName,omitempty"` InvoiceTelephone string `json:"invoiceTelephone,omitempty"` AccountName string `json:"accountName,omitempty"` AccountType string `json:"accountType,omitempty"` } // CreateSupplierResponse 创建供应商响应 type CreateSupplierResponse struct { InvokeID string `json:"invokeId,omitempty"` BizType string `json:"bizType"` Data json.RawMessage `json:"data,omitempty"` } // CreateSupplierResponseData 创建供应商响应数据 type CreateSupplierResponseData struct { Result string `json:"result,omitempty"` Success bool `json:"success"` } // SupplierInfoResponse 供应商信息(响应) type SupplierInfoResponse struct { SupplierID string `json:"supplierId"` SupplierName string `json:"supplierName"` Name string `json:"name"` CorpID string `json:"corpId"` UserDefineCode string `json:"userDefineCode,omitempty"` Description string `json:"description,omitempty"` ContactAddress string `json:"contactAddress,omitempty"` ContactCompanyTelephone string `json:"contactCompanyTelephone,omitempty"` ContactEmail string `json:"contactEmail,omitempty"` ContactName string `json:"contactName,omitempty"` ContactTelephone string `json:"contactTelephone,omitempty"` Creator string `json:"creator"` InvoiceAccount string `json:"invoiceAccount,omitempty"` InvoiceAddress string `json:"invoiceAddress,omitempty"` InvoiceBankName string `json:"invoiceBankName,omitempty"` } // ============================================================ // 接口7:根据名称查询供应商请求/响应 // ============================================================ // QuerySupplierByNameRequest 根据名称查询供应商请求 type QuerySupplierByNameRequest struct { InvokeID string `json:"invokeId,omitempty"` UserID string `json:"userId"` Data string `json:"data,omitempty"` BizType string `json:"bizType"` } // QuerySupplierByNameBizData 根据名称查询供应商业务数据 type QuerySupplierByNameBizData struct { CorpID string `json:"corpId"` Name string `json:"name"` } // QuerySupplierByNameResponse 根据名称查询供应商响应 type QuerySupplierByNameResponse struct { InvokeID string `json:"invokeId,omitempty"` BizType string `json:"bizType"` Data json.RawMessage `json:"data,omitempty"` Result string `json:"result,omitempty"` Success bool `json:"success"` } // QuerySupplierByNameResult 供应商查询结果重要字段 type QuerySupplierByNameResult struct { SupplierName string `json:"supplierName"` UserDefineCode string `json:"userDefineCode,omitempty"` InvoiceName string `json:"invoiceName,omitempty"` SupplierID string `json:"supplierId"` CorpID string `json:"corpId"` ContactTelephone string `json:"contactTelephone,omitempty"` ContactEmail string `json:"contactEmail,omitempty"` ContactName string `json:"contactName,omitempty"` InvoiceBankName string `json:"invoiceBankName,omitempty"` InvoiceAccount string `json:"invoiceAccount,omitempty"` Description string `json:"description,omitempty"` InvoiceAddress string `json:"invoiceAddress,omitempty"` InvoiceTaxNo string `json:"invoiceTaxNo,omitempty"` ContactCompanyTelephone string `json:"contactCompanyTelephone,omitempty"` CreateTime int64 `json:"createTime,omitempty"` InvoiceTelephone string `json:"invoiceTelephone,omitempty"` ContactAddress string `json:"contactAddress,omitempty"` } // ============================================================ // 接口8:更新供应商请求/响应 // ============================================================ // UpdateSupplierRequest 更新供应商请求 type UpdateSupplierRequest struct { InvokeID string `json:"invokeId,omitempty"` UserID string `json:"userId"` Data string `json:"data,omitempty"` BizType string `json:"bizType"` } // UpdateSupplierBizData 更新供应商业务数据 type UpdateSupplierBizData struct { SupplierID string `json:"supplierId"` Name string `json:"name"` CorpID string `json:"corpId"` UserDefineCode string `json:"userDefineCode,omitempty"` Description string `json:"description,omitempty"` ContactAddress string `json:"contactAddress,omitempty"` ContactCompanyTelephone string `json:"contactCompanyTelephone,omitempty"` ContactEmail string `json:"contactEmail,omitempty"` ContactName string `json:"contactName,omitempty"` ContactTelephone string `json:"contactTelephone,omitempty"` UserID string `json:"userId"` InvoiceAccount string `json:"invoiceAccount,omitempty"` InvoiceAddress string `json:"invoiceAddress,omitempty"` InvoiceBankName string `json:"invoiceBankName,omitempty"` InvoiceName string `json:"invoiceName,omitempty"` InvoiceTaxNo string `json:"invoiceTaxNo,omitempty"` PurchaserTel string `json:"purchaserTel,omitempty"` BankName string `json:"bankName,omitempty"` InvoiceTelephone string `json:"invoiceTelephone,omitempty"` AccountName string `json:"accountName,omitempty"` AccountType string `json:"accountType,omitempty"` BankBranchCode string `json:"bankBranchCode,omitempty"` BankCode string `json:"bankCode,omitempty"` BankCity string `json:"bankCity,omitempty"` BankProvince string `json:"bankProvince,omitempty"` } // UpdateSupplierResponse 更新供应商响应 type UpdateSupplierResponse struct { InvokeID string `json:"invokeId,omitempty"` BizType string `json:"bizType"` Data json.RawMessage `json:"data,omitempty"` } // UpdateSupplierResponseData 更新供应商响应数据 type UpdateSupplierResponseData struct { Result string `json:"result,omitempty"` Success bool `json:"success"` } // ============================================================ // 接口9:根据用户自定义编码查询供应商请求/响应 // ============================================================ // QuerySupplierByUserDefineCodeRequest 根据用户自定义编码查询供应商请求 type QuerySupplierByUserDefineCodeRequest struct { InvokeID string `json:"invokeId,omitempty"` UserID string `json:"userId"` Data string `json:"data,omitempty"` BizType string `json:"bizType"` } // QuerySupplierByUserDefineCodeBizData 根据自定义编码查询供应商业务数据 type QuerySupplierByUserDefineCodeBizData struct { CorpID string `json:"corpId"` UserDefineCode string `json:"userDefineCode"` } // QuerySupplierByUserDefineCodeResponse 根据自定义编码查询供应商响应 type QuerySupplierByUserDefineCodeResponse struct { InvokeID string `json:"invokeId,omitempty"` BizType string `json:"bizType"` Data json.RawMessage `json:"data,omitempty"` } // QuerySupplierByUserDefineCodeResponseData 响应数据 type QuerySupplierByUserDefineCodeResponseData struct { Success bool `json:"success"` } // QuerySupplierByUserDefineCodeResult 供应商查询结果重要字段 type QuerySupplierByUserDefineCodeResult struct { UserDefineCode string `json:"userDefineCode"` SupplierName string `json:"supplierName"` ModifiedTime int64 `json:"modifiedTime,omitempty"` Creator string `json:"creator,omitempty"` SupplierID string `json:"supplierId"` Code string `json:"code,omitempty"` CorpID string `json:"corpId"` CreateTime int64 `json:"createTime,omitempty"` Name string `json:"name"` ID int64 `json:"id,omitempty"` Status string `json:"status,omitempty"` } // File: intelligence_finance_v1/errors.go package intelligence_finance_v1 import "fmt" // SDKError SDK通用错误类型 type SDKError struct { Code int `json:"code"` Message string `json:"message"` Err error `json:"-"` } // Error 实现error接口 func (e *SDKError) Error() string { if e.Err != nil { return fmt.Sprintf("code=%d, message=%s, err=%v", e.Code, e.Message, e.Err) } return fmt.Sprintf("code=%d, message=%s", e.Code, e.Message) } // Unwrap 返回包装的错误 func (e *SDKError) Unwrap() error { return e.Err } // NewSDKError 创建SDK错误 func NewSDKError(code int, message string, err error) *SDKError { return &SDKError{ Code: code, Message: message, Err: err, } } // 预定义错误码 const ( ErrCodeSuccess = 0 ErrCodeUnknown = -1 ErrCodeInvalidParam = 1001 ErrCodeSignatureFailed = 1002 ErrCodeNetworkError = 1003 ErrCodeRequestTimeout = 1004 ErrCodeServerError = 2001 ErrCodeUnauthorized = 2002 ErrCodeNotFound = 2003 ErrCodeRateLimit = 2004 ) // 预定义错误 var ( ErrUnknown = NewSDKError(ErrCodeUnknown, "未知错误", nil) ErrInvalidParam = NewSDKError(ErrCodeInvalidParam, "参数无效", nil) ErrSignatureFailed = NewSDKError(ErrCodeSignatureFailed, "签名失败", nil) ErrNetworkError = NewSDKError(ErrCodeNetworkError, "网络错误", nil) ErrRequestTimeout = NewSDKError(ErrCodeRequestTimeout, "请求超时", nil) ErrServerError = NewSDKError(ErrCodeServerError, "服务端错误", nil) ErrUnauthorized = NewSDKError(ErrCodeUnauthorized, "未授权", nil) ErrNotFound = NewSDKError(ErrCodeNotFound, "未找到", nil) ErrRateLimit = NewSDKError(ErrCodeRateLimit, "请求频率限制", nil) ) // File: intelligence_finance_v1/client.go package intelligence_finance_v1 import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "time" ) // Client 业财连接SDK客户端 type Client struct { baseURL string tenantID string clientID string clientSecret string httpClient *http.Client debug bool } // ClientOption 客户端配置选项 type ClientOption func(*Client) // WithHTTPClient 设置自定义HTTP客户端 func WithHTTPClient(httpClient *http.Client) ClientOption { return func(c *Client) { c.httpClient = httpClient } } // WithDebug 开启调试模式 func WithDebug(debug bool) ClientOption { return func(c *Client) { c.debug = debug } } // WithTimeout 设置请求超时时间 func WithTimeout(timeout time.Duration) ClientOption { return func(c *Client) { c.httpClient.Timeout = timeout } } // NewClient 创建新的SDK客户端 // - baseURL: API基础地址 // - tenantID: 平台分配的租户唯一标识 // - clientID: 平台分配的应用标识 // - clientSecret: 平台分配的客户端密钥 // - opts: 可选配置项 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, }, } for _, opt := range opts { opt(c) } return c } // doRequest 发送HTTP请求,自动添加签名和认证头 func (c *Client) doRequest(ctx context.Context, path string, request interface{}, response interface{}) error { bodyBytes, err := json.Marshal(request) if err != nil { return NewSDKError(ErrCodeInvalidParam, "请求序列化失败", err) } if c.debug { fmt.Printf("[DEBUG] Request URL: %s%s\n", c.baseURL, path) fmt.Printf("[DEBUG] Request Body: %s\n", string(bodyBytes)) } // 生成时间戳和随机数 timestamp := GenerateTimestamp() nonce, err := GenerateNonce(16) if err != nil { return NewSDKError(ErrCodeSignatureFailed, "生成随机数失败", err) } // 签名数据:timestamp + nonce signData := timestamp + nonce signature := HmacSHA256Base64(c.clientSecret, signData) req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.baseURL+path, bytes.NewReader(bodyBytes)) if err != nil { return NewSDKError(ErrCodeNetworkError, "创建请求失败", err) } req.Header.Set("Content-Type", "application/json") req.Header.Set("tenant-id", c.tenantID) req.Header.Set("client-id", c.clientID) req.Header.Set("x-bfl-signature-timestamp", timestamp) req.Header.Set("x-bfl-signature-nonce", nonce) req.Header.Set("x-bfl-signature", signature) if c.debug { fmt.Printf("[DEBUG] Headers: tenant-id=%s, client-id=%s, x-bfl-signature=%s\n", c.tenantID, c.clientID, signature) } resp, err := c.httpClient.Do(req) if err != nil { return NewSDKError(ErrCodeNetworkError, "请求发送失败", err) } defer resp.Body.Close() respBody, err := io.ReadAll(resp.Body) if err != nil { return NewSDKError(ErrCodeNetworkError, "读取响应失败", err) } if c.debug { fmt.Printf("[DEBUG] Response Status: %s\n", resp.Status) fmt.Printf("[DEBUG] Response Body: %s\n", string(respBody)) } // 解析通用响应 var commonResp CommonResponse if err := json.Unmarshal(respBody, &commonResp); err != nil { return NewSDKError(ErrCodeUnknown, "响应解析失败", err) } if commonResp.Code != 0 { return NewSDKError(commonResp.Code, commonResp.Msg, nil) } // 如果response不为nil,将commonResp.Data反序列化到response if response != nil { if err := json.Unmarshal(commonResp.Data, response); err != nil { return NewSDKError(ErrCodeUnknown, "响应数据解析失败", err) } } return nil } // ============================================================ // 接口1:订单开票 // ============================================================ // CreateInvoice 提交订单开票申请 // 文档:接口1 - 提交订单开票申请 func (c *Client) CreateInvoice(ctx context.Context, req *CreateInvoiceRequest) (*CreateInvoiceResponse, error) { path := "/api/v1/invoice/create" var resp CreateInvoiceResponse if err := c.doRequest(ctx, path, req, &resp); err != nil { return nil, err } return &resp, nil } // ============================================================ // 接口2:开票状态查询 // ============================================================ // QueryInvoiceStatus 查询订单开票状态 // 文档:接口2 - 查询订单开票状态 func (c *Client) QueryInvoiceStatus(ctx context.Context, req *QueryInvoiceStatusRequest) (*QueryInvoiceStatusResponse, error) { path := "/api/v1/invoice/query" var resp QueryInvoiceStatusResponse if err := c.doRequest(ctx, path, req, &resp); err != nil { return nil, err } return &resp, nil } // ============================================================ // 接口3:创建付款单据 // ============================================================ // CreatePayment 创建付款单据 // 文档:接口3 - 创建付款单据 func (c *Client) CreatePayment(ctx context.Context, req *CreatePaymentRequest) (*CreatePaymentResponse, error) { path := "/api/v1/payment/create" var resp CreatePaymentResponse if err := c.doRequest(ctx, path, req, &resp); err != nil { return nil, err } return &resp, nil } // ============================================================ // 接口4:支付完成通知(平台回调客户) // 此接口由客户提供,平台调用。客户需实现HTTP Handler处理回调。 // ============================================================ // HandlePaymentNotification 处理支付完成通知 // 文档:接口4 - 支付完成通知回调处理 // 该函数用于解析平台发起的支付完成通知请求,返回SUCCESS或FAILED // 注意:平台回调时,请求体为 NotificationRequest 结构,其中 Data 字段为 PaymentNotification 的JSON字符串 func HandlePaymentNotification(body []byte) (string, error) { // 先解析外层通知结构 var notificationReq NotificationRequest if err := json.Unmarshal(body, ¬ificationReq); err != nil { return "FAILED", NewSDKError(ErrCodeInvalidParam, "解析通知请求失败", err) } // 根据bizType处理不同业务 switch notificationReq.BizType { case "payment_notification": // 解析内部支付通知数据 var paymentNotification PaymentNotification if err := json.Unmarshal([]byte(notificationReq.Data), &paymentNotification); err != nil { return "FAILED", NewSDKError(ErrCodeInvalidParam, "解析支付通知数据失败", err) } // 业务处理由调用方自行实现,此处仅返回成功 // 调用方应在此函数基础上扩展业务逻辑 _ = paymentNotification default: // 未知业务类型,记录日志后返回失败 return "FAILED", NewSDKError(ErrCodeInvalidParam, "未知业务类型: "+notificationReq.BizType, nil) } return "SUCCESS", nil } // ============================================================ // 接口5:支付状态查询 // ============================================================ // QueryPaymentStatus 查询支付状态 // 文档:接口5 - 查询支付状态 func (c *Client) QueryPaymentStatus(ctx context.Context, req *QueryPaymentStatusRequest) (*QueryPaymentStatusResponse, error) { path := "/api/v1/payment/query" var resp QueryPaymentStatusResponse if err := c.doRequest(ctx, path, req, &resp); err != nil { return nil, err } return &resp, nil } // ============================================================ // 接口6:创建供应商 // ============================================================ // CreateSupplier 创建供应商 // 文档:接口6 - 创建供应商 func (c *Client) CreateSupplier(ctx context.Context, req *CreateSupplierRequest) (*CreateSupplierResponse, error) { path := "/api/v1/supplier/create" var resp CreateSupplierResponse if err := c.doRequest(ctx, path, req, &resp); err != nil { return nil, err } return &resp, nil } // ============================================================ // 接口7:根据名称查询供应商 // ============================================================ // QuerySupplierByName 根据供应商名称查询供应商 // 文档:接口7 - 根据名称查询供应商 func (c *Client) QuerySupplierByName(ctx context.Context, req *QuerySupplierByNameRequest) (*QuerySupplierByNameResponse, error) { path := "/api/v1/supplier/queryByName" var resp QuerySupplierByNameResponse if err := c.doRequest(ctx, path, req, &resp); err != nil { return nil, err } return &resp, nil } // ============================================================ // 接口8:更新供应商 // ============================================================ // UpdateSupplier 更新供应商信息 // 文档:接口8 - 更新供应商 func (c *Client) UpdateSupplier(ctx context.Context, req *UpdateSupplierRequest) (*UpdateSupplierResponse, error) { path := "/api/v1/supplier/update" var resp UpdateSupplierResponse if err := c.doRequest(ctx, path, req, &resp); err != nil { return nil, err } return &resp, nil } // ============================================================ // 接口9:根据用户自定义编码查询供应商 // ============================================================ // QuerySupplierByUserDefineCode 根据用户自定义编码查询供应商 // 文档:接口9 - 根据用户自定义编码查询供应商 func (c *Client) QuerySupplierByUserDefineCode(ctx context.Context, req *QuerySupplierByUserDefineCodeRequest) (*QuerySupplierByUserDefineCodeResponse, error) { path := "/api/v1/supplier/queryByCode" var resp QuerySupplierByUserDefineCodeResponse if err := c.doRequest(ctx, path, req, &resp); err != nil { return nil, err } return &resp, nil } // File: intelligence_finance_v1/example_test.go package intelligence_finance_v1 import ( "context" "encoding/json" "fmt" "log" "time" ) // ExampleNewClient 展示了如何创建SDK客户端 func ExampleNewClient() { // 创建客户端 client := NewClient( "https://api.example.com", "your-tenant-id", "dd-ai-table", "your-client-secret", WithTimeout(30*time.Second), WithDebug(true), ) _ = client fmt.Println("Client created successfully") } // ExampleClient_CreateInvoice 展示了如何提交订单开票申请 func ExampleClient_CreateInvoice() { client := NewClient( "https://api.example.com", "your-tenant-id", "dd-ai-table", "your-client-secret", ) req := &CreateInvoiceRequest{ OrderID: "ORDER20240101001", InvoiceType: 9, // 数电普票 Purchaser: "测试购买方有限公司", TaxNum: "91110108MA12345678", Phone: "13800138000", Email: "test@example.com", Products: []InvoiceProduct{ { ProductName: "办公用品", RevenueCode: "1090515010000000000", AmountIncludeTax: 1130.00, Quantity: 10, Unit: "个", TaxRate: 0.13, TaxSign: 1, Discount: 0, }, }, ApplyPerson: "张三", Payee: "李四", Reviewer: "王五", } resp, err := client.CreateInvoice(context.Background(), req) if err != nil { log.Fatalf("创建开票申请失败: %v", err) } fmt.Printf("开票状态: %d, 错误信息: %s\n", resp.Status, resp.ErrorMsg) for i, inv := range resp.DataList { fmt.Printf("发票%d: 代码=%s, 号码=%s, 金额(含税)=%s\n", i+1, inv.InvoiceCode, inv.InvoiceNo, inv.TotalIncludeTax) } } // ExampleClient_QueryInvoiceStatus 展示了如何查询开票状态 func ExampleClient_QueryInvoiceStatus() { client := NewClient( "https://api.example.com", "your-tenant-id", "dd-ai-table", "your-client-secret", ) req := &QueryInvoiceStatusRequest{ OrderID: "ORDER20240101001", } resp, err := client.QueryInvoiceStatus(context.Background(), req) if err != nil { log.Fatalf("查询开票状态失败: %v", err) } fmt.Printf("开票状态: %d, 描述: %s\n", resp.Status, resp.Message) } // ExampleClient_CreatePayment 展示了如何创建付款单据 func ExampleClient_CreatePayment() { client := NewClient( "https://api.example.com", "your-tenant-id", "dd-ai-table", "your-client-secret", ) req := &CreatePaymentRequest{ Code: "PAY20240101001", UserID: "user123", Title: "采购付款-2024年1月", Amount: "50000.00", Supplier: &Supplier{ Name: "供应商A", }, Department: &Department{ Name: "财务部", }, Company: &Company{ Name: "XX科技有限公司", }, PaymentDetailList: []PaymentDetail{ { Amount: "50000.00", Remark: "1月采购货款", }, }, } resp, err := client.CreatePayment(context.Background(), req) if err != nil { log.Fatalf("创建付款单据失败: %v", err) } fmt.Printf("付款单据编码: %s\n", resp.Code) } // ExampleHandlePaymentNotification 展示了如何处理支付完成通知 func ExampleHandlePaymentNotification() { // 模拟平台回调的请求体(符合NotificationRequest格式) notificationBody := `{ "bizType": "payment_notification", "bizId": "instance_001", "data": "{\"code\":\"PAY20240101001\",\"instanceId\":\"instance_001\",\"corpId\":\"dingXXXXXX\",\"paymentStatus\":\"SUCCESS\",\"paymentTime\":\"2024-01-15 10:30:00\",\"userId\":\"user123\",\"amount\":\"50000.00\",\"source\":\"openapi\"}" }` result, err := HandlePaymentNotification([]byte(notificationBody)) if err != nil { log.Printf("处理通知失败: %v", err) } fmt.Printf("通知处理结果: %s\n", result) // 实际业务中,可以在此处解析通知数据并执行后续操作 // 解析外层 var notificationReq NotificationRequest json.Unmarshal([]byte(notificationBody), ¬ificationReq) var paymentNotification PaymentNotification json.Unmarshal([]byte(notificationReq.Data), &paymentNotification) fmt.Printf("收到支付通知: 单据编码=%s, 支付状态=%s\n", paymentNotification.Code, paymentNotification.PaymentStatus) } // ExampleClient_QueryPaymentStatus 展示了如何查询支付状态 func ExampleClient_QueryPaymentStatus() { client := NewClient( "https://api.example.com", "your-tenant-id", "dd-ai-table", "your-client-secret", ) req := &QueryPaymentStatusRequest{ Code: "PAY20240101001", UserID: "user123", } resp, err := client.QueryPaymentStatus(context.Background(), req) if err != nil { log.Fatalf("查询支付状态失败: %v", err) } fmt.Printf("支付状态: %s, 支付时间: %s\n", resp.PaymentStatus, resp.PaymentTime) } // ExampleClient_CreateSupplier 展示了如何创建供应商 func ExampleClient_CreateSupplier() { client := NewClient( "https://api.example.com", "your-tenant-id", "dd-ai-table", "your-client-secret", ) bizData := CreateSupplierBizData{ CorpID: "dingXXXXXX", Creator: "user123", SupplierInfo: &SupplierInfo{ Name: "测试供应商有限公司", CorpID: "dingXXXXXX", Creator: "user123", UserDefineCode: "SUP-001", ContactName: "张三", ContactTelephone: "13800138000", ContactEmail: "contact@supplier.com", ContactAddress: "北京市朝阳区XX路XX号", }, } bizDataBytes, _ := json.Marshal(bizData) req := &CreateSupplierRequest{ InvokeID: "invoke_001", UserID: "user123", Data: string(bizDataBytes), BizType: "create_supplier", } resp, err := client.CreateSupplier(context.Background(), req) if err != nil { log.Fatalf("创建供应商失败: %v", err) } fmt.Printf("创建供应商响应: invokeId=%s, bizType=%s\n", resp.InvokeID, resp.BizType) // 如果需要解析响应数据,可以进一步处理 resp.Data var responseData CreateSupplierResponseData if err := json.Unmarshal(resp.Data, &responseData); err == nil { fmt.Printf("响应数据: success=%v, result=%s\n", responseData.Success, responseData.Result) } } // ExampleClient_QuerySupplierByName 展示了如何根据名称查询供应商 func ExampleClient_QuerySupplierByName() { client := NewClient( "https://api.example.com", "your-tenant-id", "dd-ai-table", "your-client-secret", ) bizData := []QuerySupplierByNameBizData{ { CorpID: "dingXXXXXX", Name: "测试供应商有限公司", }, } bizDataBytes, _ := json.Marshal(bizData) req := &QuerySupplierByNameRequest{ InvokeID: "invoke_002", UserID: "user123", Data: string(bizDataBytes), BizType: "query_supplier_by_name", } resp, err := client.QuerySupplierByName(context.Background(), req) if err != nil { log.Fatalf("查询供应商失败: %v", err) } fmt.Printf("查询结果: success=%v, result=%s\n", resp.Success, resp.Result) // 解析响应数据 var results []QuerySupplierByNameResult if err := json.Unmarshal(resp.Data, &results); err == nil { for _, r := range results { fmt.Printf("供应商: %s, ID: %s\n", r.SupplierName, r.SupplierID) } } } // ExampleClient_UpdateSupplier 展示了如何更新供应商 func ExampleClient_UpdateSupplier() { client := NewClient( "https://api.example.com", "your-tenant-id", "dd-ai-table", "your-client-secret", ) bizData := UpdateSupplierBizData{ SupplierID: "SUP_XXXXX", Name: "更新后的供应商名称", CorpID: "dingXXXXXX", UserID: "user123", ContactName: "李四", ContactTelephone: "13900139000", ContactEmail: "new@supplier.com", Description: "长期合作供应商", } bizDataBytes, _ := json.Marshal(bizData) req := &UpdateSupplierRequest{ InvokeID: "invoke_003", UserID: "user123", Data: string(bizDataBytes), BizType: "update_supplier", } resp, err := client.UpdateSupplier(context.Background(), req) if err != nil { log.Fatalf("更新供应商失败: %v", err) } fmt.Printf("更新供应商响应: invokeId=%s, bizType=%s\n", resp.InvokeID, resp.BizType) } // ExampleClient_QuerySupplierByUserDefineCode 展示了如何根据自定义编码查询供应商 func ExampleClient_QuerySupplierByUserDefineCode() { client := NewClient( "https://api.example.com", "your-tenant-id", "dd-ai-table", "your-client-secret", ) bizData := QuerySupplierByUserDefineCodeBizData{ CorpID: "dingXXXXXX", UserDefineCode: "SUP-001", } bizDataBytes, _ := json.Marshal(bizData) req := &QuerySupplierByUserDefineCodeRequest{ InvokeID: "invoke_004", UserID: "user123", Data: string(bizDataBytes), BizType: "query_supplier_by_userDefineCode", } resp, err := client.QuerySupplierByUserDefineCode(context.Background(), req) if err != nil { log.Fatalf("查询供应商失败: %v", err) } fmt.Printf("查询供应商响应: invokeId=%s, bizType=%s\n", resp.InvokeID, resp.BizType) // 解析响应数据 var responseData QuerySupplierByUserDefineCodeResponseData if err := json.Unmarshal(resp.Data, &responseData); err == nil { fmt.Printf("响应数据: success=%v\n", responseData.Success) } }