intelligence_finance_v1-202.../intelligence_finance_v1/generate.md

38 KiB
Raw Blame History

现在我将根据文档生成完整的 Go SDK 代码工程。

分析

认证机制:客户应用使用 HmacSHA256 算法,密钥为 client-secret,签名数据为 timestamp + nonce,结果 Base64 编码。需要生成 timestamp 和 nonce。

接口列表

  1. 订单开票POST
  2. 开票状态查询POST
  3. 创建付款单据POST
  4. 支付完成通知(回调,客户提供接口)
  5. 支付状态查询POST

通知机制:客户提供接口,平台调用,响应直接返回字符串 SUCCESSFAILED

现在开始生成所有文件:


// File: intelligence_finance_v1/go.mod

module intelligence_finance_v1

go 1.21

// File: intelligence_finance_v1/errors.go

package intelligence_finance_v1

import "fmt"

// Error 表示 SDK 返回的错误类型。
type Error struct {
	// Code 错误码
	Code int
	// Msg 错误信息
	Msg string
	// Err 底层错误
	Err error
}

// Error 实现 error 接口。
func (e *Error) Error() string {
	if e.Err != nil {
		return fmt.Sprintf("code=%d, msg=%s, err=%v", e.Code, e.Msg, e.Err)
	}
	return fmt.Sprintf("code=%d, msg=%s", e.Code, e.Msg)
}

// Unwrap 返回底层错误,支持 errors.Is/As。
func (e *Error) Unwrap() error {
	return e.Err
}

// NewError 创建一个新的 Error。
func NewError(code int, msg string) *Error {
	return &Error{Code: code, Msg: msg}
}

// WrapError 包装底层错误。
func WrapError(code int, msg string, err error) *Error {
	return &Error{Code: code, Msg: msg, Err: err}
}

// 预定义错误
var (
	// ErrInvalidConfig 配置无效
	ErrInvalidConfig = NewError(1001, "invalid config")
	// ErrNetwork 网络请求失败
	ErrNetwork = NewError(1002, "network error")
	// ErrSignature 签名失败
	ErrSignature = NewError(1003, "signature error")
	// ErrResponse 响应解析失败
	ErrResponse = NewError(1004, "response error")
	// ErrBusiness 业务错误code != 0
	ErrBusiness = NewError(1005, "business error")
)

// File: intelligence_finance_v1/crypto.go

package intelligence_finance_v1

import (
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"encoding/base64"
	"fmt"
	"math/big"
	"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 进行签名,密钥为 secret结果以 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))
}

// BuildSignature 根据文档认证机制生成签名。
//
// 签名算法HmacSHA256
// 密钥client-secret
// 签名数据x-bfl-signature-timestamp + x-bfl-signature-nonce
// 结果编码Base64
func BuildSignature(clientSecret, timestamp, nonce string) string {
	return HmacSHA256Base64(clientSecret, timestamp+nonce)
}

// File: intelligence_finance_v1/types.go

package intelligence_finance_v1

// 发票类型枚举
const (
	InvoiceTypeSpecial          = 1 // 专用发票
	InvoiceTypeNormal           = 2 // 普通发票
	InvoiceTypeNormalElectronic = 3 // 普通发票(电子)
	InvoiceTypeSpecialElec      = 4 // 专用发票(电子)
	InvoiceTypeDigitalSpecial   = 8 // 数电专票
	InvoiceTypeDigitalNormal    = 9 // 数电普票
)

// 开票状态枚举
const (
	InvoiceStatusNotInvoiced      = 0 // 未开票
	InvoiceStatusInvoicing        = 1 // 开票中
	InvoiceStatusPartialFailed    = 2 // 部分失败
	InvoiceStatusSuccess          = 3 // 开票成功
	InvoiceStatusFailed           = 4 // 开票失败
	InvoiceStatusPartialNotDone   = 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" // 对私银行卡
)

// CommonResponse 通用响应结构。
type CommonResponse struct {
	Code int    `json:"code"` // 响应码
	Msg  string `json:"msg"`  // 响应信息
	Data any    `json:"data"` // 响应数据
}

