1307 lines
44 KiB
Markdown
1307 lines
44 KiB
Markdown
// File: intelligence_finance_v1_ga/go.mod
|
||
```go
|
||
module intelligence_finance_v1_ga
|
||
|
||
go 1.21
|
||
```
|
||
|
||
// File: intelligence_finance_v1_ga/errors.go
|
||
```go
|
||
package intelligence_finance_v1_ga
|
||
|
||
import "fmt"
|
||
|
||
// APIError 表示 HTTP 层面的错误(非 200 响应)。
|
||
type APIError struct {
|
||
// StatusCode HTTP 状态码
|
||
StatusCode int
|
||
// Body 响应体原始内容
|
||
Body string
|
||
}
|
||
|
||
// Error 实现 error 接口。
|
||
func (e *APIError) Error() string {
|
||
return fmt.Sprintf("api error: status=%d body=%s", e.StatusCode, e.Body)
|
||
}
|
||
|
||
// BusinessError 表示业务层面的错误(通用响应结构中 code 非成功值)。
|
||
type BusinessError struct {
|
||
// Code 业务错误码
|
||
Code int
|
||
// Msg 业务错误描述
|
||
Msg string
|
||
}
|
||
|
||
// Error 实现 error 接口。
|
||
func (e *BusinessError) Error() string {
|
||
return fmt.Sprintf("business error: code=%d msg=%s", e.Code, e.Msg)
|
||
}
|
||
|
||
// NotificationError 表示通知处理失败。
|
||
type NotificationError struct {
|
||
// Reason 失败原因
|
||
Reason string
|
||
}
|
||
|
||
// Error 实现 error 接口。
|
||
func (e *NotificationError) Error() string {
|
||
return fmt.Sprintf("notification error: %s", e.Reason)
|
||
}
|
||
```
|
||
|
||
// File: intelligence_finance_v1_ga/crypto.go
|
||
```go
|
||
package intelligence_finance_v1_ga
|
||
|
||
import (
|
||
"crypto/hmac"
|
||
"crypto/rand"
|
||
"crypto/sha256"
|
||
"encoding/base64"
|
||
"fmt"
|
||
"math/big"
|
||
"sort"
|
||
"strings"
|
||
"time"
|
||
)
|
||
|
||
// GenerateTimestamp 生成秒级时间戳。
|
||
func GenerateTimestamp() string {
|
||
return fmt.Sprintf("%d", time.Now().Unix())
|
||
}
|
||
|
||
// GenerateTimestampMillis 生成毫秒级时间戳。
|
||
func GenerateTimestampMillis() string {
|
||
return fmt.Sprintf("%d", time.Now().UnixMilli())
|
||
}
|
||
|
||
// GenerateNonce 生成指定长度的随机字符串(加密安全)。
|
||
func GenerateNonce(length int) (string, error) {
|
||
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||
b := make([]byte, length)
|
||
for i := range b {
|
||
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
b[i] = charset[num.Int64()]
|
||
}
|
||
return string(b), nil
|
||
}
|
||
|
||
// HmacSHA256Base64 使用 HmacSHA256 算法对 data 进行签名,密钥为 key,结果 Base64 编码。
|
||
// 对应文档中客户应用签名算法:HmacSHA256,密钥为 client-secret,
|
||
// 签名数据为 x-bfl-signature-timestamp + x-bfl-signature-nonce。
|
||
func HmacSHA256Base64(key, data string) string {
|
||
h := hmac.New(sha256.New, []byte(key))
|
||
h.Write([]byte(data))
|
||
return base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||
}
|
||
|
||
// BuildSignString 方式1:字典序排序拼接,排除空值与签名字段本身。
|
||
func BuildSignString(params map[string]string) string {
|
||
keys := make([]string, 0, len(params))
|
||
for k, v := range params {
|
||
if v != "" && k != "sign" && k != "signature" {
|
||
keys = append(keys, k)
|
||
}
|
||
}
|
||
sort.Strings(keys)
|
||
|
||
var parts []string
|
||
for _, k := range keys {
|
||
parts = append(parts, fmt.Sprintf("%s=%s", k, params[k]))
|
||
}
|
||
return strings.Join(parts, "&")
|
||
}
|
||
|
||
// BuildSignStringOrdered 方式2:固定顺序拼接,排除空值。
|
||
func BuildSignStringOrdered(params map[string]string, orderedKeys []string) string {
|
||
var parts []string
|
||
for _, k := range orderedKeys {
|
||
if v, ok := params[k]; ok && v != "" {
|
||
parts = append(parts, fmt.Sprintf("%s=%s", k, v))
|
||
}
|
||
}
|
||
return strings.Join(parts, "&")
|
||
}
|
||
```
|
||
|
||
// File: intelligence_finance_v1_ga/types.go
|
||
```go
|
||
package intelligence_finance_v1_ga
|
||
|
||
import "encoding/json"
|
||
|
||
// ==================== 通用 ====================
|
||
|
||
// CommonResponse 通用响应结构。
|
||
type CommonResponse struct {
|
||
Code int `json:"code"`
|
||
Msg string `json:"msg"`
|
||
Data json.RawMessage `json:"data"`
|
||
}
|
||
|
||
// ==================== 接口1:订单开票 ====================
|
||
|
||
// InvoiceApplyRequest 提交订单开票申请请求。
|
||
type InvoiceApplyRequest 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 float64 `json:"amountIncludeTax"`
|
||
// Specs 规格型号
|
||
Specs string `json:"specs,omitempty"`
|
||
// Unit 计量单位(如:台、个、次)
|
||
Unit string `json:"unit,omitempty"`
|
||
// Quantity 数量
|
||
Quantity float64 `json:"quantity"`
|
||
// Discount 折扣金额(无折扣传0)
|
||
Discount float64 `json:"discount,omitempty"`
|
||
// TaxSign 是否含税:0-不含税;1-含税(默认建议传1)
|
||
TaxSign int `json:"taxSign,omitempty"`
|
||
// TaxRate 税率(小数形式,如0.13表示13%)
|
||
TaxRate float64 `json:"taxRate,omitempty"`
|
||
}
|
||
|
||
// InvoiceApplyResponse 提交订单开票申请响应。
|
||
type InvoiceApplyResponse 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 string `json:"deviceCode"`
|
||
Drawer string `json:"drawer"`
|
||
Email string `json:"email"`
|
||
InvoiceType string `json:"invoiceType"`
|
||
IssueType string `json:"issueType"`
|
||
ListFlag string `json:"listFlag"`
|
||
Mobile string `json:"mobile"`
|
||
OriginalInvCode string `json:"originalInvCode,omitempty"`
|
||
OriginalInvNo string `json:"originalInvNo,omitempty"`
|
||
AdditionInfo string `json:"additionInfo,omitempty"`
|
||
Payee string `json:"payee"`
|
||
PurchaserAddress string `json:"purchaserAddress"`
|
||
PurchaserBankAccount string `json:"purchaserBankAccount"`
|
||
PurchaserBankName string `json:"purchaserBankName"`
|
||
PurchaserName string `json:"purchaserName"`
|
||
PurchaserTaxNo string `json:"purchaserTaxNo"`
|
||
PurchaserTel string `json:"purchaserTel"`
|
||
NaturalPerson string `json:"naturalPerson,omitempty"`
|
||
Remark string `json:"remark"`
|
||
Reviewer string `json:"reviewer"`
|
||
SellerAddress string `json:"sellerAddress,omitempty"`
|
||
SellerBankAccount string `json:"sellerBankAccount"`
|
||
SellerBankName string `json:"sellerBankName"`
|
||
SellerName string `json:"sellerName"`
|
||
CheckCode string `json:"checkCode"`
|
||
CipherText string `json:"cipherText"`
|
||
DrewDate string `json:"drewDate,omitempty"`
|
||
InvoiceCode string `json:"invoiceCode"`
|
||
InvoiceNo string `json:"invoiceNo"`
|
||
InvoiceStatus string `json:"invoiceStatus"`
|
||
LayoutFileURL string `json:"layoutFileUrl,omitempty"`
|
||
PDFURL string `json:"pdfUrl,omitempty"`
|
||
OFDURL string `json:"ofdUrl,omitempty"`
|
||
XMLURL string `json:"xmlUrl,omitempty"`
|
||
TotalExcludeTax string `json:"totalExcludeTax"`
|
||
TotalIncludeTax string `json:"totalIncludeTax"`
|
||
TotalTaxAmount string `json:"totalTaxAmount"`
|
||
LevyingType string `json:"levyingType"`
|
||
Details []InvoiceDetail `json:"details"`
|
||
}
|
||
|
||
// InvoiceDetail 发票商品明细。
|
||
type InvoiceDetail struct {
|
||
Amount string `json:"amount,omitempty"`
|
||
Quantity string `json:"quantity,omitempty"`
|
||
DeductionAmount string `json:"deductionAmount,omitempty"`
|
||
TaxAmount string `json:"taxAmount,omitempty"`
|
||
ItemTitle string `json:"itemTitle"`
|
||
TaxCode string `json:"taxCode"`
|
||
ItemType string `json:"itemType"`
|
||
ItemName string `json:"itemName"`
|
||
Specs string `json:"specs,omitempty"`
|
||
TaxFreePolicy string `json:"taxFreePolicy"`
|
||
PreferentialPolicy string `json:"preferentialPolicy"`
|
||
TaxRate string `json:"taxRate"`
|
||
TaxSign string `json:"taxSign"`
|
||
Unit string `json:"unit,omitempty"`
|
||
UnitPrice string `json:"unitPrice,omitempty"`
|
||
}
|
||
|
||
// ==================== 接口2:开票状态查询 ====================
|
||
|
||
// 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字段一致
|
||
Data []InvoiceData `json:"data"`
|
||
}
|
||
|
||
// ==================== 接口3:创建付款单据 ====================
|
||
|
||
// CreatePaymentRequest 创建付款单据请求。
|
||
type CreatePaymentRequest struct {
|
||
Code string `json:"code"`
|
||
YidaAppType string `json:"yidaAppType,omitempty"`
|
||
EmpAccountUserID string `json:"empAccountUserId,omitempty"`
|
||
Department *Department `json:"department,omitempty"`
|
||
Usage string `json:"usage,omitempty"`
|
||
PaymentUserID string `json:"paymentUserId,omitempty"`
|
||
Customer *Customer `json:"customer,omitempty"`
|
||
PrincipalID string `json:"principalId,omitempty"`
|
||
Remark string `json:"remark,omitempty"`
|
||
Supplier *Supplier `json:"supplier,omitempty"`
|
||
Title string `json:"title,omitempty"`
|
||
Project *Project `json:"project,omitempty"`
|
||
PaymentUserIDListStr string `json:"paymentUserIdListStr,omitempty"`
|
||
NeedPayment bool `json:"needPayment,omitempty"`
|
||
PaymentDetailListJSON string `json:"paymentDetailListJsonStr,omitempty"`
|
||
PaymentDetailList []PaymentDetail `json:"paymentDetailList,omitempty"`
|
||
Company *Company `json:"company,omitempty"`
|
||
Amount string `json:"amount,omitempty"`
|
||
RecipientAccountInfo *RecipientAccount `json:"recipientAccountInfo,omitempty"`
|
||
EnterpriseAccount *EnterpriseAccount `json:"enterpriseAccount,omitempty"`
|
||
Category []Category `json:"category,omitempty"`
|
||
UserID string `json:"userId"`
|
||
OccurDate int64 `json:"occurDate,omitempty"`
|
||
Product *Product `json:"product,omitempty"`
|
||
YidaFormUUID string `json:"yidaFormUuid,omitempty"`
|
||
CanEditPaymentInfo bool `json:"canEditPaymentInfo,omitempty"`
|
||
PaymentUserIDList []string `json:"paymentUserIdList,omitempty"`
|
||
YidaProcInsID string `json:"yidaProcInsId,omitempty"`
|
||
SyncPaymentOrder bool `json:"syncPaymentOrder,omitempty"`
|
||
}
|
||
|
||
// Department 部门信息。
|
||
type Department struct {
|
||
Code string `json:"code,omitempty"`
|
||
Name string `json:"name"`
|
||
}
|
||
|
||
// Customer 客户信息。
|
||
type Customer struct {
|
||
Code string `json:"code,omitempty"`
|
||
Name string `json:"name"`
|
||
}
|
||
|
||
// Supplier 供应商信息。
|
||
type Supplier struct {
|
||
Code string `json:"code,omitempty"`
|
||
Name string `json:"name"`
|
||
}
|
||
|
||
// Project 项目信息。
|
||
type Project struct {
|
||
Code string `json:"code,omitempty"`
|
||
Name string `json:"name"`
|
||
}
|
||
|
||
// Category 收支类别信息。
|
||
type Category struct {
|
||
Code string `json:"code,omitempty"`
|
||
Name string `json:"name"`
|
||
}
|
||
|
||
// Product 商品信息。
|
||
type Product struct {
|
||
Code string `json:"code,omitempty"`
|
||
Name string `json:"name"`
|
||
}
|
||
|
||
// Company 企业主体信息。
|
||
type Company struct {
|
||
Code string `json:"code,omitempty"`
|
||
Name string `json:"name"`
|
||
}
|
||
|
||
// EnterpriseAccount 企业账号信息。
|
||
type EnterpriseAccount struct {
|
||
EnterpriseAccountCode string `json:"enterpriseAccountCode,omitempty"`
|
||
AccountCategory string `json:"accountCategory"`
|
||
AccountType string `json:"accountType,omitempty"`
|
||
CardNo string `json:"cardNo,omitempty"`
|
||
AccountName string `json:"accountName,omitempty"`
|
||
OfficialNumber string `json:"officialNumber,omitempty"`
|
||
OfficialName string `json:"officialName,omitempty"`
|
||
Name string `json:"name,omitempty"`
|
||
Code string `json:"code,omitempty"`
|
||
City string `json:"city,omitempty"`
|
||
Province string `json:"province,omitempty"`
|
||
}
|
||
|
||
// RecipientAccount 收款账户信息。
|
||
type RecipientAccount struct {
|
||
AccountCategory string `json:"accountCategory"`
|
||
AccountType string `json:"accountType,omitempty"`
|
||
CardNo string `json:"cardNo,omitempty"`
|
||
AccountName string `json:"accountName,omitempty"`
|
||
}
|
||
|
||
// PaymentDetail 付款明细。
|
||
type PaymentDetail struct {
|
||
Amount string `json:"amount,omitempty"`
|
||
InvoiceInfo *InvoiceInfo `json:"invoiceInfo,omitempty"`
|
||
ProductCode string `json:"productCode,omitempty"`
|
||
ProjectCode string `json:"projectCode,omitempty"`
|
||
Remark string `json:"remark,omitempty"`
|
||
PrincipalID string `json:"principalId,omitempty"`
|
||
Tax string `json:"tax,omitempty"`
|
||
}
|
||
|
||
// InvoiceInfo 付款明细发票信息。
|
||
type InvoiceInfo struct {
|
||
InvoiceNo string `json:"invoiceNo,omitempty"`
|
||
InvoiceCode string `json:"invoiceCode,omitempty"`
|
||
}
|
||
|
||
// CreatePaymentResponse 创建付款单据响应。
|
||
type CreatePaymentResponse struct {
|
||
// Code 单据唯一编码
|
||
Code string `json:"code"`
|
||
}
|
||
|
||
// ==================== 接口4:支付完成通知 ====================
|
||
|
||
// PaymentNotifyData 支付完成通知数据。
|
||
type PaymentNotifyData struct {
|
||
Code string `json:"code"`
|
||
InstanceID string `json:"instanceId"`
|
||
CorpID string `json:"corpId"`
|
||
PaymentStatus string `json:"paymentStatus"`
|
||
PaymentTime string `json:"paymentTime"`
|
||
UserID string `json:"userId"`
|
||
FailReason string `json:"failReason,omitempty"`
|
||
PayerAccountInfo *PayerAccountInfo `json:"payerAccountInfo,omitempty"`
|
||
PayeeAccountInfo *PayeeAccountInfo `json:"payeeAccountInfo,omitempty"`
|
||
RelatedRowNumberList []string `json:"relatedRowNumberList,omitempty"`
|
||
Source string `json:"source,omitempty"`
|
||
Template string `json:"template,omitempty"`
|
||
Amount string `json:"amount,omitempty"`
|
||
}
|
||
|
||
// PayerAccountInfo 付款账户信息。
|
||
type PayerAccountInfo struct {
|
||
BankOpenDTO *BankOpenDTO `json:"bankOpenDTO,omitempty"`
|
||
EnterpriseAccountCode string `json:"enterpriseAccountCode,omitempty"`
|
||
AccountType string `json:"accountType,omitempty"`
|
||
}
|
||
|
||
// PayeeAccountInfo 收款账户信息。
|
||
type PayeeAccountInfo struct {
|
||
BankOpenDTO *BankOpenDTO `json:"bankOpenDTO,omitempty"`
|
||
}
|
||
|
||
// BankOpenDTO 银行信息。
|
||
type BankOpenDTO struct {
|
||
BankCode string `json:"bankCode,omitempty"`
|
||
BankName string `json:"bankName,omitempty"`
|
||
BankBranchCode string `json:"bankBranchCode,omitempty"`
|
||
BankBranchName string `json:"bankBranchName,omitempty"`
|
||
AccountName string `json:"accountName,omitempty"`
|
||
BankCardNo string `json:"bankCardNo,omitempty"`
|
||
Type string `json:"type,omitempty"`
|
||
}
|
||
|
||
// 支付状态枚举。
|
||
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" // 对私银行卡
|
||
)
|
||
|
||
// ==================== 接口5:支付状态查询 ====================
|
||
|
||
// PaymentStatusQueryRequest 查询支付状态请求。
|
||
type PaymentStatusQueryRequest struct {
|
||
Code string `json:"code"`
|
||
UserID string `json:"userId"`
|
||
}
|
||
|
||
// ==================== 通用通知机制 ====================
|
||
|
||
// Notification 通用通知数据。
|
||
type Notification struct {
|
||
BizType string `json:"bizType"`
|
||
BizID string `json:"bizId"`
|
||
Data string `json:"data,omitempty"`
|
||
}
|
||
|
||
// 通知响应常量。
|
||
const (
|
||
NotifyResultSuccess = "SUCCESS" // 成功
|
||
NotifyResultFailed = "FAILED" // 失败
|
||
)
|
||
|
||
// ==================== 接口6:创建供应商 ====================
|
||
|
||
// CreateSupplierRequest 创建供应商请求。
|
||
type CreateSupplierRequest struct {
|
||
InvokeID string `json:"invokeId,omitempty"`
|
||
UserID string `json:"userId"`
|
||
Data string `json:"data,omitempty"`
|
||
BizType string `json:"bizType"`
|
||
}
|
||
|
||
// SupplierBizData 供应商业务数据。
|
||
type SupplierBizData struct {
|
||
CorpID string `json:"corpId"`
|
||
Creator string `json:"creator"`
|
||
SupplierInfo *SupplierInfo `json:"supplierInfo,omitempty"`
|
||
}
|
||
|
||
// SupplierInfo 供应商数据。
|
||
type SupplierInfo struct {
|
||
Name string `json:"name"`
|
||
CorpID string `json:"corpId"`
|
||
UserDefineCode string `json:"userDefineCode,omitempty"`
|
||
Description string `json:"description,omitempty"`
|
||
ContactAddress string `json:"contactAddress,omitempty"`
|
||
ContactCompanyTelephone string `json:"contactCompanyTelephone,omitempty"`
|
||
ContactEmail string `json:"contactEmail,omitempty"`
|
||
ContactName string `json:"contactName,omitempty"`
|
||
ContactTelephone string `json:"contactTelephone,omitempty"`
|
||
Creator string `json:"creator"`
|
||
InvoiceAccount string `json:"invoiceAccount,omitempty"`
|
||
InvoiceAddress string `json:"invoiceAddress,omitempty"`
|
||
InvoiceBankName string `json:"invoiceBankName,omitempty"`
|
||
InvoiceName string `json:"invoiceName,omitempty"`
|
||
InvoiceTaxNo string `json:"invoiceTaxNo,omitempty"`
|
||
PurchaserTel string `json:"purchaserTel,omitempty"`
|
||
BankName string `json:"bankName,omitempty"`
|
||
InvoiceTelephone string `json:"invoiceTelephone,omitempty"`
|
||
AccountName string `json:"accountName,omitempty"`
|
||
AccountType string `json:"accountType,omitempty"`
|
||
}
|
||
|
||
// CreateSupplierResponse 创建供应商响应。
|
||
type CreateSupplierResponse struct {
|
||
InvokeID string `json:"invokeId,omitempty"`
|
||
BizType string `json:"bizType"`
|
||
Data string `json:"data,omitempty"`
|
||
}
|
||
|
||
// SupplierResponseData 供应商响应数据。
|
||
type SupplierResponseData struct {
|
||
Result string `json:"result,omitempty"`
|
||
Success bool `json:"success"`
|
||
}
|
||
|
||
// SupplierResult 供应商信息(响应)。
|
||
type SupplierResult struct {
|
||
SupplierID string `json:"supplierId"`
|
||
SupplierName string `json:"supplierName"`
|
||
Name string `json:"name"`
|
||
CorpID string `json:"corpId"`
|
||
UserDefineCode string `json:"userDefineCode,omitempty"`
|
||
Description string `json:"description,omitempty"`
|
||
ContactAddress string `json:"contactAddress,omitempty"`
|
||
ContactCompanyTelephone string `json:"contactCompanyTelephone,omitempty"`
|
||
ContactEmail string `json:"contactEmail,omitempty"`
|
||
ContactName string `json:"contactName,omitempty"`
|
||
ContactTelephone string `json:"contactTelephone,omitempty"`
|
||
Creator string `json:"creator"`
|
||
InvoiceAccount string `json:"invoiceAccount,omitempty"`
|
||
InvoiceAddress string `json:"invoiceAddress,omitempty"`
|
||
InvoiceBankName string `json:"invoiceBankName,omitempty"`
|
||
InvoiceName string `json:"invoiceName,omitempty"`
|
||
InvoiceTaxNo string `json:"invoiceTaxNo,omitempty"`
|
||
CreateTime int64 `json:"createTime,omitempty"`
|
||
ModifiedTime int64 `json:"modifiedTime,omitempty"`
|
||
Code string `json:"code,omitempty"`
|
||
ID int64 `json:"id,omitempty"`
|
||
Status string `json:"status,omitempty"`
|
||
}
|
||
|
||
// ==================== 接口7:根据名称查询供应商 ====================
|
||
|
||
// QuerySupplierByNameRequest 根据名称查询供应商请求。
|
||
type QuerySupplierByNameRequest struct {
|
||
InvokeID string `json:"invokeId,omitempty"`
|
||
UserID string `json:"userId"`
|
||
Data string `json:"data,omitempty"`
|
||
BizType string `json:"bizType"`
|
||
}
|
||
|
||
// QueryByName 按名称查询业务数据。
|
||
type QueryByName struct {
|
||
CorpID string `json:"corpId"`
|
||
Name string `json:"name"`
|
||
}
|
||
|
||
// QuerySupplierByNameResponse 根据名称查询供应商响应。
|
||
type QuerySupplierByNameResponse struct {
|
||
InvokeID string `json:"invokeId,omitempty"`
|
||
BizType string `json:"bizType"`
|
||
Data string `json:"data,omitempty"`
|
||
Result string `json:"result,omitempty"`
|
||
Success bool `json:"success"`
|
||
}
|
||
|
||
// ==================== 接口8:更新供应商 ====================
|
||
|
||
// UpdateSupplierRequest 更新供应商请求。
|
||
type UpdateSupplierRequest struct {
|
||
InvokeID string `json:"invokeId,omitempty"`
|
||
UserID string `json:"userId"`
|
||
Data string `json:"data,omitempty"`
|
||
BizType string `json:"bizType"`
|
||
}
|
||
|
||
// UpdateSupplierBizData 更新供应商业务数据。
|
||
type UpdateSupplierBizData struct {
|
||
SupplierID string `json:"supplierId"`
|
||
Name string `json:"name"`
|
||
CorpID string `json:"corpId"`
|
||
UserDefineCode string `json:"userDefineCode,omitempty"`
|
||
Description string `json:"description,omitempty"`
|
||
ContactAddress string `json:"contactAddress,omitempty"`
|
||
ContactCompanyTelephone string `json:"contactCompanyTelephone,omitempty"`
|
||
ContactEmail string `json:"contactEmail,omitempty"`
|
||
ContactName string `json:"contactName,omitempty"`
|
||
ContactTelephone string `json:"contactTelephone,omitempty"`
|
||
UserID string `json:"userId"`
|
||
InvoiceAccount string `json:"invoiceAccount,omitempty"`
|
||
InvoiceAddress string `json:"invoiceAddress,omitempty"`
|
||
InvoiceBankName string `json:"invoiceBankName,omitempty"`
|
||
InvoiceName string `json:"invoiceName,omitempty"`
|
||
InvoiceTaxNo string `json:"invoiceTaxNo,omitempty"`
|
||
PurchaserTel string `json:"purchaserTel,omitempty"`
|
||
BankName string `json:"bankName,omitempty"`
|
||
InvoiceTelephone string `json:"invoiceTelephone,omitempty"`
|
||
AccountName string `json:"accountName,omitempty"`
|
||
AccountType string `json:"accountType,omitempty"`
|
||
BankBranchCode string `json:"bankBranchCode,omitempty"`
|
||
BankCode string `json:"bankCode,omitempty"`
|
||
BankCity string `json:"bankCity,omitempty"`
|
||
BankProvince string `json:"bankProvince,omitempty"`
|
||
}
|
||
|
||
// UpdateSupplierResponse 更新供应商响应。
|
||
type UpdateSupplierResponse struct {
|
||
InvokeID string `json:"invokeId,omitempty"`
|
||
BizType string `json:"bizType"`
|
||
Data string `json:"data,omitempty"`
|
||
}
|
||
|
||
// ==================== 接口9:根据用户自定义编码查询供应商 ====================
|
||
|
||
// QuerySupplierByUserDefineCodeRequest 根据用户自定义编码查询供应商请求。
|
||
type QuerySupplierByUserDefineCodeRequest struct {
|
||
InvokeID string `json:"invokeId,omitempty"`
|
||
UserID string `json:"userId"`
|
||
Data string `json:"data,omitempty"`
|
||
BizType string `json:"bizType"`
|
||
}
|
||
|
||
// QueryByUserDefineCode 按用户自定义编码查询业务数据。
|
||
type QueryByUserDefineCode struct {
|
||
CorpID string `json:"corpId"`
|
||
UserDefineCode string `json:"userDefineCode"`
|
||
}
|
||
|
||
// QuerySupplierByUserDefineCodeResponse 根据用户自定义编码查询供应商响应。
|
||
type QuerySupplierByUserDefineCodeResponse struct {
|
||
InvokeID string `json:"invokeId,omitempty"`
|
||
BizType string `json:"bizType"`
|
||
Data string `json:"data,omitempty"`
|
||
}
|
||
```
|
||
|
||
// File: intelligence_finance_v1_ga/client.go
|
||
```go
|
||
package intelligence_finance_v1_ga
|
||
|
||
import (
|
||
"bytes"
|
||
"context"
|
||
"encoding/json"
|
||
"fmt"
|
||
"io"
|
||
"net/http"
|
||
"strings"
|
||
"time"
|
||
)
|
||
|
||
// 供应商业务类型常量。
|
||
const (
|
||
BizTypeCreateSupplier = "create_supplier"
|
||
BizTypeQuerySupplierByName = "query_supplier_by_name"
|
||
BizTypeUpdateSupplier = "update_supplier"
|
||
BizTypeQuerySupplierByUserDefine = "query_supplier_by_userDefineCode"
|
||
)
|
||
|
||
// Client 业财连接接口客户端。
|
||
type Client struct {
|
||
baseURL string
|
||
tenantID string
|
||
clientID string
|
||
clientSecret string
|
||
signEnabled bool
|
||
httpClient *http.Client
|
||
|
||
// 各接口路径(可通过 Option 覆盖)
|
||
pathSubmitInvoiceApply string
|
||
pathQueryInvoiceStatus string
|
||
pathCreatePayment string
|
||
pathQueryPaymentStatus string
|
||
pathCreateSupplier string
|
||
pathQuerySupplierByName string
|
||
pathUpdateSupplier string
|
||
pathQuerySupplierByUserDefineCode string
|
||
}
|
||
|
||
// Option 客户端配置项。
|
||
type Option func(*Client)
|
||
|
||
// WithHTTPClient 自定义 http.Client。
|
||
func WithHTTPClient(c *http.Client) Option {
|
||
return func(cli *Client) { cli.httpClient = c }
|
||
}
|
||
|
||
// WithSignEnabled 是否启用请求签名(钉钉AI表格场景可关闭,签名由AI表格自动完成)。
|
||
func WithSignEnabled(enabled bool) Option {
|
||
return func(cli *Client) { cli.signEnabled = enabled }
|
||
}
|
||
|
||
// WithPathSubmitInvoiceApply 设置提交订单开票申请接口路径。
|
||
func WithPathSubmitInvoiceApply(path string) Option {
|
||
return func(cli *Client) { cli.pathSubmitInvoiceApply = path }
|
||
}
|
||
|
||
// WithPathQueryInvoiceStatus 设置开票状态查询接口路径。
|
||
func WithPathQueryInvoiceStatus(path string) Option {
|
||
return func(cli *Client) { cli.pathQueryInvoiceStatus = path }
|
||
}
|
||
|
||
// WithPathCreatePayment 设置创建付款单据接口路径。
|
||
func WithPathCreatePayment(path string) Option {
|
||
return func(cli *Client) { cli.pathCreatePayment = path }
|
||
}
|
||
|
||
// WithPathQueryPaymentStatus 设置支付状态查询接口路径。
|
||
func WithPathQueryPaymentStatus(path string) Option {
|
||
return func(cli *Client) { cli.pathQueryPaymentStatus = path }
|
||
}
|
||
|
||
// WithPathCreateSupplier 设置创建供应商接口路径。
|
||
func WithPathCreateSupplier(path string) Option {
|
||
return func(cli *Client) { cli.pathCreateSupplier = path }
|
||
}
|
||
|
||
// WithPathQuerySupplierByName 设置根据名称查询供应商接口路径。
|
||
func WithPathQuerySupplierByName(path string) Option {
|
||
return func(cli *Client) { cli.pathQuerySupplierByName = path }
|
||
}
|
||
|
||
// WithPathUpdateSupplier 设置更新供应商接口路径。
|
||
func WithPathUpdateSupplier(path string) Option {
|
||
return func(cli *Client) { cli.pathUpdateSupplier = path }
|
||
}
|
||
|
||
// WithPathQuerySupplierByUserDefineCode 设置根据用户自定义编码查询供应商接口路径。
|
||
func WithPathQuerySupplierByUserDefineCode(path string) Option {
|
||
return func(cli *Client) { cli.pathQuerySupplierByUserDefineCode = path }
|
||
}
|
||
|
||
// NewClient 创建业财连接接口客户端。
|
||
//
|
||
// baseURL 为平台接口根地址;tenantID 为平台分配的租户唯一标识;
|
||
// clientID 为平台分配的应用标识(钉钉AI表格固定为 "dd-ai-table");
|
||
// clientSecret 为平台分配的密钥,用于请求签名。
|
||
func NewClient(baseURL, tenantID, clientID, clientSecret string, opts ...Option) *Client {
|
||
c := &Client{
|
||
baseURL: strings.TrimRight(baseURL, "/"),
|
||
tenantID: tenantID,
|
||
clientID: clientID,
|
||
clientSecret: clientSecret,
|
||
signEnabled: true,
|
||
httpClient: &http.Client{Timeout: 30 * time.Second},
|
||
|
||
pathSubmitInvoiceApply: "/invoice/apply",
|
||
pathQueryInvoiceStatus: "/invoice/status",
|
||
pathCreatePayment: "/payment/create",
|
||
pathQueryPaymentStatus: "/payment/status",
|
||
pathCreateSupplier: "/supplier/create",
|
||
pathQuerySupplierByName: "/supplier/queryByName",
|
||
pathUpdateSupplier: "/supplier/update",
|
||
pathQuerySupplierByUserDefineCode: "/supplier/queryByUserDefineCode",
|
||
}
|
||
for _, opt := range opts {
|
||
opt(c)
|
||
}
|
||
return c
|
||
}
|
||
|
||
// doRequest 发送 POST 请求并解析响应。
|
||
func (c *Client) doRequest(ctx context.Context, path string, reqBody, respBody interface{}) error {
|
||
body, err := json.Marshal(reqBody)
|
||
if err != nil {
|
||
return fmt.Errorf("marshal request: %w", err)
|
||
}
|
||
|
||
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, c.baseURL+path, bytes.NewReader(body))
|
||
if err != nil {
|
||
return fmt.Errorf("new request: %w", err)
|
||
}
|
||
|
||
httpReq.Header.Set("Content-Type", "application/json")
|
||
httpReq.Header.Set("tenant-id", c.tenantID)
|
||
httpReq.Header.Set("client-id", c.clientID)
|
||
|
||
if c.signEnabled {
|
||
timestamp := GenerateTimestamp()
|
||
nonce, err := GenerateNonce(32)
|
||
if err != nil {
|
||
return fmt.Errorf("generate nonce: %w", err)
|
||
}
|
||
sign := HmacSHA256Base64(c.clientSecret, timestamp+nonce)
|
||
httpReq.Header.Set("x-bfl-signature-timestamp", timestamp)
|
||
httpReq.Header.Set("x-bfl-signature-nonce", nonce)
|
||
httpReq.Header.Set("x-bfl-signature", sign)
|
||
}
|
||
|
||
httpResp, err := c.httpClient.Do(httpReq)
|
||
if err != nil {
|
||
return fmt.Errorf("do request: %w", err)
|
||
}
|
||
defer httpResp.Body.Close()
|
||
|
||
respData, err := io.ReadAll(httpResp.Body)
|
||
if err != nil {
|
||
return fmt.Errorf("read response: %w", err)
|
||
}
|
||
|
||
if httpResp.StatusCode != http.StatusOK {
|
||
return &APIError{StatusCode: httpResp.StatusCode, Body: string(respData)}
|
||
}
|
||
|
||
if err := json.Unmarshal(respData, respBody); err != nil {
|
||
return fmt.Errorf("unmarshal response: %w", err)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// checkBusinessCode 校验通用响应结构中的业务 code。
|
||
func checkBusinessCode(code int, msg string) error {
|
||
if code != 0 && code != 200 {
|
||
return &BusinessError{Code: code, Msg: msg}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// SubmitInvoiceApply 提交订单开票申请(接口1)。
|
||
func (c *Client) SubmitInvoiceApply(ctx context.Context, req *InvoiceApplyRequest) (*InvoiceApplyResponse, error) {
|
||
var envelope struct {
|
||
Code int `json:"code"`
|
||
Msg string `json:"msg"`
|
||
Data *InvoiceApplyResponse `json:"data"`
|
||
}
|
||
if err := c.doRequest(ctx, c.pathSubmitInvoiceApply, req, &envelope); err != nil {
|
||
return nil, err
|
||
}
|
||
if err := checkBusinessCode(envelope.Code, envelope.Msg); err != nil {
|
||
return nil, err
|
||
}
|
||
return envelope.Data, nil
|
||
}
|
||
|
||
// QueryInvoiceStatus 查询订单开票状态(接口2)。
|
||
func (c *Client) QueryInvoiceStatus(ctx context.Context, req *InvoiceStatusQueryRequest) (*InvoiceStatusQueryResponse, error) {
|
||
var envelope struct {
|
||
Code int `json:"code"`
|
||
Msg string `json:"msg"`
|
||
Data *InvoiceStatusQueryResponse `json:"data"`
|
||
}
|
||
if err := c.doRequest(ctx, c.pathQueryInvoiceStatus, req, &envelope); err != nil {
|
||
return nil, err
|
||
}
|
||
if err := checkBusinessCode(envelope.Code, envelope.Msg); err != nil {
|
||
return nil, err
|
||
}
|
||
return envelope.Data, nil
|
||
}
|
||
|
||
// CreatePayment 创建付款单据(接口3)。
|
||
func (c *Client) CreatePayment(ctx context.Context, req *CreatePaymentRequest) (*CreatePaymentResponse, error) {
|
||
var envelope struct {
|
||
Code int `json:"code"`
|
||
Msg string `json:"msg"`
|
||
Data *CreatePaymentResponse `json:"data"`
|
||
}
|
||
if err := c.doRequest(ctx, c.pathCreatePayment, req, &envelope); err != nil {
|
||
return nil, err
|
||
}
|
||
if err := checkBusinessCode(envelope.Code, envelope.Msg); err != nil {
|
||
return nil, err
|
||
}
|
||
return envelope.Data, nil
|
||
}
|
||
|
||
// QueryPaymentStatus 查询支付状态(接口5),响应数据与支付通知一致。
|
||
func (c *Client) QueryPaymentStatus(ctx context.Context, req *PaymentStatusQueryRequest) (*PaymentNotifyData, error) {
|
||
var envelope struct {
|
||
Code int `json:"code"`
|
||
Msg string `json:"msg"`
|
||
Data *PaymentNotifyData `json:"data"`
|
||
}
|
||
if err := c.doRequest(ctx, c.pathQueryPaymentStatus, req, &envelope); err != nil {
|
||
return nil, err
|
||
}
|
||
if err := checkBusinessCode(envelope.Code, envelope.Msg); err != nil {
|
||
return nil, err
|
||
}
|
||
return envelope.Data, nil
|
||
}
|
||
|
||
// CreateSupplier 创建供应商(接口6)。
|
||
func (c *Client) CreateSupplier(ctx context.Context, req *CreateSupplierRequest) (*CreateSupplierResponse, error) {
|
||
var envelope struct {
|
||
Code int `json:"code"`
|
||
Msg string `json:"msg"`
|
||
Data *CreateSupplierResponse `json:"data"`
|
||
}
|
||
if err := c.doRequest(ctx, c.pathCreateSupplier, req, &envelope); err != nil {
|
||
return nil, err
|
||
}
|
||
if err := checkBusinessCode(envelope.Code, envelope.Msg); err != nil {
|
||
return nil, err
|
||
}
|
||
return envelope.Data, nil
|
||
}
|
||
|
||
// QuerySupplierByName 根据名称查询供应商(接口7)。
|
||
func (c *Client) QuerySupplierByName(ctx context.Context, req *QuerySupplierByNameRequest) (*QuerySupplierByNameResponse, error) {
|
||
var envelope struct {
|
||
Code int `json:"code"`
|
||
Msg string `json:"msg"`
|
||
Data *QuerySupplierByNameResponse `json:"data"`
|
||
}
|
||
if err := c.doRequest(ctx, c.pathQuerySupplierByName, req, &envelope); err != nil {
|
||
return nil, err
|
||
}
|
||
if err := checkBusinessCode(envelope.Code, envelope.Msg); err != nil {
|
||
return nil, err
|
||
}
|
||
return envelope.Data, nil
|
||
}
|
||
|
||
// UpdateSupplier 更新供应商(接口8)。
|
||
func (c *Client) UpdateSupplier(ctx context.Context, req *UpdateSupplierRequest) (*UpdateSupplierResponse, error) {
|
||
var envelope struct {
|
||
Code int `json:"code"`
|
||
Msg string `json:"msg"`
|
||
Data *UpdateSupplierResponse `json:"data"`
|
||
}
|
||
if err := c.doRequest(ctx, c.pathUpdateSupplier, req, &envelope); err != nil {
|
||
return nil, err
|
||
}
|
||
if err := checkBusinessCode(envelope.Code, envelope.Msg); err != nil {
|
||
return nil, err
|
||
}
|
||
return envelope.Data, nil
|
||
}
|
||
|
||
// QuerySupplierByUserDefineCode 根据用户自定义编码查询供应商(接口9)。
|
||
func (c *Client) QuerySupplierByUserDefineCode(ctx context.Context, req *QuerySupplierByUserDefineCodeRequest) (*QuerySupplierByUserDefineCodeResponse, error) {
|
||
var envelope struct {
|
||
Code int `json:"code"`
|
||
Msg string `json:"msg"`
|
||
Data *QuerySupplierByUserDefineCodeResponse `json:"data"`
|
||
}
|
||
if err := c.doRequest(ctx, c.pathQuerySupplierByUserDefineCode, req, &envelope); err != nil {
|
||
return nil, err
|
||
}
|
||
if err := checkBusinessCode(envelope.Code, envelope.Msg); err != nil {
|
||
return nil, err
|
||
}
|
||
return envelope.Data, nil
|
||
}
|
||
|
||
// ==================== 通知机制辅助 ====================
|
||
|
||
// ParseNotification 解析通用通知数据。
|
||
func ParseNotification(body []byte) (*Notification, error) {
|
||
var n Notification
|
||
if err := json.Unmarshal(body, &n); err != nil {
|
||
return nil, fmt.Errorf("parse notification: %w", err)
|
||
}
|
||
return &n, nil
|
||
}
|
||
|
||
// ParsePaymentNotify 解析支付完成通知数据(接口4)。
|
||
func ParsePaymentNotify(body []byte) (*PaymentNotifyData, error) {
|
||
var n PaymentNotifyData
|
||
if err := json.Unmarshal(body, &n); err != nil {
|
||
return nil, fmt.Errorf("parse payment notify: %w", err)
|
||
}
|
||
return &n, nil
|
||
}
|
||
|
||
// VerifyNotificationSignature 校验通知签名。
|
||
//
|
||
// 平台使用客户提供的验签密钥对原始请求体进行 HmacSHA256 签名并 Base64 编码,
|
||
// 通过请求头 x-bfl-signature 传递。verifyKey 为客户在平台配置的验签密钥。
|
||
func VerifyNotificationSignature(verifyKey string, rawBody []byte, signature string) (bool, error) {
|
||
if signature == "" {
|
||
return false, &NotificationError{Reason: "empty signature"}
|
||
}
|
||
expected := HmacSHA256Base64(verifyKey, string(rawBody))
|
||
return hmacEqual(expected, signature), nil
|
||
}
|
||
|
||
// hmacEqual 常量时间比较两个 Base64 签名是否一致。
|
||
func hmacEqual(a, b string) bool {
|
||
if len(a) != len(b) {
|
||
return false
|
||
}
|
||
var v byte
|
||
for i := 0; i < len(a); i++ {
|
||
v |= a[i] ^ b[i]
|
||
}
|
||
return v == 0
|
||
}
|
||
```
|
||
|
||
// File: intelligence_finance_v1_ga/example_test.go
|
||
```go
|
||
package intelligence_finance_v1_ga
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"fmt"
|
||
"testing"
|
||
)
|
||
|
||
// ExampleClient_SubmitInvoiceApply 演示提交订单开票申请。
|
||
func ExampleClient_SubmitInvoiceApply() {
|
||
ctx := context.Background()
|
||
client := NewClient(
|
||
"https://api.example.com",
|
||
"your-tenant-id",
|
||
"dd-ai-table",
|
||
"your-client-secret",
|
||
)
|
||
|
||
req := &InvoiceApplyRequest{
|
||
OrderID: "ORDER-20240101-001",
|
||
InvoiceType: 9, // 数电普票
|
||
Purchaser: "某某科技有限公司",
|
||
TaxNum: "91330100XXXXXXXXXX",
|
||
Email: "finance@example.com",
|
||
Phone: "13800000000",
|
||
Products: []ProductItem{
|
||
{
|
||
ProductName: "软件服务",
|
||
RevenueCode: "3040201000000000000",
|
||
AmountIncludeTax: 11300,
|
||
Quantity: 1,
|
||
TaxRate: 0.13,
|
||
TaxSign: 1,
|
||
},
|
||
},
|
||
}
|
||
|
||
resp, err := client.SubmitInvoiceApply(ctx, req)
|
||
if err != nil {
|
||
fmt.Println("error:", err)
|
||
return
|
||
}
|
||
fmt.Printf("status=%d errorMsg=%s\n", resp.Status, resp.ErrorMsg)
|
||
// Output:
|
||
}
|
||
|
||
// ExampleClient_CreatePayment 演示创建付款单据。
|
||
func ExampleClient_CreatePayment() {
|
||
ctx := context.Background()
|
||
client := NewClient(
|
||
"https://api.example.com",
|
||
"your-tenant-id",
|
||
"dd-ai-table",
|
||
"your-client-secret",
|
||
)
|
||
|
||
req := &CreatePaymentRequest{
|
||
Code: "PAY-20240101-001",
|
||
UserID: "04221***",
|
||
Title: "供应商货款",
|
||
Amount: "11300.00",
|
||
Supplier: &Supplier{
|
||
Name: "某某供应商",
|
||
},
|
||
PaymentDetailList: []PaymentDetail{
|
||
{
|
||
Amount: "11300.00",
|
||
Tax: "1300.00",
|
||
},
|
||
},
|
||
}
|
||
|
||
resp, err := client.CreatePayment(ctx, req)
|
||
if err != nil {
|
||
fmt.Println("error:", err)
|
||
return
|
||
}
|
||
fmt.Printf("code=%s\n", resp.Code)
|
||
// Output:
|
||
}
|
||
|
||
// ExampleClient_CreateSupplier 演示创建供应商。
|
||
func ExampleClient_CreateSupplier() {
|
||
ctx := context.Background()
|
||
client := NewClient(
|
||
"https://api.example.com",
|
||
"your-tenant-id",
|
||
"dd-ai-table",
|
||
"your-client-secret",
|
||
)
|
||
|
||
bizData := []SupplierBizData{
|
||
{
|
||
CorpID: "dingXXXXXX",
|
||
Creator: "04221***",
|
||
SupplierInfo: &SupplierInfo{
|
||
Name: "某某供应商",
|
||
CorpID: "dingXXXXXX",
|
||
Creator: "04221***",
|
||
ContactName: "张三",
|
||
ContactTelephone: "18888888888",
|
||
InvoiceName: "某某供应商",
|
||
InvoiceTaxNo: "91330100XXXXXXXXXX",
|
||
},
|
||
},
|
||
}
|
||
dataBytes, _ := json.Marshal(bizData)
|
||
|
||
req := &CreateSupplierRequest{
|
||
InvokeID: "REQ-001",
|
||
UserID: "04221***",
|
||
Data: string(dataBytes),
|
||
BizType: BizTypeCreateSupplier,
|
||
}
|
||
|
||
resp, err := client.CreateSupplier(ctx, req)
|
||
if err != nil {
|
||
fmt.Println("error:", err)
|
||
return
|
||
}
|
||
fmt.Printf("bizType=%s data=%s\n", resp.BizType, resp.Data)
|
||
// Output:
|
||
}
|
||
|
||
// ExampleClient_QuerySupplierByName 演示根据名称查询供应商。
|
||
func ExampleClient_QuerySupplierByName() {
|
||
ctx := context.Background()
|
||
client := NewClient(
|
||
"https://api.example.com",
|
||
"your-tenant-id",
|
||
"dd-ai-table",
|
||
"your-client-secret",
|
||
)
|
||
|
||
queries := []QueryByName{
|
||
{CorpID: "dingXXXXXX", Name: "某某供应商"},
|
||
}
|
||
dataBytes, _ := json.Marshal(queries)
|
||
|
||
req := &QuerySupplierByNameRequest{
|
||
InvokeID: "REQ-002",
|
||
UserID: "04221***",
|
||
Data: string(dataBytes),
|
||
BizType: BizTypeQuerySupplierByName,
|
||
}
|
||
|
||
resp, err := client.QuerySupplierByName(ctx, req)
|
||
if err != nil {
|
||
fmt.Println("error:", err)
|
||
return
|
||
}
|
||
fmt.Printf("success=%v result=%s\n", resp.Success, resp.Result)
|
||
// Output:
|
||
}
|
||
|
||
// ExampleClient_UpdateSupplier 演示更新供应商。
|
||
func ExampleClient_UpdateSupplier() {
|
||
ctx := context.Background()
|
||
client := NewClient(
|
||
"https://api.example.com",
|
||
"your-tenant-id",
|
||
"dd-ai-table",
|
||
"your-client-secret",
|
||
)
|
||
|
||
bizData := UpdateSupplierBizData{
|
||
SupplierID: "SUP_XXXXX",
|
||
Name: "某某供应商",
|
||
CorpID: "dingXXXXXX",
|
||
UserID: "04221***",
|
||
ContactName: "李四",
|
||
ContactTelephone: "18888888888",
|
||
AccountType: AccountTypeCorpBankCard,
|
||
}
|
||
dataBytes, _ := json.Marshal(bizData)
|
||
|
||
req := &UpdateSupplierRequest{
|
||
InvokeID: "REQ-003",
|
||
UserID: "04221***",
|
||
Data: string(dataBytes),
|
||
BizType: BizTypeUpdateSupplier,
|
||
}
|
||
|
||
resp, err := client.UpdateSupplier(ctx, req)
|
||
if err != nil {
|
||
fmt.Println("error:", err)
|
||
return
|
||
}
|
||
fmt.Printf("bizType=%s\n", resp.BizType)
|
||
// Output:
|
||
}
|
||
|
||
// ExampleClient_QuerySupplierByUserDefineCode 演示根据用户自定义编码查询供应商。
|
||
func ExampleClient_QuerySupplierByUserDefineCode() {
|
||
ctx := context.Background()
|
||
client := NewClient(
|
||
"https://api.example.com",
|
||
"your-tenant-id",
|
||
"dd-ai-table",
|
||
"your-client-secret",
|
||
)
|
||
|
||
bizData := QueryByUserDefineCode{
|
||
CorpID: "dingXXXXXX",
|
||
UserDefineCode: "SUP-CODE-001",
|
||
}
|
||
dataBytes, _ := json.Marshal(bizData)
|
||
|
||
req := &QuerySupplierByUserDefineCodeRequest{
|
||
InvokeID: "REQ-004",
|
||
UserID: "04221***",
|
||
Data: string(dataBytes),
|
||
BizType: BizTypeQuerySupplierByUserDefine,
|
||
}
|
||
|
||
resp, err := client.QuerySupplierByUserDefineCode(ctx, req)
|
||
if err != nil {
|
||
fmt.Println("error:", err)
|
||
return
|
||
}
|
||
fmt.Printf("bizType=%s data=%s\n", resp.BizType, resp.Data)
|
||
// Output:
|
||
}
|
||
|
||
// TestVerifyNotificationSignature 演示通知验签。
|
||
func TestVerifyNotificationSignature(t *testing.T) {
|
||
verifyKey := "customer-verify-secret"
|
||
rawBody := []byte(`{"bizType":"payment","bizId":"PAY-001","data":"{}"}`)
|
||
sign := HmacSHA256Base64(verifyKey, string(rawBody))
|
||
|
||
ok, err := VerifyNotificationSignature(verifyKey, rawBody, sign)
|
||
if err != nil {
|
||
t.Fatalf("verify error: %v", err)
|
||
}
|
||
if !ok {
|
||
t.Fatal("signature mismatch")
|
||
}
|
||
}
|
||
|
||
// TestParsePaymentNotify 演示解析支付完成通知。
|
||
func TestParsePaymentNotify(t *testing.T) {
|
||
body := []byte(`{
|
||
"code":"PAY-001",
|
||
"instanceId":"inst-001",
|
||
"corpId":"dingXXXXXX",
|
||
"paymentStatus":"SUCCESS",
|
||
"paymentTime":"2024-01-01 10:00:00",
|
||
"userId":"04221***",
|
||
"amount":"11300.00"
|
||
}`)
|
||
n, err := ParsePaymentNotify(body)
|
||
if err != nil {
|
||
t.Fatalf("parse error: %v", err)
|
||
}
|
||
if n.PaymentStatus != PaymentStatusSuccess {
|
||
t.Fatalf("unexpected status: %s", n.PaymentStatus)
|
||
}
|
||
}
|
||
```
|
||
|
||
=== SDK 生成完成 === |