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