// InvoiceRequest 订单开票请求参数。
type InvoiceRequest struct {
	CompanyCode     string          `json:"companyCode"`     // 开票的企业主体编码
	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"`     // 附加信息JSON数组字符串
}

// InvoiceProduct 订单开票货物/服务明细项。
type InvoiceProduct struct {
	ProductName     string  `json:"productName"`     // 货物或服务名称
	RevenueCode     string  `json:"revenueCode"`     // 19位税收分类编码
	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"`  // 是否含税0-不含税1-含税
	TaxRate         float64 `json:"taxRate,omitempty"`  // 税率(小数形式)
}

// InvoiceResponse 订单开票响应参数。
type InvoiceResponse 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"`           // 开票类型
	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"`     // 电子发票PDF地址
	OfdURL              string        `json:"ofdUrl,omitempty"`     // 电子发票OFD地址
	XmlURL              string        `json:"xmlUrl,omitempty"`     // 电子发票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,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"`          // 单价
}

// InvoiceStatusQueryRequest 开票状态查询请求参数。
type InvoiceStatusQueryRequest struct {
	OrderID string `json:"orderId"` // 订单唯一标识
}

// InvoiceStatusQueryResponse 开票状态查询响应参数。
type InvoiceStatusQueryResponse struct {
	Status  int           `json:"status"`  // 开票状态
	Message string        `json:"message"` // 状态描述
	Data    []InvoiceData `json:"data"`    // 发票详细列表
}

// PaymentOrderRequest 创建付款单据请求参数。
type PaymentOrderRequest struct {
	Code                    string             `json:"code"`                    // 单据编码
	YidaAppType             string             `json:"yidaAppType,omitempty"`   // 宜搭应用类型
	EmpAccountUserID        string             `json:"empAccountUserId,omitempty"` // 员工账户用户ID
	Department              *Department        `json:"department,omitempty"`    // 部门信息
	Usage                   string             `json:"usage,omitempty"`         // 用途
	PaymentUserID           string             `json:"paymentUserId,omitempty"` // 付款用户ID
	Customer                *Customer          `json:"customer,omitempty"`      // 客户信息
	PrincipalID             string             `json:"principalId,omitempty"`   // 负责人ID
	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"` // 付款用户ID列表字符串
	NeedPayment             bool               `json:"needPayment,omitempty"`   // 是否需要付款
	PaymentDetailListJSONStr string            `json:"paymentDetailListJsonStr,omitempty"` // 付款明细JSON字符串
	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"`                  // 用户ID
	OccurDate               int64              `json:"occurDate,omitempty"`     // 发生日期(时间戳)
	Product                 *Product           `json:"product,omitempty"`       // 商品信息
	YidaFormUUID            string             `json:"yidaFormUuid,omitempty"`  // 宜搭表单UUID
	CanEditPaymentInfo      bool               `json:"canEditPaymentInfo,omitempty"` // 是否可编辑付款信息
	PaymentUserIDList       []string           `json:"paymentUserIdList,omitempty"` // 付款用户ID列表
	YidaProcInsID           string             `json:"yidaProcInsId,omitempty"` // 宜搭流程实例ID
	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"` // 负责人ID
	Tax         string       `json:"tax,omitempty"`         // 税额
}

// InvoiceInfo 付款明细发票信息。
type InvoiceInfo struct {
	InvoiceNo   string `json:"invoiceNo,omitempty"`   // 发票号码
	InvoiceCode string `json:"invoiceCode,omitempty"` // 发票代码
}

// PaymentOrderResponse 创建付款单据响应参数。
type PaymentOrderResponse struct {
	Code string `json:"code"` // 单据唯一编码
}

// PaymentNotifyRequest 支付完成通知数据接口4type PaymentNotifyRequest 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,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"`           // 账户类型
}

// PaymentStatusQueryRequest 支付状态查询请求参数。
type PaymentStatusQueryRequest struct {
	Code   string `json:"code"`   // 单据编码
	UserID string `json:"userId"` // 用户ID
}

// PaymentStatusQueryResponse 支付状态查询响应参数(与支付通知数据结构一致)。
type PaymentStatusQueryResponse 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,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"`      // 金额
}

