Now I'll generate all 6 files for the complete Go SDK. // 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" // ErrorCode 表示 API 返回的错误码 type ErrorCode int // APIError 表示 API 调用返回的业务错误 type APIError struct { Code int `json:"code"` Message string `json:"msg"` } // Error 实现 error 接口 func (e *APIError) Error() string { return fmt.Sprintf("API error: code=%d, msg=%s", e.Code, e.Message) } // NewAPIError 创建一个新的 APIError func NewAPIError(code int, msg string) *APIError { return &APIError{ Code: code, Message: msg, } } // 开票状态常量 const ( InvoiceStatusNotInvoiced = 0 // 未开票 InvoiceStatusInvoicing = 1 // 开票中 InvoiceStatusPartiallyFailed = 2 // 部分失败 InvoiceStatusSuccess = 3 // 开票成功 InvoiceStatusFailed = 4 // 开票失败 InvoiceStatusPartiallyUnsent = 5 // 部分未开 InvoiceStatusNoAccount = 6 // 未配置数电账号 InvoiceStatusNoAutoConfig = 7 // 未配置自动开票配置 ) // 发票类型常量 const ( InvoiceTypeSpecial = 1 // 专用发票 InvoiceTypeNormal = 2 // 普通发票 InvoiceTypeNormalElectronic = 3 // 普通发票(电子) InvoiceTypeSpecialElectronic = 4 // 专用发票(电子) InvoiceTypeDigitalSpecial = 8 // 数电专票 InvoiceTypeDigitalNormal = 9 // 数电普票 ) // 支付状态常量 const ( PaymentStatusSuccess = "SUCCESS" // 支付成功 PaymentStatusFail = "FAIL" // 支付失败 PaymentStatusTerminate = "TERMINATE" // 支付取消 PaymentStatusWaitPay = "WAIT_PAY" // 待支付 PaymentStatusPaying = "PAYING" // 支付中 PaymentStatusPartSuccess = "PART_SUCCESS" // 部分支付成功 PaymentStatusRefund = "REFUND" // 退款 ) // 账户类型常量 const ( AccountTypeAlipay = "ALIPAY" // 支付宝 AccountTypeBankCard = "BANKCARD" // 银行卡 AccountTypeCorpBankCard = "CORP_BANK_CARD" // 对公银行卡 AccountTypePersonalBankCard = "PERSONAL_BANK_CARD" // 对私银行卡 ) // 通知响应常量 const ( NotificationSuccess = "SUCCESS" NotificationFailed = "FAILED" ) // 发票状态常量(发票自身状态) const ( InvStatusNormal = "1" // 正常 InvStatusRedacted = "2" // 已红冲 InvStatusVoided = "3" // 已作废 ) // 开票类型 const ( IssueTypeNormal = "0" // 正数票 IssueTypeNegative = "1" // 负数票(红冲) ) // 来源枚举 const ( SourceApproval = "approval" // 审批单 SourceOpenAPI = "openapi" // 开发接口 ) ``` // File: intelligence_finance_v1/crypto.go ```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()) } // GenerateTimestampMillis 生成毫秒级时间戳字符串 func GenerateTimestampMillis() string { return fmt.Sprintf("%d", time.Now().UnixMilli()) } // GenerateNonce 生成指定长度的加密安全随机字符串 // length 建议为 16-32 位 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 "", fmt.Errorf("generate nonce: %w", err) } b[i] = charset[num.Int64()] } return string(b), nil } // HmacSHA256Sign 使用 HmacSHA256 算法对数据进行签名,返回 Base64 编码的签名字符串 // 密钥为 client-secret,签名数据为 timestamp + nonce func HmacSHA256Sign(secret string, data string) string { h := hmac.New(sha256.New, []byte(secret)) h.Write([]byte(data)) return base64.StdEncoding.EncodeToString(h.Sum(nil)) } // HmacSHA256Verify 验证 HmacSHA256 签名 func HmacSHA256Verify(secret string, data string, signature string) bool { expected := HmacSHA256Sign(secret, data) return hmac.Equal([]byte(expected), []byte(signature)) } // BuildSignString 按字典序排序拼接参数,格式为 key=value&key2=value2 // 排除空值、sign 和 signature 字段 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 按固定顺序拼接参数 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 ```go package intelligence_finance_v1 // ============================================================================ // 通用结构 // ============================================================================ // commonResponse 通用 API 响应结构(内部使用) type commonResponse struct { Code int `json:"code"` Msg string `json:"msg"` Data any `json:"data"` } // ============================================================================ // 订单开票(接口 1) // ============================================================================ // InvoiceRequest 订单开票请求参数 type InvoiceRequest 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 string `json:"amountIncludeTax"` // Specs 规格型号 Specs string `json:"specs,omitempty"` // Unit 计量单位(如:台、个、次) Unit string `json:"unit,omitempty"` // Quantity 数量 Quantity string `json:"quantity"` // Discount 折扣金额(无折扣传0) Discount string `json:"discount,omitempty"` // TaxSign 是否含税:0-不含税;1-含税(默认建议传1) TaxSign int `json:"taxSign,omitempty"` // TaxRate 税率(小数形式,如0.13表示13%) TaxRate string `json:"taxRate,omitempty"` } // InvoiceResponse 订单开票响应数据 type InvoiceResponse 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 税控设备号 DeviceCode string `json:"deviceCode"` // Drawer 开票人 Drawer string `json:"drawer"` // Email 邮箱(购方邮箱) Email string `json:"email"` // InvoiceType 发票类型:1-专用发票;2-普通发票;3-普通发票(电子);4-专用发票(电子);8-数电专票;9-数电普票 InvoiceType string `json:"invoiceType"` // IssueType 开票类型:0-正数票;1-负数票(红冲) IssueType string `json:"issueType"` // ListFlag 清单标识:0-无清单;1-有清单 ListFlag string `json:"listFlag"` // Mobile 手机号(购方手机) Mobile string `json:"mobile"` // OriginalInvCode 红冲时对应的原蓝票代码 OriginalInvCode string `json:"originalInvCode,omitempty"` // OriginalInvNo 红冲时对应的原蓝票号码 OriginalInvNo string `json:"originalInvNo,omitempty"` // AdditionInfo 数电发票备注栏的附加信息部分 AdditionInfo string `json:"additionInfo,omitempty"` // Payee 收款人 Payee string `json:"payee"` // PurchaserAddress 购方地址 PurchaserAddress string `json:"purchaserAddress"` // PurchaserBankAccount 购方银行账号 PurchaserBankAccount string `json:"purchaserBankAccount"` // PurchaserBankName 购方开户行 PurchaserBankName string `json:"purchaserBankName"` // PurchaserName 购方名称 PurchaserName string `json:"purchaserName"` // PurchaserTaxNo 购方税号 PurchaserTaxNo string `json:"purchaserTaxNo"` // PurchaserTel 购方电话 PurchaserTel string `json:"purchaserTel"` // NaturalPerson 购买方自然人标识:Y-是;N-否 NaturalPerson string `json:"naturalPerson,omitempty"` // Remark 备注 Remark string `json:"remark"` // Reviewer 复核人 Reviewer string `json:"reviewer"` // SellerAddress 销方地址 SellerAddress string `json:"sellerAddress,omitempty"` // SellerBankAccount 销方开户账号 SellerBankAccount string `json:"sellerBankAccount"` // SellerBankName 销方开户行 SellerBankName string `json:"sellerBankName"` // SellerName 销方名称 SellerName string `json:"sellerName"` // CheckCode 校验码 CheckCode string `json:"checkCode"` // CipherText 密码区 CipherText string `json:"cipherText"` // DrewDate 开票日期(格式:yyyy-MM-dd HH:mm:ss) DrewDate string `json:"drewDate,omitempty"` // InvoiceCode 发票代码 InvoiceCode string `json:"invoiceCode"` // InvoiceNo 发票号码 InvoiceNo string `json:"invoiceNo"` // InvoiceStatus 发票状态:1-正常;2-已红冲;3-已作废 InvoiceStatus string `json:"invoiceStatus"` // LayoutFileURL 电子发票地址(PDF/OFD) LayoutFileURL string `json:"layoutFileUrl,omitempty"` // PDFURL 电子发票PDF地址 PDFURL string `json:"pdfUrl,omitempty"` // OFDURL 电子发票OFD地址 OFDURL string `json:"ofdUrl,omitempty"` // XMLURL 电子发票XML数据地址 XMLURL string `json:"xmlUrl,omitempty"` // TotalExcludeTax 合计金额(不含税) TotalExcludeTax string `json:"totalExcludeTax"` // TotalIncludeTax 合计金额(含税) TotalIncludeTax string `json:"totalIncludeTax"` // TotalTaxAmount 合计税额 TotalTaxAmount string `json:"totalTaxAmount"` // LevyingType 征税方式 LevyingType string `json:"levyingType"` // Details 商品明细列表 Details []InvoiceDetail `json:"details"` } // InvoiceDetail 发票商品明细 type InvoiceDetail struct { // Amount 金额 Amount string `json:"amount,omitempty"` // Quantity 数量 Quantity string `json:"quantity,omitempty"` // DeductionAmount 扣除金额 DeductionAmount string `json:"deductionAmount,omitempty"` // TaxAmount 税额 TaxAmount string `json:"taxAmount,omitempty"` // ItemTitle 商品合并显示名称 ItemTitle string `json:"itemTitle"` // TaxCode 税收分类编码(19位) TaxCode string `json:"taxCode"` // ItemType 商品行性质:0-正常行;1-折扣行;2-被折扣行 ItemType string `json:"itemType"` // ItemName 商品简称 ItemName string `json:"itemName"` // Specs 商品规格型号 Specs string `json:"specs,omitempty"` // TaxFreePolicy 免税政策:1-免税;2-不征税;3-普通零税率 TaxFreePolicy string `json:"taxFreePolicy"` // PreferentialPolicy 优惠政策类型 PreferentialPolicy string `json:"preferentialPolicy"` // TaxRate 税率(小数形式,如0.13) TaxRate string `json:"taxRate"` // TaxSign 是否含税:0-否;1-是 TaxSign string `json:"taxSign"` // Unit 计量单位(如:台、个、次) Unit string `json:"unit,omitempty"` // UnitPrice 单价 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 []InvoiceData `json:"data"` } // ============================================================================ // 创建付款单据(接口 3) // ============================================================================ // PaymentDocumentRequest 创建付款单据请求参数 type PaymentDocumentRequest struct { // Code 单据编码 Code string `json:"code"` // YidaAppType 宜搭应用类型 YidaAppType string `json:"yidaAppType,omitempty"` // EmpAccountUserID 员工账号用户ID EmpAccountUserID string `json:"empAccountUserId,omitempty"` // Department 部门信息 Department *Department `json:"department,omitempty"` // Usage 用途 Usage string `json:"usage,omitempty"` // PaymentUserID 付款人用户ID PaymentUserID string `json:"paymentUserId,omitempty"` // Customer 客户信息 Customer *Customer `json:"customer,omitempty"` // PrincipalID 负责人ID PrincipalID string `json:"principalId,omitempty"` // Remark 备注 Remark string `json:"remark,omitempty"` // Supplier 供应商信息 Supplier *Supplier `json:"supplier,omitempty"` // Title 标题 Title string `json:"title,omitempty"` // Project 项目信息 Project *Project `json:"project,omitempty"` // PaymentUserIDListStr 付款人用户ID列表字符串 PaymentUserIDListStr string `json:"paymentUserIdListStr,omitempty"` // NeedPayment 是否需要付款 NeedPayment *bool `json:"needPayment,omitempty"` // PaymentDetailListJSONStr 付款明细列表JSON字符串 PaymentDetailListJSONStr string `json:"paymentDetailListJsonStr,omitempty"` // PaymentDetailList 付款明细列表 PaymentDetailList []PaymentDetail `json:"paymentDetailList,omitempty"` // Company 企业主体信息 Company *Company `json:"company,omitempty"` // Amount 金额 Amount string `json:"amount,omitempty"` // RecipientAccountInfo 收款账户信息 RecipientAccountInfo *RecipientAccount `json:"recipientAccountInfo,omitempty"` // EnterpriseAccount 企业账号信息 EnterpriseAccount *EnterpriseAccount `json:"enterpriseAccount,omitempty"` // Category 收支类别 Category []Category `json:"category,omitempty"` // UserID 用户ID UserID string `json:"userId"` // OccurDate 发生日期(时间戳,毫秒) OccurDate *int64 `json:"occurDate,omitempty"` // Product 商品信息 Product *Product `json:"product,omitempty"` // YidaFormUUID 宜搭表单UUID YidaFormUUID string `json:"yidaFormUuid,omitempty"` // CanEditPaymentInfo 是否可编辑付款信息 CanEditPaymentInfo *bool `json:"canEditPaymentInfo,omitempty"` // PaymentUserIDList 付款人用户ID列表 PaymentUserIDList []string `json:"paymentUserIdList,omitempty"` // YidaProcInsID 宜搭流程实例ID YidaProcInsID string `json:"yidaProcInsId,omitempty"` // SyncPaymentOrder 是否同步付款单 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"` } // PaymentDocumentResponse 创建付款单据响应数据 type PaymentDocumentResponse struct { // Code 单据唯一标识 Code string `json:"code"` } // ============================================================================ // 支付完成通知(接口 4)- 平台回调客户 // ============================================================================ // PaymentNotification 支付完成通知数据 type PaymentNotification struct { // Code 单据编码 Code string `json:"code"` // InstanceID 实例ID InstanceID string `json:"instanceId"` // CorpID 企业ID CorpID string `json:"corpId"` // PaymentStatus 支付状态 PaymentStatus string `json:"paymentStatus"` // PaymentTime 支付时间 PaymentTime string `json:"paymentTime"` // UserID 用户ID UserID string `json:"userId"` // FailReason 失败原因 FailReason string `json:"failReason,omitempty"` // PayerAccountInfo 付款账户信息 PayerAccountInfo *PayerAccountInfo `json:"payerAccountInfo,omitempty"` // PayeeAccountInfo 收款账户信息 PayeeAccountInfo *PayeeAccountInfo `json:"payeeAccountInfo,omitempty"` // RelatedRowNumberList 关联行号列表 RelatedRowNumberList []string `json:"relatedRowNumberList,omitempty"` // Source 来源 Source string `json:"source,omitempty"` // Template 模板 Template string `json:"template,omitempty"` // Amount 金额 Amount string `json:"amount,omitempty"` } // PayeeAccountInfo 收款账户信息 type PayeeAccountInfo struct { BankOpenDTO *BankOpenDTO `json:"bankOpenDTO,omitempty"` } // PayerAccountInfo 付款账户信息 type PayerAccountInfo struct { BankOpenDTO *BankOpenDTO `json:"bankOpenDTO,omitempty"` EnterpriseAccountCode string `json:"enterpriseAccountCode,omitempty"` AccountType string `json:"accountType,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) // ============================================================================ // PaymentStatusQueryRequest 支付状态查询请求参数 type PaymentStatusQueryRequest struct { // Code 单据编码 Code string `json:"code"` // UserID 用户ID UserID string `json:"userId"` } // PaymentStatusQueryResponse 支付状态查询响应(与支付通知数据结构一致) type PaymentStatusQueryResponse = PaymentNotification // ============================================================================ // 通用通知(回调) // ============================================================================ // GeneralNotification 通用通知数据 type GeneralNotification struct { // BizType 业务类型 BizType string `json:"bizType"` // BizID 业务ID BizID string `json:"bizId"` // Data 通知数据 Data string `json:"data,omitempty"` } ``` // File: intelligence_finance_v1/client.go ```go package intelligence_finance_v1 import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "time" ) const ( // DefaultTimeout 默认请求超时时间 DefaultTimeout = 30 * time.Second // DefaultNonceLength 默认随机数长度 DefaultNonceLength = 16 // HeaderTenantID 租户ID请求头 HeaderTenantID = "tenant-id" // HeaderClientID 应用标识请求头 HeaderClientID = "client-id" // HeaderSignatureTimestamp 签名时间戳请求头 HeaderSignatureTimestamp = "x-bfl-signature-timestamp" // HeaderSignatureNonce 签名随机数请求头 HeaderSignatureNonce = "x-bfl-signature-nonce" // HeaderSignature 签名信息请求头 HeaderSignature = "x-bfl-signature" // ClientIDDingTalk 钉钉AI表格的固定 client-id ClientIDDingTalk = "dd-ai-table" ) // Client 业财连接 SDK 客户端 // 用于调用平台提供的接口(订单开票、开票状态查询、创建付款单据、支付状态查询) // 以及处理平台回调通知 type Client struct { // BaseURL 平台接口基础地址 BaseURL string // TenantID 平台分配的租户唯一标识 TenantID string // ClientID 平台分配的应用标识 ClientID string // ClientSecret 平台分配的密钥,用于签名 ClientSecret string // HTTPClient HTTP 客户端 HTTPClient *http.Client // NonceLength 随机数长度 NonceLength int } // ClientOption 客户端配置选项 type ClientOption func(*Client) // WithHTTPClient 设置自定义 HTTP 客户端 func WithHTTPClient(httpClient *http.Client) ClientOption { return func(c *Client) { c.HTTPClient = httpClient } } // WithNonceLength 设置随机数长度 func WithNonceLength(length int) ClientOption { return func(c *Client) { c.NonceLength = length } } // NewClient 创建一个新的业财连接客户端 // baseURL: 平台接口基础地址(如 "https://api.example.com") // 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: DefaultTimeout, }, NonceLength: DefaultNonceLength, } for _, opt := range opts { opt(c) } return c } // NewDingTalkClient 创建一个钉钉AI表格专用的客户端 // baseURL: 平台接口基础地址 // tenantID: 平台分配的租户唯一标识 // clientSecret: 平台分配的密钥(在钉钉AI表格中配置为 APPSecret) // 注意:钉钉AI表格的 client-id 固定为 "dd-ai-table",签名由AI表格自动完成 func NewDingTalkClient(baseURL, tenantID, clientSecret string, opts ...ClientOption) *Client { return NewClient(baseURL, tenantID, ClientIDDingTalk, clientSecret, opts...) } // ============================================================================ // 签名相关方法 // ============================================================================ // buildSignature 构建请求签名 // 签名算法:HmacSHA256(client-secret, timestamp + nonce),结果 Base64 编码 func (c *Client) buildSignature(timestamp, nonce string) string { data := timestamp + nonce return HmacSHA256Sign(c.ClientSecret, data) } // setAuthHeaders 设置认证请求头 func (c *Client) setAuthHeaders(req *http.Request, timestamp, nonce string) { req.Header.Set(HeaderTenantID, c.TenantID) req.Header.Set(HeaderClientID, c.ClientID) req.Header.Set(HeaderSignatureTimestamp, timestamp) req.Header.Set(HeaderSignatureNonce, nonce) req.Header.Set(HeaderSignature, c.buildSignature(timestamp, nonce)) req.Header.Set("Content-Type", "application/json") } // ============================================================================ // 内部 HTTP 请求方法 // ============================================================================ // doRequest 执行带签名的 POST 请求 func (c *Client) doRequest(ctx context.Context, path string, requestBody interface{}) (*commonResponse, error) { bodyBytes, err := json.Marshal(requestBody) if err != nil { return nil, fmt.Errorf("marshal request body: %w", err) } url := c.BaseURL + path req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(bodyBytes)) if err != nil { return nil, fmt.Errorf("create request: %w", err) } timestamp := GenerateTimestamp() nonce, err := GenerateNonce(c.NonceLength) if err != nil { return nil, fmt.Errorf("generate nonce: %w", err) } c.setAuthHeaders(req, timestamp, nonce) resp, err := c.HTTPClient.Do(req) if err != nil { return nil, fmt.Errorf("execute request: %w", err) } defer resp.Body.Close() respBody, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("read response body: %w", err) } if resp.StatusCode < 200 || resp.StatusCode >= 300 { return nil, fmt.Errorf("unexpected HTTP status: %d, body: %s", resp.StatusCode, string(respBody)) } var commonResp commonResponse if err := json.Unmarshal(respBody, &commonResp); err != nil { return nil, fmt.Errorf("unmarshal response: %w", err) } if commonResp.Code != 0 { return nil, NewAPIError(commonResp.Code, commonResp.Msg) } return &commonResp, nil } // ============================================================================ // 接口 1:订单开票 // ============================================================================ // CreateInvoice 提交订单开票请求 // path: 接口路径(接口短码,由平台对接时分配) // req: 开票请求参数 // 返回开票响应,包含开票状态和发票数据列表 func (c *Client) CreateInvoice(ctx context.Context, path string, req *InvoiceRequest) (*InvoiceResponse, error) { commonResp, err := c.doRequest(ctx, path, req) if err != nil { return nil, err } // 将 data 字段重新序列化后反序列化为 InvoiceResponse dataBytes, err := json.Marshal(commonResp.Data) if err != nil { return nil, fmt.Errorf("marshal response data: %w", err) } var invoiceResp InvoiceResponse if err := json.Unmarshal(dataBytes, &invoiceResp); err != nil { return nil, fmt.Errorf("unmarshal invoice response: %w", err) } return &invoiceResp, nil } // ============================================================================ // 接口 2:开票状态查询 // ============================================================================ // QueryInvoiceStatus 查询订单开票状态 // path: 接口路径(接口短码,由平台对接时分配) // req: 查询请求参数 // 返回开票状态查询响应 func (c *Client) QueryInvoiceStatus(ctx context.Context, path string, req *InvoiceStatusQueryRequest) (*InvoiceStatusQueryResponse, error) { commonResp, err := c.doRequest(ctx, path, req) if err != nil { return nil, err } dataBytes, err := json.Marshal(commonResp.Data) if err != nil { return nil, fmt.Errorf("marshal response data: %w", err) } var statusResp InvoiceStatusQueryResponse if err := json.Unmarshal(dataBytes, &statusResp); err != nil { return nil, fmt.Errorf("unmarshal invoice status response: %w", err) } return &statusResp, nil } // ============================================================================ // 接口 3:创建付款单据 // ============================================================================ // CreatePaymentDocument 创建付款单据 // path: 接口路径(接口短码,由平台对接时分配) // req: 付款单据请求参数 // 返回创建结果,包含单据唯一标识 func (c *Client) CreatePaymentDocument(ctx context.Context, path string, req *PaymentDocumentRequest) (*PaymentDocumentResponse, error) { commonResp, err := c.doRequest(ctx, path, req) if err != nil { return nil, err } dataBytes, err := json.Marshal(commonResp.Data) if err != nil { return nil, fmt.Errorf("marshal response data: %w", err) } var paymentResp PaymentDocumentResponse if err := json.Unmarshal(dataBytes, &paymentResp); err != nil { return nil, fmt.Errorf("unmarshal payment document response: %w", err) } return &paymentResp, nil } // ============================================================================ // 接口 5:支付状态查询 // ============================================================================ // QueryPaymentStatus 查询支付状态 // path: 接口路径(接口短码,由平台对接时分配) // req: 查询请求参数 // 返回支付状态信息,结构与支付通知一致 func (c *Client) QueryPaymentStatus(ctx context.Context, path string, req *PaymentStatusQueryRequest) (*PaymentStatusQueryResponse, error) { commonResp, err := c.doRequest(ctx, path, req) if err != nil { return nil, err } dataBytes, err := json.Marshal(commonResp.Data) if err != nil { return nil, fmt.Errorf("marshal response data: %w", err) } var paymentStatusResp PaymentStatusQueryResponse if err := json.Unmarshal(dataBytes, &paymentStatusResp); err != nil { return nil, fmt.Errorf("unmarshal payment status response: %w", err) } return &paymentStatusResp, nil } // ============================================================================ // 回调通知处理(接口 4:支付完成通知) // ============================================================================ // ParsePaymentNotification 从 HTTP 请求中解析支付完成通知 // 该方法用于客户接收平台回调时使用 func ParsePaymentNotification(r *http.Request) (*PaymentNotification, error) { if r.Method != http.MethodPost { return nil, fmt.Errorf("invalid HTTP method: %s, expected POST", r.Method) } body, err := io.ReadAll(r.Body) if err != nil { return nil, fmt.Errorf("read notification body: %w", err) } defer r.Body.Close() var notification PaymentNotification if err := json.Unmarshal(body, ¬ification); err != nil { return nil, fmt.Errorf("unmarshal payment notification: %w", err) } return ¬ification, nil } // ParseGeneralNotification 从 HTTP 请求中解析通用通知 func ParseGeneralNotification(r *http.Request) (*GeneralNotification, error) { if r.Method != http.MethodPost { return nil, fmt.Errorf("invalid HTTP method: %s, expected POST", r.Method) } body, err := io.ReadAll(r.Body) if err != nil { return nil, fmt.Errorf("read notification body: %w", err) } defer r.Body.Close() var notification GeneralNotification if err := json.Unmarshal(body, ¬ification); err != nil { return nil, fmt.Errorf("unmarshal general notification: %w", err) } return ¬ification, nil } // VerifyNotificationSignature 验证回调通知签名 // payload: 原始请求体 // signature: 请求中的签名 // secret: 客户在平台配置的验签密钥 // 使用 HmacSHA256 算法验证 func VerifyNotificationSignature(payload []byte, signature, secret string) bool { return HmacSHA256Verify(secret, string(payload), signature) } // WriteNotificationResponse 写入通知响应 // success: true 返回 "SUCCESS",false 返回 "FAILED" func WriteNotificationResponse(w http.ResponseWriter, success bool) { w.Header().Set("Content-Type", "text/plain; charset=utf-8") if success { w.WriteHeader(http.StatusOK) w.Write([]byte(NotificationSuccess)) } else { w.WriteHeader(http.StatusOK) w.Write([]byte(NotificationFailed)) } } ``` // File: intelligence_finance_v1/example_test.go ```go package intelligence_finance_v1_test import ( "context" "fmt" "log" "net/http" "intelligence_finance_v1" ) // ExampleClient_CreateInvoice 演示如何提交订单开票请求 func ExampleClient_CreateInvoice() { // 创建客户端 client := intelligence_finance_v1.NewClient( "https://api.example.com", // 平台基础地址 "your-tenant-id", // 租户ID "your-client-id", // 应用标识 "your-client-secret", // 密钥 ) // 构建开票请求 req := &intelligence_finance_v1.InvoiceRequest{ OrderID: "ORDER-2024-001", InvoiceType: intelligence_finance_v1.InvoiceTypeDigitalNormal, // 数电普票 Purchaser: "测试购方企业", Taxnum: "91110000000000000X", Products: []intelligence_finance_v1.ProductItem{ { ProductName: "测试商品", RevenueCode: "1090101000000000000", AmountIncludeTax: "1130.00", Quantity: "1", Unit: "个", TaxSign: 1, TaxRate: "0.13", Discount: "0", }, }, Email: "test@example.com", Phone: "13800138000", } // 调用接口 ctx := context.Background() resp, err := client.CreateInvoice(ctx, "/api/invoice/create", req) if err != nil { log.Fatalf("创建发票失败: %v", err) } fmt.Printf("开票状态: %d\n", resp.Status) if resp.Status == intelligence_finance_v1.InvoiceStatusSuccess { for _, data := range resp.DataList { fmt.Printf("发票号码: %s, 发票代码: %s\n", data.InvoiceNo, data.InvoiceCode) } } } // ExampleClient_QueryInvoiceStatus 演示如何查询开票状态 func ExampleClient_QueryInvoiceStatus() { client := intelligence_finance_v1.NewClient( "https://api.example.com", "your-tenant-id", "your-client-id", "your-client-secret", ) req := &intelligence_finance_v1.InvoiceStatusQueryRequest{ OrderID: "ORDER-2024-001", } ctx := context.Background() resp, err := client.QueryInvoiceStatus(ctx, "/api/invoice/status", req) if err != nil { log.Fatalf("查询开票状态失败: %v", err) } fmt.Printf("状态: %d, 描述: %s\n", resp.Status, resp.Message) for _, data := range resp.Data { fmt.Printf("发票号码: %s, 发票状态: %s\n", data.InvoiceNo, data.InvoiceStatus) } } // ExampleClient_CreatePaymentDocument 演示如何创建付款单据 func ExampleClient_CreatePaymentDocument() { client := intelligence_finance_v1.NewClient( "https://api.example.com", "your-tenant-id", "your-client-id", "your-client-secret", ) needPayment := true req := &intelligence_finance_v1.PaymentDocumentRequest{ Code: "PAY-2024-001", UserID: "user123", Title: "测试付款单", Amount: "10000.00", Supplier: &intelligence_finance_v1.Supplier{ Name: "测试供应商", }, Company: &intelligence_finance_v1.Company{ Name: "测试企业", }, Department: &intelligence_finance_v1.Department{ Name: "财务部", }, NeedPayment: &needPayment, PaymentDetailList: []intelligence_finance_v1.PaymentDetail{ { Amount: "10000.00", Remark: "货款", }, }, } ctx := context.Background() resp, err := client.CreatePaymentDocument(ctx, "/api/payment/create", req) if err != nil { log.Fatalf("创建付款单据失败: %v", err) } fmt.Printf("付款单据创建成功,单据编码: %s\n", resp.Code) } // ExampleClient_QueryPaymentStatus 演示如何查询支付状态 func ExampleClient_QueryPaymentStatus() { client := intelligence_finance_v1.NewClient( "https://api.example.com", "your-tenant-id", "your-client-id", "your-client-secret", ) req := &intelligence_finance_v1.PaymentStatusQueryRequest{ Code: "PAY-2024-001", UserID: "user123", } ctx := context.Background() resp, err := client.QueryPaymentStatus(ctx, "/api/payment/status", req) if err != nil { log.Fatalf("查询支付状态失败: %v", err) } fmt.Printf("支付状态: %s, 支付时间: %s\n", resp.PaymentStatus, resp.PaymentTime) } // Example_HandlePaymentNotification 演示如何处理平台支付完成回调 func Example_HandlePaymentNotification() { // 这是一个 HTTP handler 示例,展示如何处理平台回调 http.HandleFunc("/callback/payment", func(w http.ResponseWriter, r *http.Request) { // 解析通知数据 notification, err := intelligence_finance_v1.ParsePaymentNotification(r) if err != nil { log.Printf("解析通知失败: %v", err) intelligence_finance_v1.WriteNotificationResponse(w, false) return } // 根据支付状态处理业务逻辑 switch notification.PaymentStatus { case intelligence_finance_v1.PaymentStatusSuccess: log.Printf("支付成功: 单据 %s, 金额 %s, 时间 %s", notification.Code, notification.Amount, notification.PaymentTime) // 更新订单状态等业务处理... case intelligence_finance_v1.PaymentStatusFail: log.Printf("支付失败: 单据 %s, 原因: %s", notification.Code, notification.FailReason) case intelligence_finance_v1.PaymentStatusTerminate: log.Printf("支付已取消: 单据 %s", notification.Code) default: log.Printf("支付状态: %s, 单据 %s", notification.PaymentStatus, notification.Code) } // 返回成功响应 intelligence_finance_v1.WriteNotificationResponse(w, true) }) log.Println("回调服务启动在 :8080") log.Fatal(http.ListenAndServe(":8080", nil)) } // Example_VerifySignature 演示签名生成和验证 func Example_VerifySignature() { secret := "your-client-secret" timestamp := intelligence_finance_v1.GenerateTimestamp() nonce, _ := intelligence_finance_v1.GenerateNonce(16) // 生成签名 data := timestamp + nonce signature := intelligence_finance_v1.HmacSHA256Sign(secret, data) fmt.Printf("签名: %s\n", signature) // 验证签名 valid := intelligence_finance_v1.HmacSHA256Verify(secret, data, signature) fmt.Printf("签名验证: %v\n", valid) } // Example_DingTalkClient 演示钉钉AI表格客户端的使用 func Example_DingTalkClient() { // 钉钉AI表格的 client-id 固定为 "dd-ai-table" // 签名由AI表格自动完成,但 SDK 仍会生成签名请求头 client := intelligence_finance_v1.NewDingTalkClient( "https://api.example.com", "your-tenant-id", "your-app-secret", // 在钉钉AI表格中配置为 APPSecret ) req := &intelligence_finance_v1.InvoiceStatusQueryRequest{ OrderID: "ORDER-2024-001", } ctx := context.Background() resp, err := client.QueryInvoiceStatus(ctx, "/api/invoice/status", req) if err != nil { log.Fatalf("查询失败: %v", err) } fmt.Printf("开票状态: %d\n", resp.Status) } ``` === SDK 生成完成 ===