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

982 lines
38 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

现在我将根据文档生成完整的 Go SDK 代码工程。
## 分析
**认证机制**:客户应用使用 `HmacSHA256` 算法,密钥为 `client-secret`,签名数据为 `timestamp + nonce`,结果 Base64 编码。需要生成 timestamp 和 nonce。
**接口列表**
1. 订单开票POST
2. 开票状态查询POST
3. 创建付款单据POST
4. 支付完成通知(回调,客户提供接口)
5. 支付状态查询POST
**通知机制**:客户提供接口,平台调用,响应直接返回字符串 `SUCCESS``FAILED`
现在开始生成所有文件:
---
// File: intelligence_finance_v1/go.mod
```go
module intelligence_finance_v1
go 1.21
```
---
// File: intelligence_finance_v1/errors.go
```go
package intelligence_finance_v1
import "fmt"
// 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
```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
```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 支付完成通知数据接口4
type 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/data
type Notification struct {
BizType string `json:"bizType"` // 业务类型
BizID string `json:"bizId"` // 业务ID
Data string `json:"data"` // 业务数据
}
```
---
// File: intelligence_finance_v1/client.go
```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/data
func (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
```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\n```go\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\n```go\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"
}