diff --git a/wechat/v3/constant.go b/wechat/v3/constant.go index 9e58835a..6123b4a5 100644 --- a/wechat/v3/constant.go +++ b/wechat/v3/constant.go @@ -83,13 +83,17 @@ const ( v3ScorePermissionOpenidTerminate = "/v3/payscore/permissions/openid/%s/terminate" // openid 解除用户授权记录(openid) POST // 微信支付分(公共API) - v3ScoreOrderCreate = "/v3/payscore/serviceorder" // 创建支付分订单 POST - v3ScoreOrderQuery = "/v3/payscore/serviceorder" // 查询支付分订单 GET - v3ScoreOrderCancel = "/v3/payscore/serviceorder/%s/cancel" // out_trade_no 取消支付分订单 POST - v3ScoreOrderModify = "/v3/payscore/serviceorder/%s/modify" // out_trade_no 修改订单金额 POST - v3ScoreOrderComplete = "/v3/payscore/serviceorder/%s/complete" // out_trade_no 完结支付分订单 POST - v3ScoreOrderPay = "/v3/payscore/serviceorder/%s/pay" // out_trade_no 商户发起催收扣款 POST - v3ScoreOrderSync = "/v3/payscore/serviceorder/%s/sync" // out_trade_no 同步服务订单信息 POST + v3ScoreOrderCreate = "/v3/payscore/serviceorder" // 创建支付分订单 POST + v3ScoreOrderQuery = "/v3/payscore/serviceorder" // 查询支付分订单 GET + v3ScoreOrderCancel = "/v3/payscore/serviceorder/%s/cancel" // out_trade_no 取消支付分订单 POST + v3ScoreOrderModify = "/v3/payscore/serviceorder/%s/modify" // out_trade_no 修改订单金额 POST + v3ScoreOrderComplete = "/v3/payscore/serviceorder/%s/complete" // out_trade_no 完结支付分订单 POST + v3ScoreOrderPay = "/v3/payscore/serviceorder/%s/pay" // out_trade_no 商户发起催收扣款 POST + v3ScoreOrderSync = "/v3/payscore/serviceorder/%s/sync" // out_trade_no 同步服务订单信息 POST + v3ScoreOrderPartnerCreate = "/v3/payscore/partner/serviceorder" // 服务商模式创建支付分订单 + v3ScoreOrderPartnerQuery = "/v3/payscore/partner/serviceorder" // 服务商模式查询支付分订单 + v3ScoreOrderPartnerCancel = "/v3/payscore/partner/serviceorder/%s/cancel" // 服务商模式取消支付分订单 + v3ScoreOrderPartnerComplete = "/v3/payscore/partner/serviceorder/%s/complete" // 服务商模式完结支付分订单 // 微信先享卡 v3CardPre = "/v3/discount-card/cards" // 预受理领卡请求 POST diff --git a/wechat/v3/model.go b/wechat/v3/model.go index 39445dfd..213865b4 100644 --- a/wechat/v3/model.go +++ b/wechat/v3/model.go @@ -122,6 +122,10 @@ type APPScoreQuery struct { Sign string `json:"sign"` } +type AppletScorePartnerExtraData struct { + Package string `json:"package"` +} + // ==================================分割================================== type SignInfo struct { diff --git a/wechat/v3/model_score.go b/wechat/v3/model_score.go index 820d1bf9..37f86ad1 100644 --- a/wechat/v3/model_score.go +++ b/wechat/v3/model_score.go @@ -8,6 +8,14 @@ type ScoreOrderCreateRsp struct { Error string `json:"-"` } +// ScoreOrderPartnerCreateRsp 服务商模式创建支付分订单 Rsp +type ScoreOrderPartnerCreateRsp struct { + Code int `json:"code"` + SignInfo *SignInfo `json:"-"` + Response *ScoreOrderPartnerCreate `json:"response,omitempty"` + Error string `json:"error"` +} + // 查询支付分订单 Rsp type ScoreOrderQueryRsp struct { Code int `json:"-"` @@ -16,6 +24,14 @@ type ScoreOrderQueryRsp struct { Error string `json:"-"` } +// ScoreOrderPartnerQueryRsp 服务商模式查询支付分订单 Rsp +type ScoreOrderPartnerQueryRsp struct { + Code int `json:"code"` + SignInfo *SignInfo `json:"-"` + Response *ScoreOrderPartnerQuery `json:"response,omitempty"` + Error string `json:"error"` +} + // 取消支付分订单 Rsp type ScoreOrderCancelRsp struct { Code int `json:"-"` @@ -24,6 +40,13 @@ type ScoreOrderCancelRsp struct { Error string `json:"-"` } +// ScoreOrderPartnerCancelRsp 服务商模式取消支付分订单 Rsp +type ScoreOrderPartnerCancelRsp struct { + Code int `json:"code"` + SignInfo *SignInfo `json:"-"` + Error string `json:"error"` +} + // 修改订单金额 Rsp type ScoreOrderModifyRsp struct { Code int `json:"-"` @@ -40,6 +63,13 @@ type ScoreOrderCompleteRsp struct { Error string `json:"-"` } +// ScoreOrderPartnerCompleteRsp 服务商模式完结支付分订单 Rsp +type ScoreOrderPartnerCompleteRsp struct { + Code int `json:"code"` + SignInfo *SignInfo `json:"-"` + Error string `json:"error"` +} + // 商户发起催收扣款 Rsp type ScoreOrderPayRsp struct { Code int `json:"-"` @@ -109,6 +139,30 @@ type ScoreOrderCreate struct { Package string `json:"package"` // 用户跳转到微信侧小程序订单数据,需确认模式特有API中调起支付分-确认订单传入。该数据一小时内有效。 } +type ScoreOrderPartnerCreate struct { + Appid string `json:"appid"` // 调用接口提交的公众账号Id。 + Mchid string `json:"mchid"` // 调用接口提交的商户号。 + OutOrderNo string `json:"out_order_no"` // 调用接口提交的商户服务订单号。 + ServiceId string `json:"service_id"` // 调用该接口提交的服务Id。 + ServiceIntroduction string `json:"service_introduction"` // 服务信息,用于介绍本订单所提供的服务。 + State string `json:"state"` // 表示当前单据状态。枚举值:CREATED:商户已创建服务订单,DOING:服务订单进行中,DONE:服务订单完成,REVOKED:商户取消服务订单,EXPIRED:服务订单已失效 + StateDescription string `json:"state_description,omitempty"` // 对服务订单"进行中"状态的附加说明。USER_CONFIRM:用户确认,MCH_COMPLETE:商户完结 + PostPayments []*PostPayments `json:"post_payments,omitempty"` // 后付费项目列表,最多包含100条付费项目。 如果传入,用户侧则显示此参数。 + PostDiscounts []*PostDiscounts `json:"post_discounts,omitempty"` // 后付费商户优惠,最多包含30条付费项目。 如果传入,用户侧则显示此参数。 + RiskFund *RiskFund `json:"risk_fund"` // 订单风险金信息 + TimeRange *TimeRange `json:"time_range"` // 服务时间范围 + Location *Location `json:"location,omitempty"` // 服务位置信息 + Attach string `json:"attach,omitempty"` // 商户数据包,可存放本订单所需信息,需要先urlencode后传入,总长度不大于256字符,超出报错处理。 + NotifyUrl string `json:"notify_url,omitempty"` // 商户接收用户确认订单或扣款成功回调通知的地址。 + OrderId string `json:"order_id"` // 微信支付服务订单号,每个微信支付服务订单号与商户号下对应的商户服务订单号一一对应。 + Package string `json:"package"` // 用户跳转到微信侧小程序订单数据,需确认模式特有API中调起支付分-确认订单传入。该数据一小时内有效。 + SubAppid string `json:"sub_appid"` // 调用接口传入的子商户应用ID + SubMchid string `json:"sub_mchid"` // 调用接口传入的子商户商户号 + Openid string `json:"openid"` // 用户在子商户appid下的唯一标识 + SubOpenid string `json:"sub_openid"` // 用户在子商户appid下的唯一标识 + NeedUserConfirm bool `json:"need_user_confirm"` // 是否需要用户确认,商户可通过该字段设置是否在商户侧完成服务订单的确认,枚举值:true:是,false:否。默认为false。当商户选择了服务订单需要用户确认时,用户确认后才会发起扣款。 +} + type PostPayments struct { Name string `json:"name"` // 付费项目名称 Amount int `json:"amount"` // 此付费项目总金额,大于等于0,单位为分,等于0时代表不需要扣费,只能为整数 @@ -163,6 +217,31 @@ type ScoreOrderQuery struct { Openid string `json:"openid,omitempty"` // 微信用户在商户对应appid下的唯一标识 } +type ScoreOrderPartnerQuery struct { + Appid string `json:"appid"` // 调用接口提交的公众账号Id。 + Mchid string `json:"mchid"` // 调用接口提交的商户号。 + ServiceId string `json:"service_id"` // 调用该接口提交的服务Id。 + OutOrderNo string `json:"out_order_no"` // 调用接口提交的商户服务订单号。 + ServiceIntroduction string `json:"service_introduction"` // 服务信息,用于介绍本订单所提供的服务。 + State string `json:"state"` // 表示当前单据状态。枚举值:CREATED:商户已创建服务订单,DOING:服务订单进行中,DONE:服务订单完成,REVOKED:商户取消服务订单,EXPIRED:服务订单已失效 + StateDescription string `json:"state_description,omitempty"` // 对服务订单"进行中"状态的附加说明。USER_CONFIRM:用户确认,MCH_COMPLETE:商户完结 + TotalAmount int `json:"total_amount,omitempty"` // 总金额,大于等于0的数字,单位为分,只能为整数 + PostPayments []*PostPayments `json:"post_payments,omitempty"` // 后付费项目列表,最多包含100条付费项目。 如果传入,用户侧则显示此参数。 + PostDiscounts []*PostDiscounts `json:"post_discounts,omitempty"` // 后付费商户优惠,最多包含30条付费项目。 如果传入,用户侧则显示此参数。 + RiskFund *RiskFund `json:"risk_fund"` // 订单风险金信息 + TimeRange *TimeRange `json:"time_range"` // 服务时间范围 + Location *Location `json:"location,omitempty"` // 服务位置信息 + Attach string `json:"attach,omitempty"` // 商户数据包,可存放本订单所需信息,需要先urlencode后传入,总长度不大于256字符,超出报错处理。 + NotifyUrl string `json:"notify_url"` // 商户接收用户确认订单或扣款成功回调通知的地址。 + OrderId string `json:"order_id"` // 微信支付服务订单号,每个微信支付服务订单号与商户号下对应的商户服务订单号一一对应。 + NeedCollection bool `json:"need_collection,omitempty"` // 是否需要收款 + Collection *Collection `json:"collection,omitempty"` // 收款信息 + Openid string `json:"openid,omitempty"` // 微信用户在商户对应appid下的唯一标识 + SubAppid string `json:"sub_appid"` // 调用接口传入的子商户应用ID + SubMchid string `json:"sub_mchid"` // 调用接口传入的子商户商户号 + SubOpenid string `json:"sub_openid"` // 子商户公众号下的用户标识 +} + type Collection struct { State string `json:"state"` // 收款状态,USER_PAYING:待支付,USER_PAID:已支付 TotalAmount int `json:"total_amount,omitempty"` // 总金额,大于等于0的数字,单位为分,只能为整数 diff --git a/wechat/v3/score.go b/wechat/v3/score.go index b7b776c8..93ae6188 100644 --- a/wechat/v3/score.go +++ b/wechat/v3/score.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "net/http" + "net/url" "github.com/go-pay/gopay" ) @@ -182,6 +183,33 @@ func (c *ClientV3) V3ScoreOrderCreate(ctx context.Context, bm gopay.BodyMap) (wx return wxRsp, c.verifySyncSign(si) } +// V3ScoreOrderPartnerCreate 服务行模式创建支付分订单 +// Code = 0 is success +// 微信文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter6_2_1.shtml +func (c *ClientV3) V3ScoreOrderPartnerCreate(ctx context.Context, bm gopay.BodyMap) (*ScoreOrderPartnerCreateRsp, error) { + authorization, err := c.authorization(MethodPost, v3ScoreOrderPartnerCreate, bm) + if err != nil { + return nil, err + } + res, si, bs, err := c.doProdPost(ctx, bm, v3ScoreOrderPartnerCreate, authorization) + if err != nil { + return nil, err + } + + wxRsp := &ScoreOrderPartnerCreateRsp{Code: Success, SignInfo: si} + wxRsp.Response = new(ScoreOrderPartnerCreate) + if err = json.Unmarshal(bs, wxRsp.Response); err != nil { + return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs)) + } + if res.StatusCode != http.StatusOK { + wxRsp.Code = res.StatusCode + wxRsp.Error = string(bs) + return wxRsp, nil + } + + return wxRsp, c.verifySyncSign(si) +} + // 查询支付分订单API // Code = 0 is success func (c *ClientV3) V3ScoreOrderQuery(ctx context.Context, orderNoType OrderNoType, appid, orderNo, serviceid string) (wxRsp *ScoreOrderQueryRsp, err error) { @@ -215,6 +243,44 @@ func (c *ClientV3) V3ScoreOrderQuery(ctx context.Context, orderNoType OrderNoTyp return wxRsp, c.verifySyncSign(si) } +// V3ScoreOrderPartnerQuery 服务商模式查询支付分订单 +// Code = 0 is success +// 微信文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter6_2_2.shtml +func (c *ClientV3) V3ScoreOrderPartnerQuery(ctx context.Context, orderNoType OrderNoType, orderNo, serviceid, subMchid string) (*ScoreOrderPartnerQueryRsp, error) { + query := url.Values{} + query.Set("service_id", serviceid) + query.Set("sub_mchid", subMchid) + switch orderNoType { + case OutTradeNo: + query.Set("out_order_no", orderNo) + case QueryId: + query.Set("query_id", orderNo) + default: + return nil, errors.New("unsupported order number type") + } + uri := fmt.Sprintf("%s?%s", v3ScoreOrderPartnerQuery, query.Encode()) + authorization, err := c.authorization(MethodGet, uri, nil) + if err != nil { + return nil, err + } + res, si, bs, err := c.doProdGet(ctx, uri, authorization) + if err != nil { + return nil, err + } + wxRsp := &ScoreOrderPartnerQueryRsp{Code: Success, SignInfo: si} + wxRsp.Response = new(ScoreOrderPartnerQuery) + if err = json.Unmarshal(bs, wxRsp.Response); err != nil { + return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs)) + } + if res.StatusCode != http.StatusOK { + wxRsp.Code = res.StatusCode + wxRsp.Error = string(bs) + return wxRsp, nil + } + + return wxRsp, c.verifySyncSign(si) +} + // 取消支付分订单API // Code = 0 is success func (c *ClientV3) V3ScoreOrderCancel(ctx context.Context, appid, tradeNo, serviceid, reason string) (wxRsp *ScoreOrderCancelRsp, err error) { @@ -244,6 +310,33 @@ func (c *ClientV3) V3ScoreOrderCancel(ctx context.Context, appid, tradeNo, servi return wxRsp, c.verifySyncSign(si) } +// V3ScoreOrderPartnerCancel 服务商模式取消支付分订单 +// Code = 0 is success +// 微信文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter6_2_3.shtml +func (c *ClientV3) V3ScoreOrderPartnerCancel(ctx context.Context, subMchid, tradeNo, serviceid, reason string) (*ScoreOrderPartnerCancelRsp, error) { + path := fmt.Sprintf(v3ScoreOrderPartnerCancel, tradeNo) + bm := make(gopay.BodyMap) + bm.Set("sub_mchid", subMchid). + Set("service_id", serviceid). + Set("reason", reason) + authorization, err := c.authorization(MethodPost, path, bm) + if err != nil { + return nil, err + } + res, si, bs, err := c.doProdPost(ctx, bm, path, authorization) + if err != nil { + return nil, err + } + wxRsp := &ScoreOrderPartnerCancelRsp{Code: Success, SignInfo: si} + if res.StatusCode != http.StatusOK { + wxRsp.Code = res.StatusCode + wxRsp.Error = string(bs) + return wxRsp, nil + } + + return wxRsp, c.verifySyncSign(si) +} + // 修改订单金额API // Code = 0 is success func (c *ClientV3) V3ScoreOrderModify(ctx context.Context, tradeNo string, bm gopay.BodyMap) (wxRsp *ScoreOrderModifyRsp, err error) { @@ -261,7 +354,7 @@ func (c *ClientV3) V3ScoreOrderModify(ctx context.Context, tradeNo string, bm go if err = json.Unmarshal(bs, wxRsp.Response); err != nil { return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs)) } - if res.StatusCode != http.StatusOK { + if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusNoContent { wxRsp.Code = res.StatusCode wxRsp.Error = string(bs) return wxRsp, nil @@ -294,6 +387,29 @@ func (c *ClientV3) V3ScoreOrderComplete(ctx context.Context, tradeNo string, bm return wxRsp, c.verifySyncSign(si) } +// V3ScoreOrderPartnerComplete 服务商模式完结支付分订单A +// Code = 0 is success +// 微信文档: https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter6_2_5.shtml +func (c *ClientV3) V3ScoreOrderPartnerComplete(ctx context.Context, tradeNo string, bm gopay.BodyMap) (*ScoreOrderPartnerCompleteRsp, error) { + path := fmt.Sprintf(v3ScoreOrderPartnerComplete, tradeNo) + authorization, err := c.authorization(MethodPost, path, bm) + if err != nil { + return nil, err + } + res, si, bs, err := c.doProdPost(ctx, bm, path, authorization) + if err != nil { + return nil, err + } + wxRsp := &ScoreOrderPartnerCompleteRsp{Code: Success, SignInfo: si} + if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusNoContent { + wxRsp.Code = res.StatusCode + wxRsp.Error = string(bs) + return wxRsp, nil + } + + return wxRsp, c.verifySyncSign(si) +} + // 商户发起催收扣款API // Code = 0 is success func (c *ClientV3) V3ScoreOrderPay(ctx context.Context, appid, tradeNo, serviceid string) (wxRsp *ScoreOrderPayRsp, err error) {