// Notification 通用通知数据bizType/bizId/datatype Notification struct {
	BizType string `json:"bizType"` // 业务类型
	BizID   string `json:"bizId"`   // 业务ID
	Data    string `json:"data"`    // 业务数据
}

// 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 平台接口基础地址
	baseURL string
	// tenantID 平台分配的租户唯一标识
	tenantID string
	// clientID 平台分配的应用标识
	clientID string
	// clientSecret 平台分配的密钥
	clientSecret string
	// httpClient HTTP 客户端
	httpClient *http.Client
	// signEnabled 是否启用签名钉钉AI表格无需配置签名可关闭
	signEnabled bool
}

// ClientOption 客户端配置选项。
type ClientOption func(*Client)

// WithHTTPClient 自定义 HTTP 客户端。
func WithHTTPClient(hc *http.Client) ClientOption {
	return func(c *Client) {
		c.httpClient = hc
	}
}

// WithSignEnabled 设置是否启用签名(默认启用)。
func WithSignEnabled(enabled bool) ClientOption {
	return func(c *Client) {
		c.signEnabled = enabled
	}
}

// NewClient 创建一个新的智能财务业财连接客户端。
//
// 参数:
//   - baseURL平台接口基础地址
//   - 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: 30 * time.Second},
		signEnabled:  true,
	}
	for _, opt := range opts {
		opt(c)
	}
	return c
}

// doRequest 发送 HTTP 请求并解析响应。
func (c *Client) doRequest(ctx context.Context, path string, reqBody any, respBody any) error {
	bodyBytes, err := json.Marshal(reqBody)
	if err != nil {
		return WrapError(ErrSignature.Code, "marshal request body failed", err)
	}

	req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.baseURL+path, bytes.NewReader(bodyBytes))
	if err != nil {
		return WrapError(ErrNetwork.Code, "create request failed", err)
	}

	// 设置公共请求头
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("tenant-id", c.tenantID)
	req.Header.Set("client-id", c.clientID)

	// 签名钉钉AI表格无需配置可通过 WithSignEnabled(false) 关闭)
	if c.signEnabled {
		timestamp := GenerateTimestamp()
		nonce, err := GenerateNonce(16)
		if err != nil {
			return WrapError(ErrSignature.Code, "generate nonce failed", err)
		}
		req.Header.Set("x-bfl-signature-timestamp", timestamp)
		req.Header.Set("x-bfl-signature-nonce", nonce)
		req.Header.Set("x-bfl-signature", BuildSignature(c.clientSecret, timestamp, nonce))
	}

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return WrapError(ErrNetwork.Code, "do request failed", err)
	}
	defer resp.Body.Close()

	respBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return WrapError(ErrResponse.Code, "read response body failed", err)
	}

	if resp.StatusCode != http.StatusOK {
		return WrapError(ErrResponse.Code, fmt.Sprintf("unexpected http status: %d", resp.StatusCode), nil)
	}

	if err := json.Unmarshal(respBytes, respBody); err != nil {
		return WrapError(ErrResponse.Code, "unmarshal response body failed", err)
	}
	return nil
}

// doRequestCommon 发送请求并解析通用响应结构code/msg/datafunc (c *Client) doRequestCommon(ctx context.Context, path string, reqBody any, data any) error {
	var common CommonResponse
	if err := c.doRequest(ctx, path, reqBody, &common); err != nil {
		return err
	}
	if common.Code != 0 {
		return WrapError(common.Code, common.Msg, nil)
	}
	if data != nil && common.Data != nil {
		// 将 data 重新序列化后反序列化到目标结构
		dataBytes, err := json.Marshal(common.Data)
		if err != nil {
			return WrapError(ErrResponse.Code, "marshal data failed", err)
		}
		if err := json.Unmarshal(dataBytes, data); err != nil {
			return WrapError(ErrResponse.Code, "unmarshal data failed", err)
		}
	}
	return nil
}

