github.com/chanxuehong/wechat@v0.0.0-20230222024006-36f0325263cd/mch/pay/unifiedorder.go (about)

     1  package pay
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"time"
     7  
     8  	"github.com/chanxuehong/wechat/mch/core"
     9  	"github.com/chanxuehong/wechat/util"
    10  )
    11  
    12  // UnifiedOrder 统一下单.
    13  func UnifiedOrder(clt *core.Client, req map[string]string) (resp map[string]string, err error) {
    14  	return clt.PostXML(core.APIBaseURL()+"/pay/unifiedorder", req)
    15  }
    16  
    17  type UnifiedOrderRequest struct {
    18  	XMLName struct{} `xml:"xml" json:"-"`
    19  
    20  	// 必选参数
    21  	Body           string `xml:"body"`             // 商品或支付单简要描述
    22  	OutTradeNo     string `xml:"out_trade_no"`     // 商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号
    23  	TotalFee       int64  `xml:"total_fee"`        // 订单总金额,单位为分,详见支付金额
    24  	SpbillCreateIP string `xml:"spbill_create_ip"` // APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。
    25  	NotifyURL      string `xml:"notify_url"`       // 接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
    26  	TradeType      string `xml:"trade_type"`       // 取值如下:JSAPI,NATIVE,APP,详细说明见参数规定
    27  
    28  	// 可选参数
    29  	DeviceInfo string    `xml:"device_info"` // 终端设备号(门店号或收银设备ID),注意:PC网页或公众号内支付请传"WEB"
    30  	NonceStr   string    `xml:"nonce_str"`   // 随机字符串,不长于32位。NOTE: 如果为空则系统会自动生成一个随机字符串。
    31  	SignType   string    `xml:"sign_type"`   // 签名类型,默认为MD5,支持HMAC-SHA256和MD5。
    32  	Detail     string    `xml:"detail"`      // 商品名称明细列表
    33  	Attach     string    `xml:"attach"`      // 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
    34  	FeeType    string    `xml:"fee_type"`    // 符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
    35  	TimeStart  time.Time `xml:"time_start"`  // 订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
    36  	TimeExpire time.Time `xml:"time_expire"` // 订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。其他详见时间规则
    37  	GoodsTag   string    `xml:"goods_tag"`   // 商品标记,代金券或立减优惠功能的参数,说明详见代金券或立减优惠
    38  	ProductId  string    `xml:"product_id"`  // trade_type=NATIVE,此参数必传。此id为二维码中包含的商品ID,商户自行定义。
    39  	LimitPay   string    `xml:"limit_pay"`   // no_credit--指定不能使用信用卡支付
    40  	OpenId     string    `xml:"openid"`      // rade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。
    41  	SubOpenId  string    `xml:"sub_openid"`  // trade_type=JSAPI,此参数必传,用户在子商户appid下的唯一标识。openid和sub_openid可以选传其中之一,如果选择传sub_openid,则必须传sub_appid。
    42  	SceneInfo  string    `xml:"scene_info"`  // 该字段用于上报支付的场景信息,针对H5支付有以下三种场景,请根据对应场景上报,H5支付不建议在APP端使用,针对场景1,2请接入APP支付,不然可能会出现兼容性问题
    43  }
    44  
    45  type UnifiedOrderResponse struct {
    46  	XMLName struct{} `xml:"xml" json:"-"`
    47  
    48  	// 必选返回
    49  	PrepayId  string `xml:"prepay_id"`  // 微信生成的预支付回话标识,用于后续接口调用中使用,该值有效期为2小时
    50  	TradeType string `xml:"trade_type"` // 调用接口提交的交易类型,取值如下:JSAPI,NATIVE,APP,详细说明见参数规定
    51  
    52  	// 下面字段都是可选返回的(详细见微信支付文档), 为空值表示没有返回, 程序逻辑里需要判断
    53  	DeviceInfo string `xml:"device_info"` // 调用接口提交的终端设备号。
    54  	CodeURL    string `xml:"code_url"`    // trade_type 为 NATIVE 时有返回,可将该参数值生成二维码展示出来进行扫码支付
    55  	MWebURL    string `xml:"mweb_url"`    // trade_type 为 MWEB 时有返回
    56  }
    57  
    58  // UnifiedOrder2 统一下单.
    59  func UnifiedOrder2(clt *core.Client, req *UnifiedOrderRequest) (resp *UnifiedOrderResponse, err error) {
    60  	m1 := make(map[string]string, 24)
    61  	m1["body"] = req.Body
    62  	m1["out_trade_no"] = req.OutTradeNo
    63  	m1["total_fee"] = strconv.FormatInt(req.TotalFee, 10)
    64  	m1["spbill_create_ip"] = req.SpbillCreateIP
    65  	m1["notify_url"] = req.NotifyURL
    66  	m1["trade_type"] = req.TradeType
    67  	if req.DeviceInfo != "" {
    68  		m1["device_info"] = req.DeviceInfo
    69  	}
    70  	if req.NonceStr != "" {
    71  		m1["nonce_str"] = req.NonceStr
    72  	} else {
    73  		m1["nonce_str"] = util.NonceStr()
    74  	}
    75  	if req.SignType != "" {
    76  		m1["sign_type"] = req.SignType
    77  	}
    78  	if req.Detail != "" {
    79  		m1["detail"] = req.Detail
    80  	}
    81  	if req.Attach != "" {
    82  		m1["attach"] = req.Attach
    83  	}
    84  	if req.FeeType != "" {
    85  		m1["fee_type"] = req.FeeType
    86  	}
    87  	if !req.TimeStart.IsZero() {
    88  		m1["time_start"] = core.FormatTime(req.TimeStart)
    89  	}
    90  	if !req.TimeExpire.IsZero() {
    91  		m1["time_expire"] = core.FormatTime(req.TimeExpire)
    92  	}
    93  	if req.GoodsTag != "" {
    94  		m1["goods_tag"] = req.GoodsTag
    95  	}
    96  	if req.ProductId != "" {
    97  		m1["product_id"] = req.ProductId
    98  	}
    99  	if req.LimitPay != "" {
   100  		m1["limit_pay"] = req.LimitPay
   101  	}
   102  	if req.OpenId != "" {
   103  		m1["openid"] = req.OpenId
   104  	}
   105  	if req.SubOpenId != "" {
   106  		m1["sub_openid"] = req.SubOpenId
   107  	}
   108  	if req.SceneInfo != "" {
   109  		m1["scene_info"] = req.SceneInfo
   110  	}
   111  
   112  	m2, err := UnifiedOrder(clt, m1)
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  
   117  	// 校验 trade_type
   118  	respTradeType := m2["trade_type"]
   119  	if respTradeType != req.TradeType {
   120  		err = fmt.Errorf("trade_type mismatch, have: %s, want: %s", respTradeType, req.TradeType)
   121  		return nil, err
   122  	}
   123  
   124  	resp = &UnifiedOrderResponse{
   125  		PrepayId:   m2["prepay_id"],
   126  		TradeType:  respTradeType,
   127  		DeviceInfo: m2["device_info"],
   128  		CodeURL:    m2["code_url"],
   129  		MWebURL:    m2["mweb_url"],
   130  	}
   131  	return resp, nil
   132  }