// File: intelligence_finance_v1_ga/go.mod ```go module intelligence_finance_v1_ga go 1.21 ``` // File: intelligence_finance_v1_ga/errors.go ```go package intelligence_finance_v1_ga import "fmt" // APIError 表示 HTTP 层面的错误(非 200 响应)。 type APIError struct { // StatusCode HTTP 状态码 StatusCode int // Body 响应体原始内容 Body string } // Error 实现 error 接口。 func (e *APIError) Error() string { return fmt.Sprintf("api error: status=%d body=%s", e.StatusCode, e.Body) } // BusinessError 表示业务层面的错误(通用响应结构中 code 非成功值)。 type BusinessError struct { // Code 业务错误码 Code int // Msg 业务错误描述 Msg string } // Error 实现 error 接口。 func (e *BusinessError) Error() string { return fmt.Sprintf("business error: code=%d msg=%s", e.Code, e.Msg) } // NotificationError 表示通知处理失败。 type NotificationError struct { // Reason 失败原因 Reason string } // Error 实现 error 接口。 func (e *NotificationError) Error() string { return fmt.Sprintf("notification error: %s", e.Reason) } ``` // File: intelligence_finance_v1_ga/crypto.go ```go package intelligence_finance_v1_ga 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()) } // 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 进行签名,密钥为 key,结果 Base64 编码。 // 对应文档中客户应用签名算法:HmacSHA256,密钥为 client-secret, // 签名数据为 x-bfl-signature-timestamp + x-bfl-signature-nonce。 func HmacSHA256Base64(key, data string) string { h := hmac.New(sha256.New, []byte(key)) h.Write([]byte(data)) return base64.StdEncoding.EncodeToString(h.Sum(nil)) } // BuildSignString 方式1:字典序排序拼接,排除空值与签名字段本身。 func BuildSignString(params map[string]string) string { keys := make([]string, 0, len(params)) for k, v := range params { if v != "" && k != "sign" && k != "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 方式2:固定顺序拼接,排除空值。 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_ga/types.go ```go package intelligence_finance_v1_ga import "encoding/json" // ==================== 通用 ==================== // CommonResponse 通用响应结构。 type CommonResponse struct { Code int `json:"code"` Msg string `json:"msg"` Data json.RawMessage `json:"data"` } // ==================== 接口1:订单开票 ==================== // InvoiceApplyRequest 提交订单开票申请请求。 type InvoiceApplyRequest struct { // CompanyCode 开票的企业主体编码,不传则默认主体开票 CompanyCode string `json:"companyCode,omitempty"` // OrderID 订单唯一标识(需保证在贵方系统内唯一) OrderID string `json:"orderId"` // InvoiceType 发票类型枚举:1-专用发票;2-普通发票;3-普通发票(电子);4-专用发票(电子);8-数电专票;9-数电普票 InvoiceType int `json:"invoiceType"` // Products 货物/服务明细列表,至少一项 Products []ProductItem `json:"products"` // Remark 订单备注(非发票备注) Remark string `json:"remark,omitempty"` // Purchaser 购方企业名称 Purchaser string `json:"purchaser"` // TaxNum 购方纳税人识别号 TaxNum string `json:"taxnum,omitempty"` // PurchaserAddress 购方地址 PurchaserAddress string `json:"purchaserAddress,omitempty"` // PurchaserTel 购方电话 PurchaserTel string `json:"purchaserTel,omitempty"` // BankName 购方开户行名称 BankName string `json:"bankName,omitempty"` // BankAccount 购方银行账号 BankAccount string `json:"bankAccount,omitempty"` // Phone 收票人手机号(用于接收电票短信) Phone string `json:"phone,omitempty"` // Email 收票人邮箱(用于接收电票邮件) Email string `json:"email,omitempty"` // ApplyPerson 开票申请人名称 ApplyPerson string `json:"applyPerson,omitempty"` // Payee 收款人(发票票面) Payee string `json:"payee,omitempty"` // Reviewer 复核人(发票票面) Reviewer string `json:"reviewer,omitempty"` // InvoiceRemark 发票备注栏内容 InvoiceRemark string `json:"invoiceRemark,omitempty"` // NaturalPerson 购买方自然人标识:Y-是,N-否(默认N),数电票可选传 NaturalPerson string `json:"naturalPerson,omitempty"` // AdditionInfo 附加信息(JSON数组字符串) AdditionInfo string `json:"additionInfo,omitempty"` } // ProductItem 货物/服务明细项。 type ProductItem struct { // ProductName 货物或服务名称 ProductName string `json:"productName"` // RevenueCode 19位税收分类编码 RevenueCode string `json:"revenueCode"` // AmountIncludeTax 单条明细含税总金额(单位:元) AmountIncludeTax float64 `json:"amountIncludeTax"` // Specs 规格型号 Specs string `json:"specs,omitempty"` // Unit 计量单位(如:台、个、次) Unit string `json:"unit,omitempty"` // Quantity 数量 Quantity float64 `json:"quantity"` // Discount 折扣金额(无折扣传0) Discount float64 `json:"discount,omitempty"` // TaxSign 是否含税:0-不含税;1-含税(默认建议传1) TaxSign int `json:"taxSign,omitempty"` // TaxRate 税率(小数形式,如0.13表示13%) TaxRate float64 `json:"taxRate,omitempty"` } // InvoiceApplyResponse 提交订单开票申请响应。 type InvoiceApplyResponse struct { // Status 开票状态:0-未开票;1-开票中;2-部分失败;3-开票成功;4-开票失败;5-部分未开;6-未配置数电账号;7-未配置自动开票配置 Status int `json:"status"` // ErrorMsg 错误信息(开票失败时返回原因) ErrorMsg string `json:"errorMsg,omitempty"` // DataList 发票数据列表(一张订单可能对应多张发票) 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:开票状态查询 ==================== // InvoiceStatusQueryRequest 查询订单开票状态请求。 type InvoiceStatusQueryRequest struct { // OrderID 订单唯一标识(需保证在贵方系统内唯一) OrderID string `json:"orderId"` } // InvoiceStatusQueryResponse 查询订单开票状态响应。 type InvoiceStatusQueryResponse struct { // Status 开票状态(0-未开票,3-成功,4-失败,6-未配置数电账号,7-未配置自动开票) Status int `json:"status"` // Message 状态描述(如"开票成功") Message string `json:"message"` // Data 发票详细列表,结构与回调中的data字段一致 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"` PaymentDetailListJSON 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 单据唯一编码 Code string `json:"code"` } // ==================== 接口4:支付完成通知 ==================== // PaymentNotifyData 支付完成通知数据。 type PaymentNotifyData 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"` } // 支付状态枚举。 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" // 对私银行卡 ) // ==================== 接口5:支付状态查询 ==================== // PaymentStatusQueryRequest 查询支付状态请求。 type PaymentStatusQueryRequest struct { Code string `json:"code"` UserID string `json:"userId"` } // ==================== 通用通知机制 ==================== // Notification 通用通知数据。 type Notification struct { BizType string `json:"bizType"` BizID string `json:"bizId"` Data string `json:"data,omitempty"` } // 通知响应常量。 const ( NotifyResultSuccess = "SUCCESS" // 成功 NotifyResultFailed = "FAILED" // 失败 ) // ==================== 接口6:创建供应商 ==================== // CreateSupplierRequest 创建供应商请求。 type CreateSupplierRequest struct { InvokeID string `json:"invokeId,omitempty"` UserID string `json:"userId"` Data string `json:"data,omitempty"` BizType string `json:"bizType"` } // SupplierBizData 供应商业务数据。 type SupplierBizData 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 string `json:"data,omitempty"` } // SupplierResponseData 供应商响应数据。 type SupplierResponseData struct { Result string `json:"result,omitempty"` Success bool `json:"success"` } // SupplierResult 供应商信息(响应)。 type SupplierResult 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"` InvoiceName string `json:"invoiceName,omitempty"` InvoiceTaxNo string `json:"invoiceTaxNo,omitempty"` CreateTime int64 `json:"createTime,omitempty"` ModifiedTime int64 `json:"modifiedTime,omitempty"` Code string `json:"code,omitempty"` ID int64 `json:"id,omitempty"` Status string `json:"status,omitempty"` } // ==================== 接口7:根据名称查询供应商 ==================== // QuerySupplierByNameRequest 根据名称查询供应商请求。 type QuerySupplierByNameRequest struct { InvokeID string `json:"invokeId,omitempty"` UserID string `json:"userId"` Data string `json:"data,omitempty"` BizType string `json:"bizType"` } // QueryByName 按名称查询业务数据。 type QueryByName struct { CorpID string `json:"corpId"` Name string `json:"name"` } // QuerySupplierByNameResponse 根据名称查询供应商响应。 type QuerySupplierByNameResponse struct { InvokeID string `json:"invokeId,omitempty"` BizType string `json:"bizType"` Data string `json:"data,omitempty"` Result string `json:"result,omitempty"` Success bool `json:"success"` } // ==================== 接口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 string `json:"data,omitempty"` } // ==================== 接口9:根据用户自定义编码查询供应商 ==================== // QuerySupplierByUserDefineCodeRequest 根据用户自定义编码查询供应商请求。 type QuerySupplierByUserDefineCodeRequest struct { InvokeID string `json:"invokeId,omitempty"` UserID string `json:"userId"` Data string `json:"data,omitempty"` BizType string `json:"bizType"` } // QueryByUserDefineCode 按用户自定义编码查询业务数据。 type QueryByUserDefineCode struct { CorpID string `json:"corpId"` UserDefineCode string `json:"userDefineCode"` } // QuerySupplierByUserDefineCodeResponse 根据用户自定义编码查询供应商响应。 type QuerySupplierByUserDefineCodeResponse struct { InvokeID string `json:"invokeId,omitempty"` BizType string `json:"bizType"` Data string `json:"data,omitempty"` } ``` // File: intelligence_finance_v1_ga/client.go ```go package intelligence_finance_v1_ga import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "strings" "time" ) // 供应商业务类型常量。 const ( BizTypeCreateSupplier = "create_supplier" BizTypeQuerySupplierByName = "query_supplier_by_name" BizTypeUpdateSupplier = "update_supplier" BizTypeQuerySupplierByUserDefine = "query_supplier_by_userDefineCode" ) // Client 业财连接接口客户端。 type Client struct { baseURL string tenantID string clientID string clientSecret string signEnabled bool httpClient *http.Client // 各接口路径(可通过 Option 覆盖) pathSubmitInvoiceApply string pathQueryInvoiceStatus string pathCreatePayment string pathQueryPaymentStatus string pathCreateSupplier string pathQuerySupplierByName string pathUpdateSupplier string pathQuerySupplierByUserDefineCode string } // Option 客户端配置项。 type Option func(*Client) // WithHTTPClient 自定义 http.Client。 func WithHTTPClient(c *http.Client) Option { return func(cli *Client) { cli.httpClient = c } } // WithSignEnabled 是否启用请求签名(钉钉AI表格场景可关闭,签名由AI表格自动完成)。 func WithSignEnabled(enabled bool) Option { return func(cli *Client) { cli.signEnabled = enabled } } // WithPathSubmitInvoiceApply 设置提交订单开票申请接口路径。 func WithPathSubmitInvoiceApply(path string) Option { return func(cli *Client) { cli.pathSubmitInvoiceApply = path } } // WithPathQueryInvoiceStatus 设置开票状态查询接口路径。 func WithPathQueryInvoiceStatus(path string) Option { return func(cli *Client) { cli.pathQueryInvoiceStatus = path } } // WithPathCreatePayment 设置创建付款单据接口路径。 func WithPathCreatePayment(path string) Option { return func(cli *Client) { cli.pathCreatePayment = path } } // WithPathQueryPaymentStatus 设置支付状态查询接口路径。 func WithPathQueryPaymentStatus(path string) Option { return func(cli *Client) { cli.pathQueryPaymentStatus = path } } // WithPathCreateSupplier 设置创建供应商接口路径。 func WithPathCreateSupplier(path string) Option { return func(cli *Client) { cli.pathCreateSupplier = path } } // WithPathQuerySupplierByName 设置根据名称查询供应商接口路径。 func WithPathQuerySupplierByName(path string) Option { return func(cli *Client) { cli.pathQuerySupplierByName = path } } // WithPathUpdateSupplier 设置更新供应商接口路径。 func WithPathUpdateSupplier(path string) Option { return func(cli *Client) { cli.pathUpdateSupplier = path } } // WithPathQuerySupplierByUserDefineCode 设置根据用户自定义编码查询供应商接口路径。 func WithPathQuerySupplierByUserDefineCode(path string) Option { return func(cli *Client) { cli.pathQuerySupplierByUserDefineCode = path } } // NewClient 创建业财连接接口客户端。 // // baseURL 为平台接口根地址;tenantID 为平台分配的租户唯一标识; // clientID 为平台分配的应用标识(钉钉AI表格固定为 "dd-ai-table"); // clientSecret 为平台分配的密钥,用于请求签名。 func NewClient(baseURL, tenantID, clientID, clientSecret string, opts ...Option) *Client { c := &Client{ baseURL: strings.TrimRight(baseURL, "/"), tenantID: tenantID, clientID: clientID, clientSecret: clientSecret, signEnabled: true, httpClient: &http.Client{Timeout: 30 * time.Second}, pathSubmitInvoiceApply: "/invoice/apply", pathQueryInvoiceStatus: "/invoice/status", pathCreatePayment: "/payment/create", pathQueryPaymentStatus: "/payment/status", pathCreateSupplier: "/supplier/create", pathQuerySupplierByName: "/supplier/queryByName", pathUpdateSupplier: "/supplier/update", pathQuerySupplierByUserDefineCode: "/supplier/queryByUserDefineCode", } for _, opt := range opts { opt(c) } return c } // doRequest 发送 POST 请求并解析响应。 func (c *Client) doRequest(ctx context.Context, path string, reqBody, respBody interface{}) error { body, err := json.Marshal(reqBody) if err != nil { return fmt.Errorf("marshal request: %w", err) } httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, c.baseURL+path, bytes.NewReader(body)) if err != nil { return fmt.Errorf("new request: %w", err) } httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("tenant-id", c.tenantID) httpReq.Header.Set("client-id", c.clientID) if c.signEnabled { timestamp := GenerateTimestamp() nonce, err := GenerateNonce(32) if err != nil { return fmt.Errorf("generate nonce: %w", err) } sign := HmacSHA256Base64(c.clientSecret, timestamp+nonce) httpReq.Header.Set("x-bfl-signature-timestamp", timestamp) httpReq.Header.Set("x-bfl-signature-nonce", nonce) httpReq.Header.Set("x-bfl-signature", sign) } httpResp, err := c.httpClient.Do(httpReq) if err != nil { return fmt.Errorf("do request: %w", err) } defer httpResp.Body.Close() respData, err := io.ReadAll(httpResp.Body) if err != nil { return fmt.Errorf("read response: %w", err) } if httpResp.StatusCode != http.StatusOK { return &APIError{StatusCode: httpResp.StatusCode, Body: string(respData)} } if err := json.Unmarshal(respData, respBody); err != nil { return fmt.Errorf("unmarshal response: %w", err) } return nil } // checkBusinessCode 校验通用响应结构中的业务 code。 func checkBusinessCode(code int, msg string) error { if code != 0 && code != 200 { return &BusinessError{Code: code, Msg: msg} } return nil } // SubmitInvoiceApply 提交订单开票申请(接口1)。 func (c *Client) SubmitInvoiceApply(ctx context.Context, req *InvoiceApplyRequest) (*InvoiceApplyResponse, error) { var envelope struct { Code int `json:"code"` Msg string `json:"msg"` Data *InvoiceApplyResponse `json:"data"` } if err := c.doRequest(ctx, c.pathSubmitInvoiceApply, req, &envelope); err != nil { return nil, err } if err := checkBusinessCode(envelope.Code, envelope.Msg); err != nil { return nil, err } return envelope.Data, nil } // QueryInvoiceStatus 查询订单开票状态(接口2)。 func (c *Client) QueryInvoiceStatus(ctx context.Context, req *InvoiceStatusQueryRequest) (*InvoiceStatusQueryResponse, error) { var envelope struct { Code int `json:"code"` Msg string `json:"msg"` Data *InvoiceStatusQueryResponse `json:"data"` } if err := c.doRequest(ctx, c.pathQueryInvoiceStatus, req, &envelope); err != nil { return nil, err } if err := checkBusinessCode(envelope.Code, envelope.Msg); err != nil { return nil, err } return envelope.Data, nil } // CreatePayment 创建付款单据(接口3)。 func (c *Client) CreatePayment(ctx context.Context, req *CreatePaymentRequest) (*CreatePaymentResponse, error) { var envelope struct { Code int `json:"code"` Msg string `json:"msg"` Data *CreatePaymentResponse `json:"data"` } if err := c.doRequest(ctx, c.pathCreatePayment, req, &envelope); err != nil { return nil, err } if err := checkBusinessCode(envelope.Code, envelope.Msg); err != nil { return nil, err } return envelope.Data, nil } // QueryPaymentStatus 查询支付状态(接口5),响应数据与支付通知一致。 func (c *Client) QueryPaymentStatus(ctx context.Context, req *PaymentStatusQueryRequest) (*PaymentNotifyData, error) { var envelope struct { Code int `json:"code"` Msg string `json:"msg"` Data *PaymentNotifyData `json:"data"` } if err := c.doRequest(ctx, c.pathQueryPaymentStatus, req, &envelope); err != nil { return nil, err } if err := checkBusinessCode(envelope.Code, envelope.Msg); err != nil { return nil, err } return envelope.Data, nil } // CreateSupplier 创建供应商(接口6)。 func (c *Client) CreateSupplier(ctx context.Context, req *CreateSupplierRequest) (*CreateSupplierResponse, error) { var envelope struct { Code int `json:"code"` Msg string `json:"msg"` Data *CreateSupplierResponse `json:"data"` } if err := c.doRequest(ctx, c.pathCreateSupplier, req, &envelope); err != nil { return nil, err } if err := checkBusinessCode(envelope.Code, envelope.Msg); err != nil { return nil, err } return envelope.Data, nil } // QuerySupplierByName 根据名称查询供应商(接口7)。 func (c *Client) QuerySupplierByName(ctx context.Context, req *QuerySupplierByNameRequest) (*QuerySupplierByNameResponse, error) { var envelope struct { Code int `json:"code"` Msg string `json:"msg"` Data *QuerySupplierByNameResponse `json:"data"` } if err := c.doRequest(ctx, c.pathQuerySupplierByName, req, &envelope); err != nil { return nil, err } if err := checkBusinessCode(envelope.Code, envelope.Msg); err != nil { return nil, err } return envelope.Data, nil } // UpdateSupplier 更新供应商(接口8)。 func (c *Client) UpdateSupplier(ctx context.Context, req *UpdateSupplierRequest) (*UpdateSupplierResponse, error) { var envelope struct { Code int `json:"code"` Msg string `json:"msg"` Data *UpdateSupplierResponse `json:"data"` } if err := c.doRequest(ctx, c.pathUpdateSupplier, req, &envelope); err != nil { return nil, err } if err := checkBusinessCode(envelope.Code, envelope.Msg); err != nil { return nil, err } return envelope.Data, nil } // QuerySupplierByUserDefineCode 根据用户自定义编码查询供应商(接口9)。 func (c *Client) QuerySupplierByUserDefineCode(ctx context.Context, req *QuerySupplierByUserDefineCodeRequest) (*QuerySupplierByUserDefineCodeResponse, error) { var envelope struct { Code int `json:"code"` Msg string `json:"msg"` Data *QuerySupplierByUserDefineCodeResponse `json:"data"` } if err := c.doRequest(ctx, c.pathQuerySupplierByUserDefineCode, req, &envelope); err != nil { return nil, err } if err := checkBusinessCode(envelope.Code, envelope.Msg); err != nil { return nil, err } return envelope.Data, nil } // ==================== 通知机制辅助 ==================== // ParseNotification 解析通用通知数据。 func ParseNotification(body []byte) (*Notification, error) { var n Notification if err := json.Unmarshal(body, &n); err != nil { return nil, fmt.Errorf("parse notification: %w", err) } return &n, nil } // ParsePaymentNotify 解析支付完成通知数据(接口4)。 func ParsePaymentNotify(body []byte) (*PaymentNotifyData, error) { var n PaymentNotifyData if err := json.Unmarshal(body, &n); err != nil { return nil, fmt.Errorf("parse payment notify: %w", err) } return &n, nil } // VerifyNotificationSignature 校验通知签名。 // // 平台使用客户提供的验签密钥对原始请求体进行 HmacSHA256 签名并 Base64 编码, // 通过请求头 x-bfl-signature 传递。verifyKey 为客户在平台配置的验签密钥。 func VerifyNotificationSignature(verifyKey string, rawBody []byte, signature string) (bool, error) { if signature == "" { return false, &NotificationError{Reason: "empty signature"} } expected := HmacSHA256Base64(verifyKey, string(rawBody)) return hmacEqual(expected, signature), nil } // hmacEqual 常量时间比较两个 Base64 签名是否一致。 func hmacEqual(a, b string) bool { if len(a) != len(b) { return false } var v byte for i := 0; i < len(a); i++ { v |= a[i] ^ b[i] } return v == 0 } ``` // File: intelligence_finance_v1_ga/example_test.go ```go package intelligence_finance_v1_ga import ( "context" "encoding/json" "fmt" "testing" ) // ExampleClient_SubmitInvoiceApply 演示提交订单开票申请。 func ExampleClient_SubmitInvoiceApply() { ctx := context.Background() client := NewClient( "https://api.example.com", "your-tenant-id", "dd-ai-table", "your-client-secret", ) req := &InvoiceApplyRequest{ OrderID: "ORDER-20240101-001", InvoiceType: 9, // 数电普票 Purchaser: "某某科技有限公司", TaxNum: "91330100XXXXXXXXXX", Email: "finance@example.com", Phone: "13800000000", Products: []ProductItem{ { ProductName: "软件服务", RevenueCode: "3040201000000000000", AmountIncludeTax: 11300, Quantity: 1, TaxRate: 0.13, TaxSign: 1, }, }, } resp, err := client.SubmitInvoiceApply(ctx, req) if err != nil { fmt.Println("error:", err) return } fmt.Printf("status=%d errorMsg=%s\n", resp.Status, resp.ErrorMsg) // Output: } // ExampleClient_CreatePayment 演示创建付款单据。 func ExampleClient_CreatePayment() { ctx := context.Background() client := NewClient( "https://api.example.com", "your-tenant-id", "dd-ai-table", "your-client-secret", ) req := &CreatePaymentRequest{ Code: "PAY-20240101-001", UserID: "04221***", Title: "供应商货款", Amount: "11300.00", Supplier: &Supplier{ Name: "某某供应商", }, PaymentDetailList: []PaymentDetail{ { Amount: "11300.00", Tax: "1300.00", }, }, } resp, err := client.CreatePayment(ctx, req) if err != nil { fmt.Println("error:", err) return } fmt.Printf("code=%s\n", resp.Code) // Output: } // ExampleClient_CreateSupplier 演示创建供应商。 func ExampleClient_CreateSupplier() { ctx := context.Background() client := NewClient( "https://api.example.com", "your-tenant-id", "dd-ai-table", "your-client-secret", ) bizData := []SupplierBizData{ { CorpID: "dingXXXXXX", Creator: "04221***", SupplierInfo: &SupplierInfo{ Name: "某某供应商", CorpID: "dingXXXXXX", Creator: "04221***", ContactName: "张三", ContactTelephone: "18888888888", InvoiceName: "某某供应商", InvoiceTaxNo: "91330100XXXXXXXXXX", }, }, } dataBytes, _ := json.Marshal(bizData) req := &CreateSupplierRequest{ InvokeID: "REQ-001", UserID: "04221***", Data: string(dataBytes), BizType: BizTypeCreateSupplier, } resp, err := client.CreateSupplier(ctx, req) if err != nil { fmt.Println("error:", err) return } fmt.Printf("bizType=%s data=%s\n", resp.BizType, resp.Data) // Output: } // ExampleClient_QuerySupplierByName 演示根据名称查询供应商。 func ExampleClient_QuerySupplierByName() { ctx := context.Background() client := NewClient( "https://api.example.com", "your-tenant-id", "dd-ai-table", "your-client-secret", ) queries := []QueryByName{ {CorpID: "dingXXXXXX", Name: "某某供应商"}, } dataBytes, _ := json.Marshal(queries) req := &QuerySupplierByNameRequest{ InvokeID: "REQ-002", UserID: "04221***", Data: string(dataBytes), BizType: BizTypeQuerySupplierByName, } resp, err := client.QuerySupplierByName(ctx, req) if err != nil { fmt.Println("error:", err) return } fmt.Printf("success=%v result=%s\n", resp.Success, resp.Result) // Output: } // ExampleClient_UpdateSupplier 演示更新供应商。 func ExampleClient_UpdateSupplier() { ctx := context.Background() client := NewClient( "https://api.example.com", "your-tenant-id", "dd-ai-table", "your-client-secret", ) bizData := UpdateSupplierBizData{ SupplierID: "SUP_XXXXX", Name: "某某供应商", CorpID: "dingXXXXXX", UserID: "04221***", ContactName: "李四", ContactTelephone: "18888888888", AccountType: AccountTypeCorpBankCard, } dataBytes, _ := json.Marshal(bizData) req := &UpdateSupplierRequest{ InvokeID: "REQ-003", UserID: "04221***", Data: string(dataBytes), BizType: BizTypeUpdateSupplier, } resp, err := client.UpdateSupplier(ctx, req) if err != nil { fmt.Println("error:", err) return } fmt.Printf("bizType=%s\n", resp.BizType) // Output: } // ExampleClient_QuerySupplierByUserDefineCode 演示根据用户自定义编码查询供应商。 func ExampleClient_QuerySupplierByUserDefineCode() { ctx := context.Background() client := NewClient( "https://api.example.com", "your-tenant-id", "dd-ai-table", "your-client-secret", ) bizData := QueryByUserDefineCode{ CorpID: "dingXXXXXX", UserDefineCode: "SUP-CODE-001", } dataBytes, _ := json.Marshal(bizData) req := &QuerySupplierByUserDefineCodeRequest{ InvokeID: "REQ-004", UserID: "04221***", Data: string(dataBytes), BizType: BizTypeQuerySupplierByUserDefine, } resp, err := client.QuerySupplierByUserDefineCode(ctx, req) if err != nil { fmt.Println("error:", err) return } fmt.Printf("bizType=%s data=%s\n", resp.BizType, resp.Data) // Output: } // TestVerifyNotificationSignature 演示通知验签。 func TestVerifyNotificationSignature(t *testing.T) { verifyKey := "customer-verify-secret" rawBody := []byte(`{"bizType":"payment","bizId":"PAY-001","data":"{}"}`) sign := HmacSHA256Base64(verifyKey, string(rawBody)) ok, err := VerifyNotificationSignature(verifyKey, rawBody, sign) if err != nil { t.Fatalf("verify error: %v", err) } if !ok { t.Fatal("signature mismatch") } } // TestParsePaymentNotify 演示解析支付完成通知。 func TestParsePaymentNotify(t *testing.T) { body := []byte(`{ "code":"PAY-001", "instanceId":"inst-001", "corpId":"dingXXXXXX", "paymentStatus":"SUCCESS", "paymentTime":"2024-01-01 10:00:00", "userId":"04221***", "amount":"11300.00" }`) n, err := ParsePaymentNotify(body) if err != nil { t.Fatalf("parse error: %v", err) } if n.PaymentStatus != PaymentStatusSuccess { t.Fatalf("unexpected status: %s", n.PaymentStatus) } } ``` === SDK 生成完成 ===