// File: intelligence_finance_v1_ga/go.mod ```go module intelligence_finance_v1_ga go 1.21 ``` // File: intelligence_finance_v1_ga/types.go ```go package intelligence_finance_v1_ga import "encoding/json" // BaseResponse 是平台所有请求接口的通用响应结构。 // code 为业务状态码(0 或 200 表示成功),msg 为提示信息,data 为具体业务数据。 type BaseResponse struct { Code int `json:"code"` Msg string `json:"msg"` Data json.RawMessage `json:"data"` } // 发票类型枚举。 const ( InvoiceTypeSpecial = 1 // 专用发票 InvoiceTypeNormal = 2 // 普通发票 InvoiceTypeNormalElectronic = 3 // 普通发票(电子) InvoiceTypeSpecialElectronic = 4 // 专用发票(电子) InvoiceTypeDigitalSpecial = 8 // 数电专票 InvoiceTypeDigitalNormal = 9 // 数电普票 ) // 开票状态枚举。 const ( InvoiceStatusNotIssued = 0 // 未开票 InvoiceStatusIssuing = 1 // 开票中 InvoiceStatusPartialFailed = 2 // 部分失败 InvoiceStatusSuccess = 3 // 开票成功 InvoiceStatusFailed = 4 // 开票失败 InvoiceStatusPartialNotIssued = 5 // 部分未开 InvoiceStatusNoDigitalAccount = 6 // 未配置数电账号 InvoiceStatusNoAutoConfig = 7 // 未配置自动开票配置 ) // 支付状态枚举。 const ( PaymentStatusSuccess = "SUCCESS" // 支付成功 PaymentStatusFail = "FAIL" // 支付失败 PaymentStatusTerminate = "TERMINATE" // 支付取消 PaymentStatusWaitPay = "WAIT_PAY" // 待支付 PaymentStatusPaying = "PAYING" // 支付中 PaymentStatusPartSuccess = "PART_SUCCESS" // 部分支付成功 PaymentStatusRefund = "REFUND" // 退款 ) // 来源枚举。 const ( SourceApproval = "approval" // 审批单 SourceOpenAPI = "openapi" // 开发接口 ) // 账户类型枚举。 const ( AccountTypeAlipay = "ALIPAY" // 支付宝 AccountTypeBankCard = "BANKCARD" // 银行卡 AccountTypeCorpBankCard = "CORP_BANK_CARD" // 对公银行卡 AccountTypePersonalBankCard = "PERSONAL_BANK_CARD" // 对私银行卡 ) // 通知响应常量。 const ( NotifySuccess = "SUCCESS" // 通知处理成功 NotifyFailed = "FAILED" // 通知处理失败 ) // InvoiceOrderRequest 是订单开票请求参数。 type InvoiceOrderRequest struct { CompanyCode string `json:"companyCode"` // 开票的企业主体编码,不传则默认主体开票 OrderID string `json:"orderId"` // 订单唯一标识(需保证在贵方系统内唯一) InvoiceType int `json:"invoiceType"` // 发票类型枚举 Products []InvoiceProduct `json:"products"` // 货物/服务明细列表,至少一项 Remark string `json:"remark"` // 订单备注(非发票备注) Purchaser string `json:"purchaser"` // 购方企业名称 TaxNum string `json:"taxnum"` // 购方纳税人识别号 PurchaserAddress string `json:"purchaserAddress"` // 购方地址 PurchaserTel string `json:"purchaserTel"` // 购方电话 BankName string `json:"bankName"` // 购方开户行名称 BankAccount string `json:"bankAccount"` // 购方银行账号 Phone string `json:"phone"` // 收票人手机号(用于接收电票短信) Email string `json:"email"` // 收票人邮箱(用于接收电票邮件) ApplyPerson string `json:"applyPerson"` // 开票申请人名称 Payee string `json:"payee"` // 收款人(发票票面) Reviewer string `json:"reviewer"` // 复核人(发票票面) InvoiceRemark string `json:"invoiceRemark"` // 发票备注栏内容 NaturalPerson string `json:"naturalPerson"` // 购买方自然人标识:Y-是,N-否(默认N) AdditionInfo string `json:"additionInfo"` // 附加信息(JSON数组字符串) } // InvoiceProduct 是订单开票的货物/服务明细项。 type InvoiceProduct struct { ProductName string `json:"productName"` // 货物或服务名称 RevenueCode string `json:"revenueCode"` // 19位税收分类编码 AmountIncludeTax float64 `json:"amountIncludeTax"` // 单条明细含税总金额(单位:元) Specs string `json:"specs"` // 规格型号 Unit string `json:"unit"` // 计量单位(如:台、个、次) Quantity float64 `json:"quantity"` // 数量 Discount float64 `json:"discount"` // 折扣金额(无折扣传0) TaxSign int `json:"taxSign"` // 是否含税:0-不含税;1-含税(默认建议传1) TaxRate float64 `json:"taxRate"` // 税率(小数形式,如0.13表示13%) } // InvoiceOrderData 是订单开票接口的响应数据。 type InvoiceOrderData struct { Status int `json:"status"` // 开票状态 ErrorMsg string `json:"errorMsg"` // 错误信息(开票失败时返回原因) DataList []InvoiceData `json:"dataList"` // 发票数据列表(一张订单可能对应多张发票) } // InvoiceData 是单张发票的详细数据。 type InvoiceData struct { DeviceCode string `json:"deviceCode"` // 税控设备号 Drawer string `json:"drawer"` // 开票人 Email string `json:"email"` // 邮箱(购方邮箱) InvoiceType string `json:"invoiceType"` // 发票类型 IssueType string `json:"issueType"` // 开票类型:0-正数票;1-负数票(红冲) ListFlag string `json:"listFlag"` // 清单标识:0-无清单;1-有清单 Mobile string `json:"mobile"` // 手机号(购方手机) OriginalInvCode string `json:"originalInvCode"` // 红冲时对应的原蓝票代码 OriginalInvNo string `json:"originalInvNo"` // 红冲时对应的原蓝票号码 AdditionInfo string `json:"additionInfo"` // 数电发票备注栏的附加信息部分 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"` // 购买方自然人标识:Y-是;N-否 Remark string `json:"remark"` // 备注 Reviewer string `json:"reviewer"` // 复核人 SellerAddress string `json:"sellerAddress"` // 销方地址 SellerBankAccount string `json:"sellerBankAccount"` // 销方开户账号 SellerBankName string `json:"sellerBankName"` // 销方开户行 SellerName string `json:"sellerName"` // 销方名称 CheckCode string `json:"checkCode"` // 校验码 CipherText string `json:"cipherText"` // 密码区 DrewDate string `json:"drewDate"` // 开票日期(格式:yyyy-MM-dd HH:mm:ss) InvoiceCode string `json:"invoiceCode"` // 发票代码 InvoiceNo string `json:"invoiceNo"` // 发票号码 InvoiceStatus string `json:"invoiceStatus"` // 发票状态:1-正常;2-已红冲;3-已作废 LayoutFileURL string `json:"layoutFileUrl"` // 电子发票地址(PDF/OFD) PDFURL string `json:"pdfUrl"` // 电子发票PDF地址 OFDURL string `json:"ofdUrl"` // 电子发票OFD地址 XMLURL string `json:"xmlUrl"` // 电子发票XML数据地址 TotalExcludeTax string `json:"totalExcludeTax"` // 合计金额(不含税) TotalIncludeTax string `json:"totalIncludeTax"` // 合计金额(含税) TotalTaxAmount string `json:"totalTaxAmount"` // 合计税额 LevyingType string `json:"levyingType"` // 征税方式 Details []InvoiceDetail `json:"details"` // 商品明细列表 } // InvoiceDetail 是发票中的商品明细。 type InvoiceDetail struct { Amount string `json:"amount"` // 金额 Quantity string `json:"quantity"` // 数量 DeductionAmount string `json:"deductionAmount"` // 扣除金额 TaxAmount string `json:"taxAmount"` // 税额 ItemTitle string `json:"itemTitle"` // 商品合并显示名称 TaxCode string `json:"taxCode"` // 税收分类编码(19位) ItemType string `json:"itemType"` // 商品行性质:0-正常行;1-折扣行;2-被折扣行 ItemName string `json:"itemName"` // 商品简称 Specs string `json:"specs"` // 商品规格型号 TaxFreePolicy string `json:"taxFreePolicy"` // 免税政策:1-免税;2-不征税;3-普通零税率 PreferentialPolicy string `json:"preferentialPolicy"` // 优惠政策类型 TaxRate string `json:"taxRate"` // 税率(小数形式,如0.13) TaxSign string `json:"taxSign"` // 是否含税:0-否;1-是 Unit string `json:"unit"` // 计量单位(如:台、个、次) UnitPrice string `json:"unitPrice"` // 单价 } // QueryInvoiceStatusRequest 是开票状态查询请求参数。 type QueryInvoiceStatusRequest struct { OrderID string `json:"orderId"` // 订单唯一标识 } // QueryInvoiceStatusData 是开票状态查询接口的响应数据。 type QueryInvoiceStatusData struct { Status int `json:"status"` // 开票状态 Message string `json:"message"` // 状态描述(如"开票成功") Data []InvoiceData `json:"data"` // 发票详细列表,结构与回调中的 data 字段一致 } // CreatePaymentRequest 是创建付款单据请求参数。 type CreatePaymentRequest struct { Code string `json:"code"` // 单据编码 YidaAppType string `json:"yidaAppType"` // 宜搭应用类型 EmpAccountUserID string `json:"empAccountUserId"` // 员工账号用户ID Department *Department `json:"department"` // 部门信息 Usage string `json:"usage"` // 用途 PaymentUserID string `json:"paymentUserId"` // 付款用户ID Customer *Customer `json:"customer"` // 客户信息 PrincipalID string `json:"principalId"` // 负责人ID Remark string `json:"remark"` // 备注 Supplier *Supplier `json:"supplier"` // 供应商信息 Title string `json:"title"` // 标题 Project *Project `json:"project"` // 项目信息 PaymentUserIDListStr string `json:"paymentUserIdListStr"` // 付款用户ID列表字符串 NeedPayment bool `json:"needPayment"` // 是否需要付款 PaymentDetailListJsonStr string `json:"paymentDetailListJsonStr"` // 付款明细列表JSON字符串 PaymentDetailList []PaymentDetail `json:"paymentDetailList"` // 付款明细列表 Company *Company `json:"company"` // 企业主体信息 Amount string `json:"amount"` // 金额 RecipientAccountInfo *RecipientAccount `json:"recipientAccountInfo"` // 收款账户信息 EnterpriseAccount *EnterpriseAccount `json:"enterpriseAccount"` // 企业账号信息 Category []Category `json:"category"` // 收支类别信息 UserID string `json:"userId"` // 用户ID OccurDate int64 `json:"occurDate"` // 发生日期(时间戳) Product *Product `json:"product"` // 商品信息 YidaFormUUID string `json:"yidaFormUuid"` // 宜搭表单UUID CanEditPaymentInfo bool `json:"canEditPaymentInfo"` // 是否可编辑付款信息 PaymentUserIDList []string `json:"paymentUserIdList"` // 付款用户ID列表 YidaProcInsID string `json:"yidaProcInsId"` // 宜搭流程实例ID SyncPaymentOrder bool `json:"syncPaymentOrder"` // 是否同步付款单据 } // Department 是部门信息。 type Department struct { Code string `json:"code"` // 部门编码 Name string `json:"name"` // 部门名称 } // Customer 是客户信息。 type Customer struct { Code string `json:"code"` // 客户编码 Name string `json:"name"` // 客户名称 } // Supplier 是供应商信息。 type Supplier struct { Code string `json:"code"` // 供应商编码 Name string `json:"name"` // 供应商名称 } // Project 是项目信息。 type Project struct { Code string `json:"code"` // 项目编码 Name string `json:"name"` // 项目名称 } // Category 是收支类别信息。 type Category struct { Code string `json:"code"` // 类别编码 Name string `json:"name"` // 类别名称 } // Product 是商品信息。 type Product struct { Code string `json:"code"` // 商品编码 Name string `json:"name"` // 商品名称 } // Company 是企业主体信息。 type Company struct { Code string `json:"code"` // 企业编码 Name string `json:"name"` // 企业名称 } // EnterpriseAccount 是企业账号信息。 type EnterpriseAccount struct { EnterpriseAccountCode string `json:"enterpriseAccountCode"` // 企业账号编码 AccountCategory string `json:"accountCategory"` // 账户类别 AccountType string `json:"accountType"` // 账户类型 CardNo string `json:"cardNo"` // 卡号 AccountName string `json:"accountName"` // 账户名称 OfficialNumber string `json:"officialNumber"` // 对公账号 OfficialName string `json:"officialName"` // 对公账户名称 Name string `json:"name"` // 名称 Code string `json:"code"` // 编码 City string `json:"city"` // 城市 Province string `json:"province"` // 省份 } // RecipientAccount 是收款账户信息。 type RecipientAccount struct { AccountCategory string `json:"accountCategory"` // 账户类别 AccountType string `json:"accountType"` // 账户类型 CardNo string `json:"cardNo"` // 卡号 AccountName string `json:"accountName"` // 账户名称 } // PaymentDetail 是付款明细。 type PaymentDetail struct { Amount string `json:"amount"` // 金额 InvoiceInfo *InvoiceInfo `json:"invoiceInfo"` // 发票信息 ProductCode string `json:"productCode"` // 商品编码 ProjectCode string `json:"projectCode"` // 项目编码 Remark string `json:"remark"` // 备注 PrincipalID string `json:"principalId"` // 负责人ID Tax string `json:"tax"` // 税额 } // InvoiceInfo 是付款明细中的发票信息。 type InvoiceInfo struct { InvoiceNo string `json:"invoiceNo"` // 发票号码 InvoiceCode string `json:"invoiceCode"` // 发票代码 } // CreatePaymentData 是创建付款单据接口的响应数据。 type CreatePaymentData struct { Code string `json:"code"` // 单据唯一编码 } // QueryPaymentStatusRequest 是支付状态查询请求参数。 type QueryPaymentStatusRequest struct { Code string `json:"code"` // 单据编码 UserID string `json:"userId"` // 用户ID } // PaymentInfo 是支付信息,用于支付完成通知和支付状态查询。 type PaymentInfo struct { Code string `json:"code"` // 单据编码 InstanceID string `json:"instanceId"` // 实例ID CorpID string `json:"corpId"` // 企业ID PaymentStatus string `json:"paymentStatus"` // 支付状态 PaymentTime string `json:"paymentTime"` // 支付时间 UserID string `json:"userId"` // 用户ID FailReason string `json:"failReason"` // 失败原因 PayerAccountInfo *PayerAccountInfo `json:"payerAccountInfo"` // 付款账户信息 PayeeAccountInfo *PayeeAccountInfo `json:"payeeAccountInfo"` // 收款账户信息 RelatedRowNumberList []string `json:"relatedRowNumberList"` // 关联行号列表 Source string `json:"source"` // 来源 Template string `json:"template"` // 模板 Amount string `json:"amount"` // 金额 } // PayerAccountInfo 是付款账户信息。 type PayerAccountInfo struct { BankOpenDTO *BankOpenDTO `json:"bankOpenDTO"` // 银行信息 EnterpriseAccountCode string `json:"enterpriseAccountCode"` // 企业账号编码 AccountType string `json:"accountType"` // 账户类型 } // PayeeAccountInfo 是收款账户信息。 type PayeeAccountInfo struct { BankOpenDTO *BankOpenDTO `json:"bankOpenDTO"` // 银行信息 } // BankOpenDTO 是银行信息。 type BankOpenDTO struct { BankCode string `json:"bankCode"` // 银行编码 BankName string `json:"bankName"` // 银行名称 BankBranchCode string `json:"bankBranchCode"` // 银行支行编码 BankBranchName string `json:"bankBranchName"` // 银行支行名称 AccountName string `json:"accountName"` // 账户名称 BankCardNo string `json:"bankCardNo"` // 银行卡号 Type string `json:"type"` // 账户类型 } // NotifyRequest 是通用通知机制的通知数据。 type NotifyRequest struct { BizType string `json:"bizType"` // 业务类型 BizID string `json:"bizId"` // 业务ID Data string `json:"data"` // 业务数据 } ``` // 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 进行签名,并返回 Base64 编码结果。 // 密钥为 client-secret,签名数据为 timestamp + nonce。 func HmacSHA256Base64(secret, data string) string { mac := hmac.New(sha256.New, []byte(secret)) mac.Write([]byte(data)) return base64.StdEncoding.EncodeToString(mac.Sum(nil)) } // BuildSignString 方式1:字典序排序拼接。 // 空值会被排除,且自动排除 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 方式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/errors.go ```go package intelligence_finance_v1_ga import "fmt" // SDKError 表示 SDK 内部错误(网络、编解码等)。 type SDKError struct { Op string // 发生错误的操作名称 Err error // 底层错误 } // Error 实现 error 接口。 func (e *SDKError) Error() string { if e.Op != "" { return fmt.Sprintf("intelligence_finance_v1_ga: %s: %v", e.Op, e.Err) } return fmt.Sprintf("intelligence_finance_v1_ga: %v", e.Err) } // Unwrap 返回底层错误,支持 errors.Is / errors.As。 func (e *SDKError) Unwrap() error { return e.Err } // APIError 表示平台返回的业务错误或非 200 的 HTTP 响应。 type APIError struct { HTTPStatus int // HTTP 状态码(非 200 时设置) Code int // 平台业务错误码 Msg string // 平台错误信息 Body string // 原始响应体(HTTP 非 200 时设置) } // Error 实现 error 接口。 func (e *APIError) Error() string { if e.HTTPStatus != 0 { return fmt.Sprintf("intelligence_finance_v1_ga: http status %d: %s", e.HTTPStatus, e.Body) } return fmt.Sprintf("intelligence_finance_v1_ga: api error code=%d msg=%s", e.Code, e.Msg) } ``` // File: intelligence_finance_v1_ga/client.go ```go package intelligence_finance_v1_ga import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "strings" "time" ) // 默认接口路径(实际路径以接口对接时分配的短码为准,可通过 Option 覆盖)。 const ( DefaultInvoiceOrderPath = "/invoice/order" DefaultQueryInvoiceStatusPath = "/invoice/status/query" DefaultCreatePaymentPath = "/payment/create" DefaultQueryPaymentStatusPath = "/payment/status/query" ) // Client 是业财连接接口的客户端。 type Client struct { baseURL string tenantID string clientID string clientSecret string httpClient *http.Client signEnabled bool invoiceOrderPath string queryInvoiceStatusPath string createPaymentPath string queryPaymentStatusPath string } // Option 定义客户端配置选项。 type Option func(*Client) // WithHTTPClient 设置自定义 HTTP 客户端。 func WithHTTPClient(c *http.Client) Option { return func(cli *Client) { cli.httpClient = c } } // WithTimeout 设置请求超时时间。 func WithTimeout(d time.Duration) Option { return func(cli *Client) { cli.httpClient.Timeout = d } } // WithSignEnabled 设置是否启用签名。 // 客户应用需启用签名(默认启用);钉钉AI表格无需配置签名,可设置为 false。 func WithSignEnabled(enabled bool) Option { return func(cli *Client) { cli.signEnabled = enabled } } // WithInvoiceOrderPath 覆盖订单开票接口路径。 func WithInvoiceOrderPath(p string) Option { return func(cli *Client) { cli.invoiceOrderPath = p } } // WithQueryInvoiceStatusPath 覆盖开票状态查询接口路径。 func WithQueryInvoiceStatusPath(p string) Option { return func(cli *Client) { cli.queryInvoiceStatusPath = p } } // WithCreatePaymentPath 覆盖创建付款单据接口路径。 func WithCreatePaymentPath(p string) Option { return func(cli *Client) { cli.createPaymentPath = p } } // WithQueryPaymentStatusPath 覆盖支付状态查询接口路径。 func WithQueryPaymentStatusPath(p string) Option { return func(cli *Client) { cli.queryPaymentStatusPath = p } } // 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, httpClient: &http.Client{Timeout: 30 * time.Second}, signEnabled: true, invoiceOrderPath: DefaultInvoiceOrderPath, queryInvoiceStatusPath: DefaultQueryInvoiceStatusPath, createPaymentPath: DefaultCreatePaymentPath, queryPaymentStatusPath: DefaultQueryPaymentStatusPath, } for _, opt := range opts { opt(c) } return c } // doRequest 发送 POST 请求并处理通用响应结构,返回 data 字段的原始字节。 func (c *Client) doRequest(ctx context.Context, path string, reqBody interface{}) (json.RawMessage, error) { bodyBytes, err := json.Marshal(reqBody) if err != nil { return nil, &SDKError{Op: "doRequest", Err: fmt.Errorf("marshal request: %w", err)} } req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.baseURL+path, bytes.NewReader(bodyBytes)) if err != nil { return nil, &SDKError{Op: "doRequest", Err: fmt.Errorf("new request: %w", err)} } req.Header.Set("Content-Type", "application/json") req.Header.Set("tenant-id", c.tenantID) req.Header.Set("client-id", c.clientID) if c.signEnabled { timestamp := GenerateTimestamp() nonce, err := GenerateNonce(16) if err != nil { return nil, &SDKError{Op: "doRequest", Err: fmt.Errorf("generate nonce: %w", err)} } // 签名算法:HmacSHA256,密钥为 client-secret,签名数据为 timestamp + nonce,结果 Base64 编码。 signature := HmacSHA256Base64(c.clientSecret, timestamp+nonce) req.Header.Set("x-bfl-signature-timestamp", timestamp) req.Header.Set("x-bfl-signature-nonce", nonce) req.Header.Set("x-bfl-signature", signature) } resp, err := c.httpClient.Do(req) if err != nil { return nil, &SDKError{Op: "doRequest", Err: fmt.Errorf("do request: %w", err)} } defer resp.Body.Close() respBytes, err := io.ReadAll(resp.Body) if err != nil { return nil, &SDKError{Op: "doRequest", Err: fmt.Errorf("read response: %w", err)} } if resp.StatusCode != http.StatusOK { return nil, &APIError{HTTPStatus: resp.StatusCode, Body: string(respBytes)} } var base BaseResponse if err := json.Unmarshal(respBytes, &base); err != nil { return nil, &SDKError{Op: "doRequest", Err: fmt.Errorf("unmarshal base response: %w", err)} } if base.Code != 0 && base.Code != 200 { return nil, &APIError{Code: base.Code, Msg: base.Msg} } return base.Data, nil } // InvoiceOrder 提交订单开票请求。 func (c *Client) InvoiceOrder(ctx context.Context, req *InvoiceOrderRequest) (*InvoiceOrderData, error) { data, err := c.doRequest(ctx, c.invoiceOrderPath, req) if err != nil { return nil, err } var out InvoiceOrderData if len(data) > 0 { if err := json.Unmarshal(data, &out); err != nil { return nil, &SDKError{Op: "InvoiceOrder", Err: fmt.Errorf("unmarshal data: %w", err)} } } return &out, nil } // QueryInvoiceStatus 查询订单开票状态。 func (c *Client) QueryInvoiceStatus(ctx context.Context, req *QueryInvoiceStatusRequest) (*QueryInvoiceStatusData, error) { data, err := c.doRequest(ctx, c.queryInvoiceStatusPath, req) if err != nil { return nil, err } var out QueryInvoiceStatusData if len(data) > 0 { if err := json.Unmarshal(data, &out); err != nil { return nil, &SDKError{Op: "QueryInvoiceStatus", Err: fmt.Errorf("unmarshal data: %w", err)} } } return &out, nil } // CreatePayment 创建付款单据。 func (c *Client) CreatePayment(ctx context.Context, req *CreatePaymentRequest) (*CreatePaymentData, error) { data, err := c.doRequest(ctx, c.createPaymentPath, req) if err != nil { return nil, err } var out CreatePaymentData if len(data) > 0 { if err := json.Unmarshal(data, &out); err != nil { return nil, &SDKError{Op: "CreatePayment", Err: fmt.Errorf("unmarshal data: %w", err)} } } return &out, nil } // QueryPaymentStatus 查询支付状态。 // 响应数据与支付完成通知的数据结构一致。 func (c *Client) QueryPaymentStatus(ctx context.Context, req *QueryPaymentStatusRequest) (*PaymentInfo, error) { data, err := c.doRequest(ctx, c.queryPaymentStatusPath, req) if err != nil { return nil, err } var out PaymentInfo if len(data) > 0 { if err := json.Unmarshal(data, &out); err != nil { return nil, &SDKError{Op: "QueryPaymentStatus", Err: fmt.Errorf("unmarshal data: %w", err)} } } return &out, nil } // ParsePaymentNotify 解析支付完成通知请求体。 // 该接口由客户提供、平台调用,用于在支付完成后接收平台回调。 func ParsePaymentNotify(body []byte) (*PaymentInfo, error) { var info PaymentInfo if err := json.Unmarshal(body, &info); err != nil { return nil, &SDKError{Op: "ParsePaymentNotify", Err: fmt.Errorf("unmarshal notify: %w", err)} } return &info, nil } // ParseNotify 解析通用通知请求体(bizType / bizId / data)。 func ParseNotify(body []byte) (*NotifyRequest, error) { var req NotifyRequest if err := json.Unmarshal(body, &req); err != nil { return nil, &SDKError{Op: "ParseNotify", Err: fmt.Errorf("unmarshal notify: %w", err)} } return &req, nil } ``` // File: intelligence_finance_v1_ga/example_test.go ```go package intelligence_finance_v1_ga import ( "context" "net/http" "net/http/httptest" "testing" ) // TestInvoiceOrder 演示订单开票接口的调用。 func TestInvoiceOrder(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { t.Errorf("expected POST, got %s", r.Method) } if got := r.Header.Get("tenant-id"); got != "tenant-001" { t.Errorf("tenant-id = %q", got) } if got := r.Header.Get("client-id"); got != "dd-ai-table" { t.Errorf("client-id = %q", got) } if got := r.Header.Get("x-bfl-signature"); got == "" { t.Error("missing x-bfl-signature header") } if got := r.Header.Get("x-bfl-signature-timestamp"); got == "" { t.Error("missing x-bfl-signature-timestamp header") } if got := r.Header.Get("x-bfl-signature-nonce"); got == "" { t.Error("missing x-bfl-signature-nonce header") } w.Header().Set("Content-Type", "application/json") w.Write([]byte(`{"code":0,"msg":"ok","data":{"status":1,"errorMsg":"","dataList":[]}}`)) })) defer server.Close() client := NewClient(server.URL, "tenant-001", "dd-ai-table", "test-secret") resp, err := client.InvoiceOrder(context.Background(), &InvoiceOrderRequest{ OrderID: "ORDER-001", InvoiceType: InvoiceTypeDigitalNormal, Purchaser: "测试购方企业", TaxNum: "91310000MA1FL00000", Email: "buyer@example.com", Products: []InvoiceProduct{ { ProductName: "测试服务", RevenueCode: "3040101000000000000", AmountIncludeTax: 113, Quantity: 1, TaxSign: 1, TaxRate: 0.13, }, }, }) if err != nil { t.Fatalf("InvoiceOrder: %v", err) } if resp.Status != InvoiceStatusIssuing { t.Errorf("status = %d, want %d", resp.Status, InvoiceStatusIssuing) } } // TestQueryInvoiceStatus 演示开票状态查询接口的调用。 func TestQueryInvoiceStatus(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.Write([]byte(`{"code":0,"msg":"ok","data":{"status":3,"message":"开票成功","data":[]}}`)) })) defer server.Close() client := NewClient(server.URL, "tenant-001", "dd-ai-table", "test-secret") resp, err := client.QueryInvoiceStatus(context.Background(), &QueryInvoiceStatusRequest{ OrderID: "ORDER-001", }) if err != nil { t.Fatalf("QueryInvoiceStatus: %v", err) } if resp.Status != InvoiceStatusSuccess { t.Errorf("status = %d, want %d", resp.Status, InvoiceStatusSuccess) } if resp.Message != "开票成功" { t.Errorf("message = %q", resp.Message) } } // TestCreatePayment 演示创建付款单据接口的调用。 func TestCreatePayment(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.Write([]byte(`{"code":0,"msg":"ok","data":{"code":"PAY-001"}}`)) })) defer server.Close() client := NewClient(server.URL, "tenant-001", "dd-ai-table", "test-secret") resp, err := client.CreatePayment(context.Background(), &CreatePaymentRequest{ Code: "PAY-001", UserID: "user-001", Title: "测试付款单", Amount: "1000.00", Department: &Department{ Name: "财务部", }, Supplier: &Supplier{ Name: "测试供应商", }, PaymentDetailList: []PaymentDetail{ { Amount: "1000.00", InvoiceInfo: &InvoiceInfo{ InvoiceNo: "12345678", InvoiceCode: "011001900111", }, }, }, }) if err != nil { t.Fatalf("CreatePayment: %v", err) } if resp.Code != "PAY-001" { t.Errorf("code = %q, want PAY-001", resp.Code) } } // TestQueryPaymentStatus 演示支付状态查询接口的调用。 func TestQueryPaymentStatus(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.Write([]byte(`{"code":0,"msg":"ok","data":{"code":"PAY-001","instanceId":"inst-001","corpId":"corp-001","paymentStatus":"SUCCESS","paymentTime":"2024-01-01 10:00:00","userId":"user-001","source":"openapi","amount":"1000.00"}}`)) })) defer server.Close() client := NewClient(server.URL, "tenant-001", "dd-ai-table", "test-secret") resp, err := client.QueryPaymentStatus(context.Background(), &QueryPaymentStatusRequest{ Code: "PAY-001", UserID: "user-001", }) if err != nil { t.Fatalf("QueryPaymentStatus: %v", err) } if resp.PaymentStatus != PaymentStatusSuccess { t.Errorf("paymentStatus = %q, want %q", resp.PaymentStatus, PaymentStatusSuccess) } } // TestParsePaymentNotify 演示解析支付完成通知。 func TestParsePaymentNotify(t *testing.T) { body := []byte(`{"code":"PAY-001","instanceId":"inst-001","corpId":"corp-001","paymentStatus":"SUCCESS","paymentTime":"2024-01-01 10:00:00","userId":"user-001","source":"openapi","amount":"1000.00","payerAccountInfo":{"accountType":"BANKCARD","bankOpenDTO":{"bankName":"交通银行","bankCardNo":"6222000000000000"}}}`) info, err := ParsePaymentNotify(body) if err != nil { t.Fatalf("ParsePaymentNotify: %v", err) } if info.Code != "PAY-001" { t.Errorf("code = %q", info.Code) } if info.PaymentStatus != PaymentStatusSuccess { t.Errorf("paymentStatus = %q", info.PaymentStatus) } if info.PayerAccountInfo == nil || info.PayerAccountInfo.BankOpenDTO == nil { t.Error("payerAccountInfo.bankOpenDTO is nil") } } // TestParseNotify 演示解析通用通知。 func TestParseNotify(t *testing.T) { body := []byte(`{"bizType":"invoice","bizId":"ORDER-001","data":"{\"status\":3}"}`) req, err := ParseNotify(body) if err != nil { t.Fatalf("ParseNotify: %v", err) } if req.BizType != "invoice" { t.Errorf("bizType = %q", req.BizType) } if req.BizID != "ORDER-001" { t.Errorf("bizId = %q", req.BizID) } } // TestHmacSHA256Base64 演示签名算法的正确性。 func TestHmacSHA256Base64(t *testing.T) { // 使用固定输入验证签名结果可复现。 sig := HmacSHA256Base64("secret", "1700000000abc123") if sig == "" { t.Error("signature is empty") } } ```