Skip to content

Commit 997bd64

Browse files
authored
Merge pull request #613 from ivothgle/mini-program-b2b-payment
add mini program b2b pay
2 parents 1bd6680 + f776df3 commit 997bd64

File tree

6 files changed

+357
-6
lines changed

6 files changed

+357
-6
lines changed

src/kernel/Encryptor.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ import (
1212
"encoding/json"
1313
"encoding/xml"
1414
"fmt"
15-
"github.com/ArtisanCloud/PowerLibs/v3/object"
16-
"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/support"
1715
"math/rand"
1816
"sort"
1917
"strings"
2018
"time"
19+
20+
"github.com/ArtisanCloud/PowerLibs/v3/object"
21+
"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/support"
2122
)
2223

2324
// Wechat Docs: https://open.work.weixin.qq.com/api/doc/90000/90138/90307
@@ -249,7 +250,6 @@ func (encryptor *Encryptor) Signature(token, timestamp, nonce, data string) stri
249250

250251
func CalcPaySig(uri, postBody, appkey string) string {
251252
needSignMsg := uri + "&" + postBody
252-
fmt.Println("need sign str => ", needSignMsg)
253253
h := hmac.New(sha256.New, []byte(appkey))
254254
h.Write([]byte(needSignMsg))
255255
paySig := hex.EncodeToString(h.Sum(nil))

src/miniProgram/application.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel"
99
"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/providers"
1010
"github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram/auth"
11+
"github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram/b2b"
1112
"github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram/base"
1213
"github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram/customerServiceMessage"
1314
"github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram/dataCube"
@@ -42,6 +43,7 @@ type MiniProgram struct {
4243
*kernel.ServiceContainer
4344

4445
Base *base.Client
46+
B2B *b2b.Client
4547
VirtualPayment *virtualPayment.Client
4648
AccessToken *auth.AccessToken
4749
Auth *auth.Client
@@ -109,9 +111,10 @@ type UserConfig struct {
109111
Token string
110112
AESKey string
111113

112-
// 小程序虚拟支付新增配置
113-
AppKey string // 现网AppKey
114-
OfferID string // OfferID(支付应用ID) 等同于商户号
114+
// 小程序虚拟支付 或 b2b 支付
115+
AppKey string // 现网AppKey
116+
SandBoxKey string // 沙盒AppKey
117+
OfferID string // OfferID(支付应用ID) 等同于商户号
115118

116119
ResponseType string
117120
Log Log
@@ -378,6 +381,12 @@ func NewMiniProgram(config *UserConfig, extraInfos ...*kernel.ExtraInfo) (*MiniP
378381
return nil, err
379382
}
380383

384+
//-------------- B2B Pay --------------
385+
app.B2B, err = b2b.RegisterProvider(app)
386+
if err != nil {
387+
return nil, err
388+
}
389+
381390
return app, err
382391
}
383392

src/miniProgram/b2b/client.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package b2b
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
7+
"github.com/ArtisanCloud/PowerLibs/v3/object"
8+
"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel"
9+
"github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram/b2b/request"
10+
"github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram/b2b/response"
11+
)
12+
13+
type Client struct {
14+
BaseClient *kernel.BaseClient
15+
16+
appKey string
17+
sandboxAppKey string
18+
}
19+
20+
// CreatePayment 创建 B2b 支付 https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/B2b_store_assistant.html#_3-3-%E6%94%AF%E4%BB%98%E4%B8%8E%E9%80%80%E6%AC%BE
21+
func (c *Client) CreatePayment(sessionKey string, data any, overAppKey ...string) (*object.StringMap, error) {
22+
appKey := c.appKey
23+
if len(overAppKey) > 0 && overAppKey[0] != "" {
24+
appKey = overAppKey[0]
25+
}
26+
jsonData, err := json.Marshal(data)
27+
if err != nil {
28+
return nil, err
29+
}
30+
strData := string(jsonData)
31+
return &object.StringMap{
32+
"sign_data": strData,
33+
"mode": "retail_pay_goods",
34+
"signature": kernel.CalcSignature(strData, sessionKey),
35+
"pay_sig": kernel.CalcSignature("requestCommonPayment"+"&"+strData, appKey),
36+
}, nil
37+
}
38+
39+
func postJson[I, O any](ctx context.Context, c *Client, path string, in *I) (out *O, err error) {
40+
postBody, err := json.Marshal(in)
41+
if err != nil {
42+
return nil, err
43+
}
44+
signPost := string(postBody)
45+
46+
paySign := kernel.CalcSignature(path+"&"+signPost, c.appKey)
47+
query := &object.StringMap{
48+
"pay_sig": paySign,
49+
}
50+
out = new(O)
51+
_, err = c.BaseClient.HttpPostJson(ctx, path, signPost, query, nil, out)
52+
return out, err
53+
}
54+
55+
// GetOrder 查询订单 https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/B2b_store_assistant.html#_3-3-%E6%94%AF%E4%BB%98%E4%B8%8E%E9%80%80%E6%AC%BE
56+
func (c *Client) GetOrder(ctx context.Context, in *request.GetOrderReq) (*response.GetOrderRes, error) {
57+
return postJson[request.GetOrderReq, response.GetOrderRes](ctx, c, "/retail/B2b/getorder", in)
58+
}
59+
60+
// Refund 退款
61+
func (c *Client) Refund(ctx context.Context, in *request.RefundReq) (*response.RefundRes, error) {
62+
if in.AppKey != "" {
63+
c.appKey = in.AppKey
64+
}
65+
return postJson[request.RefundReq, response.RefundRes](ctx, c, "/retail/B2b/refund", in)
66+
}
67+
68+
// GetRefund 查询退款
69+
func (c *Client) GetRefund(ctx context.Context, in *request.GetRefundReq) (*response.GetRefundRes, error) {
70+
return postJson[request.GetRefundReq, response.GetRefundRes](ctx, c, "/retail/B2b/getrefund", in)
71+
}
72+
73+
// AddProfitSharingAccount 添加分账方
74+
func (c *Client) AddProfitSharingAccount(ctx context.Context, in *request.AddProfitSharingAccountReq) (*response.AddProfitSharingAccountRes, error) {
75+
return postJson[request.AddProfitSharingAccountReq, response.AddProfitSharingAccountRes](ctx, c, "/retail/B2b/addprofitsharingaccount", in)
76+
}
77+
78+
// DelProfitSharingAccount 删除分账方
79+
func (c *Client) DelProfitSharingAccount(ctx context.Context, in *request.DelProfitSharingAccountReq) (*response.DelProfitSharingAccountRes, error) {
80+
return postJson[request.DelProfitSharingAccountReq, response.DelProfitSharingAccountRes](ctx, c, "/retail/B2b/delprofitsharingaccount", in)
81+
}
82+
83+
// QueryProfitSharingAccount 查询分账方
84+
func (c *Client) QueryProfitSharingAccount(ctx context.Context, in *request.QueryProfitSharingAccountReq) (*response.QueryProfitSharingAccountRes, error) {
85+
return postJson[request.QueryProfitSharingAccountReq, response.QueryProfitSharingAccountRes](ctx, c, "/retail/B2b/queryprofitsharingaccount", in)
86+
}
87+
88+
// CreateProfitSharingOrder 请求分账
89+
func (c *Client) CreateProfitSharingOrder(ctx context.Context, in *request.CreateProfitSharingOrderReq) (*response.CreateProfitSharingOrderRes, error) {
90+
return postJson[request.CreateProfitSharingOrderReq, response.CreateProfitSharingOrderRes](ctx, c, "/retail/B2b/createprofitsharingorder", in)
91+
}
92+
93+
// QueryProfitSharingOrder 查询分账结果
94+
func (c *Client) QueryProfitSharingOrder(ctx context.Context, in *request.QueryProfitSharingOrderReq) (*response.QueryProfitSharingOrderRes, error) {
95+
return postJson[request.QueryProfitSharingOrderReq, response.QueryProfitSharingOrderRes](ctx, c, "/retail/B2b/queryprofitsharingorder?", in)
96+
}
97+
98+
// QueryProfitSharingRemainAmt 查询分账剩余金额
99+
func (c *Client) QueryProfitSharingRemainAmt(ctx context.Context, in *request.QueryProfitSharingRemainAmtReq) (*response.QueryProfitSharingRemainAmtRes, error) {
100+
return postJson[request.QueryProfitSharingRemainAmtReq, response.QueryProfitSharingRemainAmtRes](ctx, c, "/retail/B2b/queryprofitsharingremainamt", in)
101+
}
102+
103+
// FinishProfitSharingOrder 完成分账
104+
func (c *Client) FinishProfitSharingOrder(ctx context.Context, in *request.FinishProfitSharingOrderReq) (*response.FinishProfitSharingOrderRes, error) {
105+
return postJson[request.FinishProfitSharingOrderReq, response.FinishProfitSharingOrderRes](ctx, c, "/retail/B2b/finishprofitsharingorder", in)
106+
}
107+
108+
// RefundProfitSharing 请求分账回退
109+
func (c *Client) RefundProfitSharing(ctx context.Context, in *request.RefundProfitSharingReq) (*response.RefundProfitSharingRes, error) {
110+
return postJson[request.RefundProfitSharingReq, response.RefundProfitSharingRes](ctx, c, "/retail/B2b/refundprofitsharing", in)
111+
}
112+
113+
// QueryRefundProfitSharingOrder 查询分账回退结果
114+
func (c *Client) QueryRefundProfitSharingOrder(ctx context.Context, in *request.QueryRefundProfitSharingOrderReq) (*response.QueryRefundProfitSharingOrderRes, error) {
115+
return postJson[request.QueryRefundProfitSharingOrderReq, response.QueryRefundProfitSharingOrderRes](ctx, c, "/retail/B2b/queryrefundprofitsharingorder", in)
116+
}
117+
118+
// DownloadBill 查看账单
119+
func (c *Client) DownloadBill(ctx context.Context, in *request.DownloadBillReq) (*response.DownloadBillRes, error) {
120+
return postJson[request.DownloadBillReq, response.DownloadBillRes](ctx, c, "/retail/B2b/downloadbill", in)
121+
}

src/miniProgram/b2b/provider.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package b2b
2+
3+
import (
4+
"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel"
5+
)
6+
7+
func RegisterProvider(app kernel.ApplicationInterface) (*Client, error) {
8+
baseClient, err := kernel.NewBaseClient(&app, nil)
9+
config := app.GetConfig()
10+
appKey := config.GetString("app_key", "")
11+
sandboxAppKey := config.GetString("sandbox_app_key", "")
12+
13+
if err != nil {
14+
return nil, err
15+
}
16+
return &Client{
17+
baseClient,
18+
appKey,
19+
sandboxAppKey,
20+
}, nil
21+
22+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package request
2+
3+
type GetOrderReq struct {
4+
MchID string `json:"mchid"` // 微信 b2b 支付配的商户号,和商户订单号配合使用
5+
OutTradeNo string `json:"out_trade_no,omitempty"`
6+
OrderId string `json:"order_id,omitempty"`
7+
}
8+
9+
type RefundReq struct {
10+
MchID string `json:"mchid"` // 微信商户号
11+
OutTradeNo string `json:"out_trade_no,omitempty"` // 商户订单号
12+
OrderID string `json:"order_id,omitempty"` // B2b支付订单号
13+
OutRefundNo string `json:"out_refund_no"` // 商户退款单号
14+
RefundAmount int64 `json:"refund_amount"` // 退款金额,单位为分,只能为整数,不能超过原订单支付金额。
15+
RefundFrom int `json:"refund_from"` // 退款来源,枚举值 1:人工客服退款 2:用户自己退款 3:其他
16+
RefundReason int `json:"refund_reason,omitempty"` // 退款原因,枚举值 0:暂无描述 1:产品问题 2:售后问题 3:意愿问题 4:价格问题 5:其他原因
17+
AppKey string `json:"-" xml:"-"` // 唯独这个接口一定要现网 appKey,覆盖计算
18+
}
19+
20+
type GetRefundReq struct {
21+
MchID string `json:"mchid"` // 微信商户号
22+
OutRefundNo string `json:"out_refund_no,omitempty"` // 商户退款单号
23+
RefundID string `json:"refund_id,omitempty"` // B2b支付退款单号
24+
}
25+
26+
type AddProfitSharingAccountReq struct {
27+
ProfitSharingRelationType string `json:"profit_sharing_relation_type"` // 分账接收方关系类型
28+
PayeeType string `json:"payee_type"` // 分账接收方类型
29+
PayeeID string `json:"payee_id"` // 分账接收方标识
30+
PayeeName string `json:"payee_name,omitempty"` // 分账接收方名称
31+
Env int `json:"env"` // 环境类型
32+
}
33+
34+
type DelProfitSharingAccountReq struct {
35+
PayeeType string `json:"payee_type"` // 分账接收方类型
36+
PayeeID string `json:"payee_id"` // 分账接收方标识
37+
Env int `json:"env"` // 环境类型
38+
}
39+
40+
type QueryProfitSharingAccountReq struct {
41+
Offset int `json:"offset"` // 偏移量
42+
Limit int `json:"limit"` // 查询数量
43+
}
44+
45+
type CreateProfitSharingOrderReq struct {
46+
MchID string `json:"mchid"` // 子商户id
47+
OutTradeNo string `json:"out_trade_no"` // 支付单id
48+
ProfitFee int64 `json:"profit_fee"` // 分账费用, 单位:分,不超过支付单本身的金额
49+
ReceiverType string `json:"receiver_type"` // 分账接收方类型
50+
ReceiverAccount string `json:"receiver_account"` // 分账接收方账号
51+
}
52+
53+
type QueryProfitSharingOrderReq struct {
54+
MchID string `json:"mchid"` // 商户号,必填
55+
OutTradeNo string `json:"out_trade_no"` // 支付单 id,必填
56+
ReceiverType string `json:"receiver_type"` // 分账接收方类型,必填
57+
ReceiverAccount string `json:"receiver_account"` // 分账接收方账号,必填
58+
}
59+
60+
type QueryProfitSharingRemainAmtReq struct {
61+
MchID string `json:"mchid"` // 子商户id,必填
62+
OutTradeNo string `json:"out_trade_no"` // 订单 id,必填
63+
Env int `json:"env"` // 环境参数,0标识正式(默认值),1 标识沙盒,否
64+
}
65+
66+
type FinishProfitSharingOrderReq struct {
67+
MchID string `json:"mchid"` // 子商户id,必填
68+
OutTradeNo string `json:"out_trade_no"` // 订单 id,必填
69+
}
70+
71+
type RefundProfitSharingReq struct {
72+
MchID string `json:"mchid"` // 商户号 id,必填
73+
OutTradeNo string `json:"out_trade_no"` // 订单 id,必填
74+
OutRefundNo string `json:"out_refund_no"` // 退款单 id,必填
75+
PayeeType string `json:"payee_type"` // 退款分账方类型,必填
76+
PayeeID string `json:"payee_id"` // 退款分账方 id,必填
77+
RefundAmt int64 `json:"refund_amt"` // 退款金额,必填
78+
}
79+
80+
type QueryRefundProfitSharingOrderReq struct {
81+
MchID string `json:"mchid"` // 商户号 id,必填
82+
OutTradeNo string `json:"out_trade_no"` // 订单 id,必填
83+
OutRefundNo string `json:"out_refund_no"` // 退款单 id,必填
84+
PayeeType string `json:"payee_type"` // 退款分账方类型,必填
85+
PayeeID string `json:"payee_id"` // 退款分账方 id,必填
86+
}
87+
88+
type DownloadBillReq struct {
89+
MchID string `json:"mchid"` // 商户号,必填
90+
BillDate string `json:"bill_date"` // 账单日期,格式:yyyymmdd,必填
91+
}

0 commit comments

Comments
 (0)