package pay import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" ) const jsapiPrepayPath = "/v3/pay/transactions/jsapi" type jsapiPrepayBody struct { AppID string `json:"appid"` MchID string `json:"mchid"` Description string `json:"description"` OutTradeNo string `json:"out_trade_no"` TimeExpire interface{} `json:"time_expire,omitempty"` Attach string `json:"attach,omitempty"` NotifyURL string `json:"notify_url"` GoodsTag string `json:"goods_tag,omitempty"` SupportFapiao *bool `json:"support_fapiao,omitempty"` Amount Amount `json:"amount"` Payer Payer `json:"payer"` Detail *Detail `json:"detail,omitempty"` SceneInfo *SceneInfo `json:"scene_info,omitempty"` SettleInfo *SettleInfo `json:"settle_info,omitempty"` } func (c *Client) JsapiPrepay(ctx context.Context, req *JsapiPrepayRequest) (*JsapiPrepayResponse, error) { if req == nil { return nil, fmt.Errorf("request is required") } if req.OutTradeNo == "" || req.Description == "" || req.Payer.OpenID == "" { return nil, fmt.Errorf("out_trade_no, description and payer.openid are required") } if req.Amount.Total <= 0 { return nil, fmt.Errorf("amount.total must be greater than 0") } notifyURL := req.NotifyURL if notifyURL == "" { notifyURL = c.NotifyURL } if notifyURL == "" { return nil, fmt.Errorf("notify_url is required") } currency := req.Amount.Currency if currency == "" { currency = "CNY" } body := jsapiPrepayBody{ AppID: c.AppID, MchID: c.MchID, Description: req.Description, OutTradeNo: req.OutTradeNo, Attach: req.Attach, NotifyURL: notifyURL, GoodsTag: req.GoodsTag, SupportFapiao: req.SupportFapiao, Amount: Amount{ Total: req.Amount.Total, Currency: currency, }, Payer: req.Payer, Detail: req.Detail, SceneInfo: req.SceneInfo, SettleInfo: req.SettleInfo, } if req.TimeExpire != nil { body.TimeExpire = req.TimeExpire } reqBody, err := json.Marshal(body) if err != nil { return nil, err } httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, c.Host+jsapiPrepayPath, bytes.NewReader(reqBody)) if err != nil { return nil, err } httpReq.Header.Set("Accept", "application/json") httpReq.Header.Set("Content-Type", "application/json") if c.platformSerial != "" { httpReq.Header.Set("Wechatpay-Serial", c.platformSerial) } authorization, err := c.buildAuthorization(http.MethodPost, jsapiPrepayPath, reqBody) if err != nil { return nil, err } httpReq.Header.Set("Authorization", authorization) resp, err := c.httpClient.Do(httpReq) if err != nil { return nil, err } defer resp.Body.Close() respBody, err := io.ReadAll(resp.Body) if err != nil { return nil, err } if !isSuccessStatus(resp.StatusCode) { return nil, newAPIError(resp.StatusCode, respBody) } if err := c.validateResponse(resp.Header, respBody); err != nil { return nil, err } var result JsapiPrepayResponse if err := json.Unmarshal(respBody, &result); err != nil { return nil, err } if result.PrepayID == "" { return nil, fmt.Errorf("empty prepay_id in response") } return &result, nil } func (c *Client) MiniProgramPay(ctx context.Context, req *JsapiPrepayRequest) (*MiniProgramPayResult, error) { prepay, err := c.JsapiPrepay(ctx, req) if err != nil { return nil, err } payParams, err := c.BuildMiniProgramPayParams(prepay.PrepayID) if err != nil { return nil, err } return &MiniProgramPayResult{ PrepayID: prepay.PrepayID, Pay: payParams, }, nil }