// CreateInvoice 提交订单开票请求接口1//
// 请求方法POST
// 描述:提交订单开票请求。
func (c *Client) CreateInvoice(ctx context.Context, req *InvoiceRequest) (*InvoiceResponse, error) {
	var resp InvoiceResponse
	if err := c.doRequestCommon(ctx, "/invoice/create", req, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// QueryInvoiceStatus 查询订单开票状态接口2//
// 请求方法POST
// 描述:查询订单开票状态。
func (c *Client) QueryInvoiceStatus(ctx context.Context, req *InvoiceStatusQueryRequest) (*InvoiceStatusQueryResponse, error) {
	var resp InvoiceStatusQueryResponse
	if err := c.doRequestCommon(ctx, "/invoice/status", req, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// CreatePaymentOrder 创建付款单据接口3//
// 请求方法POST
// 描述:创建付款单据。
func (c *Client) CreatePaymentOrder(ctx context.Context, req *PaymentOrderRequest) (*PaymentOrderResponse, error) {
	var resp PaymentOrderResponse
	if err := c.doRequestCommon(ctx, "/payment/order/create", req, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// QueryPaymentStatus 查询支付状态接口5//
// 请求方法POST
// 描述:查询支付状态,响应数据与支付通知一致。
func (c *Client) QueryPaymentStatus(ctx context.Context, req *PaymentStatusQueryRequest) (*PaymentStatusQueryResponse, error) {
	var resp PaymentStatusQueryResponse
	if err := c.doRequestCommon(ctx, "/payment/status", req, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// HandlePaymentNotify 处理支付完成通知接口4平台回调客户接口//
// 该方法用于客户服务端接收平台回调,解析通知数据并返回响应字符串。
// 响应 BODY 直接返回字符串SUCCESS 表示成功FAILED 表示失败。
func HandlePaymentNotify(body []byte) (*PaymentNotifyRequest, error) {
	var req PaymentNotifyRequest
	if err := json.Unmarshal(body, &req); err != nil {
		return nil, WrapError(ErrResponse.Code, "unmarshal payment notify failed", err)
	}
	return &req, nil
}

// HandleNotification 处理通用通知bizType/bizId/data//
// 该方法用于客户服务端接收平台通用通知,解析通知数据。
func HandleNotification(body []byte) (*Notification, error) {
	var n Notification
	if err := json.Unmarshal(body, &n); err != nil {
		return nil, WrapError(ErrResponse.Code, "unmarshal notification failed", err)
	}
	return &n, nil
}

// File: intelligence_finance_v1/example_test.go

package intelligence_finance_v1

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"testing"
)

// ExampleNewClient 展示如何创建客户端并提交订单开票请求。
func ExampleNewClient() {
	// 创建客户端
	client := NewClient(
		"https://api.example.com",
		"your-tenant-id",
		"dd-ai-table",
		"your-client-secret",
	)

	// 构建订单开票请求
	req := &InvoiceRequest{
		OrderID:     "ORDER20240101001",
		InvoiceType: InvoiceTypeDigitalSpecial, // 数电专票
		Purchaser:   "示例科技有限公司",
		TaxNum:      "91110000XXXXXXXXXX",
		Email:       "finance@example.com",
		Phone:       "13800000000",
		Products: []InvoiceProduct{
			{
				ProductName:      "软件开发服务",
				RevenueCode:      "1090101000000000000",
				AmountIncludeTax: 11300,
				Quantity:         1,
				TaxSign:          1,
				TaxRate:          0.13,
			},
		},
	}

	resp, err := client.CreateInvoice(context.Background(), req)
	if err != nil {
		fmt.Printf("create invoice failed: %v\n", err)
		return
	}
	fmt.Printf("invoice status: %d\n", resp.Status)
}

// ExampleClient_QueryInvoiceStatus 展示如何查询开票状态。
func ExampleClient_QueryInvoiceStatus() {
	client := NewClient(
		"https://api.example.com",
		"your-tenant-id",
		"dd-ai-table",
		"your-client-secret",
	)

	resp, err := client.QueryInvoiceStatus(context.Background(), &InvoiceStatusQueryRequest{
		OrderID: "ORDER20240101001",
	})
	if err != nil {
		fmt.Printf("query invoice status failed: %v\n", err)
		return
	}
	fmt.Printf("status: %d, message: %s\n", resp.Status, resp.Message)
}

// ExampleClient_CreatePaymentOrder 展示如何创建付款单据。
func ExampleClient_CreatePaymentOrder() {
	client := NewClient(
		"https://api.example.com",
		"your-tenant-id",
		"dd-ai-table",
		"your-client-secret",
	)

	req := &PaymentOrderRequest{
		Code:   "PAY20240101001",
		UserID: "user-001",
		Title:  "供应商货款",
		Amount: "11300.00",
		Supplier: &Supplier{
			Name: "示例供应商有限公司",
		},
		Company: &Company{
			Name: "示例科技有限公司",
		},
		PaymentDetailList: []PaymentDetail{
			{
				Amount: "11300.00",
				InvoiceInfo: &InvoiceInfo{
					InvoiceNo:   "12345678",
					InvoiceCode: "011002000111",
				},
			},
		},
	}

	resp, err := client.CreatePaymentOrder(context.Background(), req)
	if err != nil {
		fmt.Printf("create payment order failed: %v\n", err)
		return
	}
	fmt.Printf("payment order code: %s\n", resp.Code)
}

// ExampleClient_QueryPaymentStatus 展示如何查询支付状态。
func ExampleClient_QueryPaymentStatus() {
	client := NewClient(
		"https://api.example.com",
		"your-tenant-id",
		"dd-ai-table",
		"your-client-secret",
	)

	resp, err := client.QueryPaymentStatus(context.Background(), &PaymentStatusQueryRequest{
		Code:   "PAY20240101001",
		UserID: "user-001",
	})
	if err != nil {
		fmt.Printf("query payment status failed: %v\n", err)
		return
	}
	fmt.Printf("payment status: %s\n", resp.PaymentStatus)
}

// ExampleHandlePaymentNotify 展示如何在客户服务端处理支付完成通知。
func ExampleHandlePaymentNotify() {
	// 模拟平台回调的请求体
	body := []byte(`{
		"code": "PAY20240101001",
		"instanceId": "inst-001",
		"corpId": "corp-001",
		"paymentStatus": "SUCCESS",
		"paymentTime": "2024-01-01 12:00:00",
		"userId": "user-001",
		"amount": "11300.00",
		"source": "openapi"
	}`)

	notify, err := HandlePaymentNotify(body)
	if err != nil {
		fmt.Printf("handle payment notify failed: %v\n", err)
		return
	}

	// 处理业务逻辑...

	// 返回响应字符串
	if notify.PaymentStatus == PaymentStatusSuccess {
		fmt.Println("SUCCESS")
	} else {
		fmt.Println("FAILED")
	}
}

// TestSignature 验证签名算法。
func TestSignature(t *testing.T) {
	secret := "test-secret"
	timestamp := "1700000000"
	nonce := "abc123"
	sig := BuildSignature(secret, timestamp, nonce)
	if sig == "" {
		t.Fatal("signature should not be empty")
	}
	t.Logf("signature: %s", sig)
}

// TestHandlePaymentNotify 测试支付通知解析。
func TestHandlePaymentNotify(t *testing.T) {
	body := []byte(`{"code":"PAY001","instanceId":"i1","corpId":"c1","paymentStatus":"SUCCESS","paymentTime":"2024-01-01 12:00:00","userId":"u1"}`)
	n, err := HandlePaymentNotify(body)
	if err != nil {
		t.Fatalf("handle payment notify failed: %v", err)
	}
	if n.Code != "PAY001" {
		t.Fatalf("unexpected code: %s", n.Code)
	}
}

// TestHandleNotification 测试通用通知解析。
func TestHandleNotification(t *testing.T) {
	body := []byte(`{"bizType":"invoice","bizId":"ORDER001","data":"{\"status\":3}"}`)
	n, err := HandleNotification(body)
	if err != nil {
		t.Fatalf("handle notification failed: %v", err)
	}
	if n.BizType != "invoice" {
		t.Fatalf("unexpected bizType: %s", n.BizType)
	}
}

// TestJSONMarshal 测试请求体 JSON 序列化。
func TestJSONMarshal(t *testing.T) {
	req := &InvoiceRequest{
		OrderID:     "ORDER001",
		InvoiceType: 9,
		Purchaser:   "测试公司",
		Products: []InvoiceProduct{
			{ProductName: "服务", RevenueCode: "1090101000000000000", AmountIncludeTax: 100, Quantity: 1},
		},
	}
	b, err := json.Marshal(req)
	if err != nil {
		t.Fatalf("marshal failed: %v", err)
	}
	t.Logf("request json: %s", string(b))
}

// TestHTTPHandler 展示如何将通知处理接入 HTTP 服务。
func TestHTTPHandler(t *testing.T) {
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		var body []byte
		_, _ = r.Body.Read(body)
		notify, err := HandlePaymentNotify(body)
		if err != nil {
			w.Write([]byte("FAILED"))
			return
		}
		_ = notify
		w.Write([]byte("SUCCESS"))
	})
	_ = handler
}

=== SDK 生成完成 ===

加密实现

以下是从加密工具获取的完整实现:

加密实现 1

{ "success": true, "data": "\n### 参数拼接规范\n\n常见拼接方式\n\n1. 字典序排序按key字典序排序后拼接 key=value\u0026key2=value2\n2. 固定顺序:按文档指定顺序拼接\n3. JSON字符串:整个请求体作为签名串\n\n代码模板\n\ngo\npackage crypto\n\nimport (\n \"fmt\"\n \"sort\"\n \"strings\"\n)\n\n// BuildSignString 方式1字典序排序拼接\nfunc BuildSignString(params map[string]string) string {\n keys := make([]string, 0, len(params))\n for k, v := range params {\n if v != \"\" \u0026\u0026 k != \"sign\" \u0026\u0026 k != \"signature\" {\n keys = append(keys, k)\n }\n }\n sort.Strings(keys)\n \n var parts []string\n for _, k := range keys {\n parts = append(parts, fmt.Sprintf(\"%s=%s\", k, params[k]))\n }\n return strings.Join(parts, \"\u0026\")\n}\n\n// BuildSignStringOrdered 方式2固定顺序拼接\nfunc BuildSignStringOrdered(params map[string]string, orderedKeys []string) string {\n var parts []string\n for _, k := range orderedKeys {\n if v, ok := params[k]; ok \u0026\u0026 v != \"\" {\n parts = append(parts, fmt.Sprintf(\"%s=%s\", k, v))\n }\n }\n return strings.Join(parts, \"\u0026\")\n}\n\n\n注意事项\n- 确认文档指定的排序规则(字典序/固定顺序)\n- 确认空值是否要包含(通常排除空值)\n- 确认是否需要URL编码\n- 注意排除签名字段本身\n", "tool": "param_concat" }

加密实现 2

{ "success": true, "data": "\n### 时间戳和随机数生成规范\n\n代码模板\n\ngo\npackage crypto\n\nimport (\n \"crypto/rand\"\n \"fmt\"\n \"math/big\"\n \"time\"\n)\n\n// GenerateTimestamp 生成秒级时间戳\nfunc GenerateTimestamp() string {\n return fmt.Sprintf(\"%d\", time.Now().Unix())\n}\n\n// GenerateTimestampMillis 生成毫秒级时间戳\nfunc GenerateTimestampMillis() string {\n return fmt.Sprintf(\"%d\", time.Now().UnixMilli())\n}\n\n// GenerateNonce 生成指定长度的随机字符串(加密安全)\nfunc GenerateNonce(length int) (string, error) {\n const charset = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\"\n b := make([]byte, length)\n for i := range b {\n num, err := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))\n if err != nil {\n return \"\", err\n }\n b[i] = charset[num.Int64()]\n }\n return string(b), nil\n}\n\n\n注意事项\n- 确认文档要求的是秒级还是毫秒级时间戳\n- 确认nonce的长度要求通常16-32位\n- 生产环境建议使用加密安全的随机数生成器\n", "tool": "nonce_timestamp" }