package client import ( "bytes" "encoding/json" "errors" "fmt" "io" "net/http" "strings" "time" ) type AGPayClient struct { baseURL string httpClient *http.Client } type AGPayCreateRequest struct { TotalFeeFen int64 `json:"totalFee"` MerchantOrder string `json:"tradeNo"` NotifyURL string `json:"notifyUrl"` } type AGPayRequestRejectedError struct { StatusCode int } func (e *AGPayRequestRejectedError) Error() string { return fmt.Sprintf("AGPay rejected payment request with HTTP %d", e.StatusCode) } func NewAGPayClient(baseURL string) (*AGPayClient, error) { if baseURL == "" { return nil, errors.New("AGPay base URL is required") } return &AGPayClient{baseURL: strings.TrimRight(baseURL, "/"), httpClient: &http.Client{Timeout: 15 * time.Second}}, nil } func (c *AGPayClient) CreateWechatQRCode(req AGPayCreateRequest) ([]byte, error) { if req.TotalFeeFen <= 0 || req.MerchantOrder == "" || req.NotifyURL == "" { return nil, errors.New("invalid AGPay payment request") } body, err := json.Marshal(req) if err != nil { return nil, err } httpReq, err := http.NewRequest(http.MethodPost, c.baseURL+"/pay/wx/pay", bytes.NewReader(body)) if err != nil { return nil, err } httpReq.Header.Set("Content-Type", "application/json") resp, err := c.httpClient.Do(httpReq) if err != nil { return nil, fmt.Errorf("call AGPay: %w", err) } defer resp.Body.Close() response, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20)) if err != nil { return nil, err } if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices { return nil, &AGPayRequestRejectedError{StatusCode: resp.StatusCode} } return response, nil }