---
url: "https://pay.weixin.qq.com/doc/v3/merchant/4012533439-go.md"
title: "查询投诉单协商历史-请求示例-go"
doc_id: 4012533439
example_lang: "go"
---

## Go


需配合微信支付工具库 wxpay\_utility 使用，请参考 [Go](https://pay.weixin.qq.com/doc/v3/merchant/4015119334.md)

```
package main

import (
	"demo/wxpay_utility" // 引用微信支付工具库，参考 https://pay.weixin.qq.com/doc/v3/merchant/4015119334
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strings"
)

func main() {
	// TODO: 请准备商户开发必要参数，参考：https://pay.weixin.qq.com/doc/v3/merchant/4013070756
	config, err := wxpay_utility.CreateMchConfig(
		"19xxxxxxxx",                 // 商户号，是由微信支付系统生成并分配给每个商户的唯一标识符，商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
		"1DDE55AD98Exxxxxxxxxx",      // 商户API证书序列号，如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
		"/path/to/apiclient_key.pem", // 商户API证书私钥文件路径，本地文件路径
		"PUB_KEY_ID_xxxxxxxxxxxxx",   // 微信支付公钥ID，如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
		"/path/to/wxp_pub.pem",       // 微信支付公钥文件路径，本地文件路径
	)
	if err != nil {
		fmt.Println(err)
		return
	}

	request := &QueryNegotiationHistoryV2Request{
		ComplaintId: wxpay_utility.String("200201820200101080076610000"),
		Limit:       wxpay_utility.Int64(50),
		Offset:      wxpay_utility.Int64(10),
	}

	response, err := QueryNegotiationHistoryV2(config, request)
	if err != nil {
		fmt.Printf("请求失败: %+v\n", err)
		// TODO: 请求失败，根据状态码执行不同的处理
		return
	}

	// TODO: 请求成功，继续业务逻辑
	fmt.Printf("请求成功: %+v\n", response)
}

func QueryNegotiationHistoryV2(config *wxpay_utility.MchConfig, request *QueryNegotiationHistoryV2Request) (response *QueryNegotiationHistoryV2Response, err error) {
	const (
		host   = "https://api.mch.weixin.qq.com"
		method = "GET"
		path   = "/v3/merchant-service/complaints-v2/{complaint_id}/negotiation-historys"
	)

	reqUrl, err := url.Parse(fmt.Sprintf("%s%s", host, path))
	if err != nil {
		return nil, err
	}
	reqUrl.Path = strings.Replace(reqUrl.Path, "{complaint_id}", url.PathEscape(*request.ComplaintId), -1)
	query := reqUrl.Query()
	if request.Limit != nil {
		query.Add("limit", fmt.Sprintf("%v", *request.Limit))
	}
	if request.Offset != nil {
		query.Add("offset", fmt.Sprintf("%v", *request.Offset))
	}
	reqUrl.RawQuery = query.Encode()
	httpRequest, err := http.NewRequest(method, reqUrl.String(), nil)
	if err != nil {
		return nil, err
	}
	httpRequest.Header.Set("Accept", "application/json")
	httpRequest.Header.Set("Wechatpay-Serial", config.WechatPayPublicKeyId())
	authorization, err := wxpay_utility.BuildAuthorization(config.MchId(), config.CertificateSerialNo(), config.PrivateKey(), method, reqUrl.RequestURI(), nil)
	if err != nil {
		return nil, err
	}
	httpRequest.Header.Set("Authorization", authorization)

	client := &http.Client{}
	httpResponse, err := client.Do(httpRequest)
	if err != nil {
		return nil, err
	}
	respBody, err := wxpay_utility.ExtractResponseBody(httpResponse)
	if err != nil {
		return nil, err
	}
	if httpResponse.StatusCode >= 200 && httpResponse.StatusCode < 300 {
		// 2XX 成功，验证应答签名
		err = wxpay_utility.ValidateResponse(
			config.WechatPayPublicKeyId(),
			config.WechatPayPublicKey(),
			&httpResponse.Header,
			respBody,
		)
		if err != nil {
			return nil, err
		}
		response := &QueryNegotiationHistoryV2Response{}
		if err := json.Unmarshal(respBody, response); err != nil {
			return nil, err
		}

		return response, nil
	} else {
		return nil, wxpay_utility.NewApiException(
			httpResponse.StatusCode,
			httpResponse.Header,
			respBody,
		)
	}
}

type QueryNegotiationHistoryV2Request struct {
	ComplaintId *string `json:"complaint_id,omitempty"`
	Limit       *int64  `json:"limit,omitempty"`
	Offset      *int64  `json:"offset,omitempty"`
}

func (o *QueryNegotiationHistoryV2Request) MarshalJSON() ([]byte, error) {
	type Alias QueryNegotiationHistoryV2Request
	a := &struct {
		ComplaintId *string `json:"complaint_id,omitempty"`
		Limit       *int64  `json:"limit,omitempty"`
		Offset      *int64  `json:"offset,omitempty"`
		*Alias
	}{
		// 序列化时移除非 Body 字段
		ComplaintId: nil,
		Limit:       nil,
		Offset:      nil,
		Alias:       (*Alias)(o),
	}
	return json.Marshal(a)
}

type QueryNegotiationHistoryV2Response struct {
	Data       []ComplaintNegotiationHistoryWithLogId `json:"data,omitempty"`
	Limit      *int64                                 `json:"limit,omitempty"`
	Offset     *int64                                 `json:"offset,omitempty"`
	TotalCount *int64                                 `json:"total_count,omitempty"`
}

type ComplaintNegotiationHistoryWithLogId struct {
	LogId                                    *string                          `json:"log_id,omitempty"`
	Operator                                 *string                          `json:"operator,omitempty"`
	OperateTime                              *string                          `json:"operate_time,omitempty"`
	OperateType                              *ComplaintNegotiationOperateType `json:"operate_type,omitempty"`
	OperateDetails                           *string                          `json:"operate_details,omitempty"`
	ImageList                                []string                         `json:"image_list,omitempty"`
	ComplaintMediaList                       *ComplaintMedia                  `json:"complaint_media_list,omitempty"`
	UserAppyPlatformServiceReason            *string                          `json:"user_appy_platform_service_reason,omitempty"`
	UserAppyPlatformServiceReasonDescription *string                          `json:"user_appy_platform_service_reason_description,omitempty"`
	NormalMessage                            *NormalMessage                   `json:"normal_message,omitempty"`
	ClickMessage                             *ClickMessage                    `json:"click_message,omitempty"`
}

type ComplaintNegotiationOperateType string

func (e ComplaintNegotiationOperateType) Ptr() *ComplaintNegotiationOperateType {
	return &e
}

const (
	COMPLAINTNEGOTIATIONOPERATETYPE_USER_CREATE_COMPLAINT                     ComplaintNegotiationOperateType = "USER_CREATE_COMPLAINT"
	COMPLAINTNEGOTIATIONOPERATETYPE_USER_CONTINUE_COMPLAINT                   ComplaintNegotiationOperateType = "USER_CONTINUE_COMPLAINT"
	COMPLAINTNEGOTIATIONOPERATETYPE_USER_RESPONSE                             ComplaintNegotiationOperateType = "USER_RESPONSE"
	COMPLAINTNEGOTIATIONOPERATETYPE_PLATFORM_RESPONSE                         ComplaintNegotiationOperateType = "PLATFORM_RESPONSE"
	COMPLAINTNEGOTIATIONOPERATETYPE_MERCHANT_RESPONSE                         ComplaintNegotiationOperateType = "MERCHANT_RESPONSE"
	COMPLAINTNEGOTIATIONOPERATETYPE_MERCHANT_CONFIRM_COMPLETE                 ComplaintNegotiationOperateType = "MERCHANT_CONFIRM_COMPLETE"
	COMPLAINTNEGOTIATIONOPERATETYPE_USER_CREATE_COMPLAINT_SYSTEM_MESSAGE      ComplaintNegotiationOperateType = "USER_CREATE_COMPLAINT_SYSTEM_MESSAGE"
	COMPLAINTNEGOTIATIONOPERATETYPE_COMPLAINT_FULL_REFUNDED_SYSTEM_MESSAGE    ComplaintNegotiationOperateType = "COMPLAINT_FULL_REFUNDED_SYSTEM_MESSAGE"
	COMPLAINTNEGOTIATIONOPERATETYPE_USER_CONTINUE_COMPLAINT_SYSTEM_MESSAGE    ComplaintNegotiationOperateType = "USER_CONTINUE_COMPLAINT_SYSTEM_MESSAGE"
	COMPLAINTNEGOTIATIONOPERATETYPE_USER_REVOKE_COMPLAINT                     ComplaintNegotiationOperateType = "USER_REVOKE_COMPLAINT"
	COMPLAINTNEGOTIATIONOPERATETYPE_USER_COMFIRM_COMPLAINT                    ComplaintNegotiationOperateType = "USER_COMFIRM_COMPLAINT"
	COMPLAINTNEGOTIATIONOPERATETYPE_PLATFORM_HELP_APPLICATION                 ComplaintNegotiationOperateType = "PLATFORM_HELP_APPLICATION"
	COMPLAINTNEGOTIATIONOPERATETYPE_USER_APPLY_PLATFORM_HELP                  ComplaintNegotiationOperateType = "USER_APPLY_PLATFORM_HELP"
	COMPLAINTNEGOTIATIONOPERATETYPE_MERCHANT_APPROVE_REFUND                   ComplaintNegotiationOperateType = "MERCHANT_APPROVE_REFUND"
	COMPLAINTNEGOTIATIONOPERATETYPE_MERCHANT_REFUSE_RERUND                    ComplaintNegotiationOperateType = "MERCHANT_REFUSE_RERUND"
	COMPLAINTNEGOTIATIONOPERATETYPE_USER_SUBMIT_SATISFACTION                  ComplaintNegotiationOperateType = "USER_SUBMIT_SATISFACTION"
	COMPLAINTNEGOTIATIONOPERATETYPE_SERVICE_ORDER_CANCEL                      ComplaintNegotiationOperateType = "SERVICE_ORDER_CANCEL"
	COMPLAINTNEGOTIATIONOPERATETYPE_SERVICE_ORDER_COMPLETE                    ComplaintNegotiationOperateType = "SERVICE_ORDER_COMPLETE"
	COMPLAINTNEGOTIATIONOPERATETYPE_COMPLAINT_PARTIAL_REFUNDED_SYSTEM_MESSAGE ComplaintNegotiationOperateType = "COMPLAINT_PARTIAL_REFUNDED_SYSTEM_MESSAGE"
	COMPLAINTNEGOTIATIONOPERATETYPE_COMPLAINT_REFUND_RECEIVED_SYSTEM_MESSAGE  ComplaintNegotiationOperateType = "COMPLAINT_REFUND_RECEIVED_SYSTEM_MESSAGE"
	COMPLAINTNEGOTIATIONOPERATETYPE_COMPLAINT_ENTRUSTED_REFUND_SYSTEM_MESSAGE ComplaintNegotiationOperateType = "COMPLAINT_ENTRUSTED_REFUND_SYSTEM_MESSAGE"
	COMPLAINTNEGOTIATIONOPERATETYPE_USER_APPLY_PLATFORM_SERVICE               ComplaintNegotiationOperateType = "USER_APPLY_PLATFORM_SERVICE"
	COMPLAINTNEGOTIATIONOPERATETYPE_USER_CANCEL_PLATFORM_SERVICE              ComplaintNegotiationOperateType = "USER_CANCEL_PLATFORM_SERVICE"
	COMPLAINTNEGOTIATIONOPERATETYPE_PLATFORM_SERVICE_FINISHED                 ComplaintNegotiationOperateType = "PLATFORM_SERVICE_FINISHED"
	COMPLAINTNEGOTIATIONOPERATETYPE_USER_CLICK_RESPONSE                       ComplaintNegotiationOperateType = "USER_CLICK_RESPONSE"
)

type ComplaintMedia struct {
	MediaType *ComplaintMediaType `json:"media_type,omitempty"`
	MediaUrl  []string            `json:"media_url,omitempty"`
}

type NormalMessage struct {
	Blocks         []Block         `json:"blocks,omitempty"`
	SenderIdentity *SenderIdentity `json:"sender_identity,omitempty"`
	CustomData     *string         `json:"custom_data,omitempty"`
}

type ClickMessage struct {
	MessageContent *string `json:"message_content,omitempty"`
	ActionId       *string `json:"action_id,omitempty"`
	ClickedLogId   *string `json:"clicked_log_id,omitempty"`
}

type ComplaintMediaType string

func (e ComplaintMediaType) Ptr() *ComplaintMediaType {
	return &e
}

const (
	COMPLAINTMEDIATYPE_USER_COMPLAINT_IMAGE ComplaintMediaType = "USER_COMPLAINT_IMAGE"
	COMPLAINTMEDIATYPE_OPERATION_IMAGE      ComplaintMediaType = "OPERATION_IMAGE"
)

type Block struct {
	Type        *BlockType   `json:"type,omitempty"`
	Text        *Text        `json:"text,omitempty"`
	Image       *Image       `json:"image,omitempty"`
	Link        *Link        `json:"link,omitempty"`
	FaqList     *FaqList     `json:"faq_list,omitempty"`
	Button      *Button      `json:"button,omitempty"`
	ButtonGroup *ButtonGroup `json:"button_group,omitempty"`
}

type SenderIdentity string

func (e SenderIdentity) Ptr() *SenderIdentity {
	return &e
}

const (
	SENDERIDENTITY_UNKNOWN SenderIdentity = "UNKNOWN"
	SENDERIDENTITY_MANUAL  SenderIdentity = "MANUAL"
	SENDERIDENTITY_MACHINE SenderIdentity = "MACHINE"
)

type BlockType string

func (e BlockType) Ptr() *BlockType {
	return &e
}

const (
	BLOCKTYPE_TEXT         BlockType = "TEXT"
	BLOCKTYPE_IMAGE        BlockType = "IMAGE"
	BLOCKTYPE_LINK         BlockType = "LINK"
	BLOCKTYPE_FAQ_LIST     BlockType = "FAQ_LIST"
	BLOCKTYPE_BUTTON       BlockType = "BUTTON"
	BLOCKTYPE_BUTTON_GROUP BlockType = "BUTTON_GROUP"
)

type Text struct {
	Text   *string    `json:"text,omitempty"`
	Color  *TextColor `json:"color,omitempty"`
	IsBold *bool      `json:"is_bold,omitempty"`
}

type Image struct {
	MediaId        *string         `json:"media_id,omitempty"`
	ImageStyleType *ImageStyleType `json:"image_style_type,omitempty"`
}

type Link struct {
	Text        *string      `json:"text,omitempty"`
	Action      *ClickAction `json:"action,omitempty"`
	InvalidInfo *InvalidInfo `json:"invalid_info,omitempty"`
}

type FaqList struct {
	Faqs []FaqListItem `json:"faqs,omitempty"`
}

type Button struct {
	Text        *string      `json:"text,omitempty"`
	Action      *ClickAction `json:"action,omitempty"`
	InvalidInfo *InvalidInfo `json:"invalid_info,omitempty"`
}

type ButtonGroup struct {
	Buttons      []InnerButton `json:"buttons,omitempty"`
	ButtonLayout *ButtonLayout `json:"button_layout,omitempty"`
	InvalidInfo  *InvalidInfo  `json:"invalid_info,omitempty"`
}

type TextColor string

func (e TextColor) Ptr() *TextColor {
	return &e
}

const (
	TEXTCOLOR_DEFAULT   TextColor = "DEFAULT"
	TEXTCOLOR_SECONDARY TextColor = "SECONDARY"
)

type ImageStyleType string

func (e ImageStyleType) Ptr() *ImageStyleType {
	return &e
}

const (
	IMAGESTYLETYPE_IMAGE_STYLE_TYPE_NARROW ImageStyleType = "IMAGE_STYLE_TYPE_NARROW"
	IMAGESTYLETYPE_IMAGE_STYLE_TYPE_WIDE   ImageStyleType = "IMAGE_STYLE_TYPE_WIDE"
)

type ClickAction struct {
	ActionType          *ActionType      `json:"action_type,omitempty"`
	JumpUrl             *string          `json:"jump_url,omitempty"`
	MiniProgramJumpInfo *MiniProgramJump `json:"mini_program_jump_info,omitempty"`
	MessageInfo         *MessageInfo     `json:"message_info,omitempty"`
	ActionId            *string          `json:"action_id,omitempty"`
}

type InvalidInfo struct {
	ExpiredTime    *string `json:"expired_time,omitempty"`
	MultiClickable *bool   `json:"multi_clickable,omitempty"`
}

type FaqListItem struct {
	FaqId    *string      `json:"faq_id,omitempty"`
	FaqTitle *string      `json:"faq_title,omitempty"`
	Action   *ClickAction `json:"action,omitempty"`
}

type InnerButton struct {
	Text   *string      `json:"text,omitempty"`
	Action *ClickAction `json:"action,omitempty"`
}

type ButtonLayout string

func (e ButtonLayout) Ptr() *ButtonLayout {
	return &e
}

const (
	BUTTONLAYOUT_LAYOUT_UNKNOWN    ButtonLayout = "LAYOUT_UNKNOWN"
	BUTTONLAYOUT_LAYOUT_HORIZONTAL ButtonLayout = "LAYOUT_HORIZONTAL"
	BUTTONLAYOUT_LAYOUT_VERTICAL   ButtonLayout = "LAYOUT_VERTICAL"
)

type ActionType string

func (e ActionType) Ptr() *ActionType {
	return &e
}

const (
	ACTIONTYPE_ACTION_TYPE_SEND_MESSAGE      ActionType = "ACTION_TYPE_SEND_MESSAGE"
	ACTIONTYPE_ACTION_TYPE_JUMP_URL          ActionType = "ACTION_TYPE_JUMP_URL"
	ACTIONTYPE_ACTION_TYPE_JUMP_MINI_PROGRAM ActionType = "ACTION_TYPE_JUMP_MINI_PROGRAM"
)

type MiniProgramJump struct {
	Appid *string `json:"appid,omitempty"`
	Path  *string `json:"path,omitempty"`
}

type MessageInfo struct {
	Content    *string `json:"content,omitempty"`
	CustomData *string `json:"custom_data,omitempty"`
}

```
