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