145 lines
3.6 KiB
Go
145 lines
3.6 KiB
Go
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
|
|
}
|