Skip to content

Commit

Permalink
Merge pull request #382 from ArtisanCloud/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
Matrix-X authored Aug 23, 2023
2 parents 144a082 + bc0c1c3 commit a1dbae4
Show file tree
Hide file tree
Showing 12 changed files with 273 additions and 17 deletions.
20 changes: 20 additions & 0 deletions src/kernel/Encryptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ package kernel
import (
"bytes"
"crypto/aes"
"crypto/hmac"
"crypto/sha1"
"crypto/sha256"
"encoding/base64"
"encoding/binary"
"encoding/hex"
"encoding/xml"
"fmt"
"github.com/ArtisanCloud/PowerLibs/v3/object"
Expand Down Expand Up @@ -236,3 +239,20 @@ func (encryptor *Encryptor) Signature(token, timestamp, nonce, data string) stri
signature := fmt.Sprintf("%x", sha.Sum(nil))
return signature
}

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

func CalcSignature(postBody, sessionKey string) string {
needSignMsg := postBody
h := hmac.New(sha256.New, []byte(sessionKey))
h.Write([]byte(needSignMsg))
signature := hex.EncodeToString(h.Sum(nil))
return signature
}
18 changes: 17 additions & 1 deletion src/kernel/messages/news.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,21 @@ func (msg *News) OverridePropertiesToArray() {

msg.PropertiesToArray = func(data *object.HashMap, aliases *object.HashMap) (*object.HashMap, error) {
arrayItems := msg.Get("items", nil).([]*object.HashMap)
title := msg.Get("title", nil).(string)
description := msg.Get("description", nil).(string)
picUrl := msg.Get("picUrl", nil).(string)
url := msg.Get("url", nil).(string)
arrayMapItems := []*object.HashMap{}
for _, item := range arrayItems {
arrayMapItems = append(arrayMapItems, item)
}

return &object.HashMap{
"articles": arrayMapItems,
"articles": arrayMapItems,
"title": title,
"description": description,
"picUrl": picUrl,
"url": url,
}, nil
}

Expand All @@ -42,6 +50,10 @@ func (msg *News) OverrideToXmlArray() {
items := []*object.HashMap{}

getItem := msg.Get("items", nil)
title := msg.Get("title", nil).(string)
description := msg.Get("description", nil).(string)
picUrl := msg.Get("picUrl", nil).(string)
url := msg.Get("url", nil).(string)
if getItem != nil {
arrayItems := getItem.([]*object.HashMap)
for _, item := range arrayItems {
Expand All @@ -55,6 +67,10 @@ func (msg *News) OverrideToXmlArray() {
return &object.HashMap{
"ArticleCount": len(items),
"Articles": items,
"Title": title,
"Description": description,
"PicUrl": picUrl,
"Url": url,
}
}
}
24 changes: 21 additions & 3 deletions src/miniProgram/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,18 @@ import (
"github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram/updatableMessage"
"github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram/urlLink"
"github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram/urlScheme"
"github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram/virtualPayment"
"github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram/wxaCode"
"github.com/ArtisanCloud/PowerWeChat/v3/src/officialAccount/server"
)

type MiniProgram struct {
*kernel.ServiceContainer

Base *base.Client
AccessToken *auth.AccessToken
Auth *auth.Client
Base *base.Client
VirtualPayment *virtualPayment.Client
AccessToken *auth.AccessToken
Auth *auth.Client

Server *server.Guard

Expand Down Expand Up @@ -107,6 +109,10 @@ type UserConfig struct {
Token string
AESKey string

// 小程序虚拟支付新增配置
AppKey string // 现网AppKey
OfferID string // OfferID(支付应用ID) 等同于商户号

ResponseType string
Log Log
OAuth OAuth
Expand Down Expand Up @@ -359,6 +365,12 @@ func NewMiniProgram(config *UserConfig, extraInfos ...*kernel.ExtraInfo) (*MiniP
return nil, err
}

//-------------- Virtual Pay --------------
app.VirtualPayment, err = virtualPayment.RegisterProvider(app)
if err != nil {
return nil, err
}

return app, err
}

Expand Down Expand Up @@ -456,6 +468,9 @@ func (app *MiniProgram) GetComponent(name string) interface{} {
case "MiniDramaVDO":
return app.MiniDramaVOD

case "VirtualPayment":
return app.VirtualPayment

case "Logger":
return app.Logger

Expand All @@ -477,6 +492,9 @@ func MapUserConfig(userConfig *UserConfig) (*object.HashMap, error) {
"app_id": userConfig.AppID,
"secret": userConfig.Secret,

"app_key": userConfig.AppKey, // 现网AppKey
"offer_id": userConfig.OfferID, // OfferID(支付应用ID) 等同于商户号

"token": userConfig.Token,
"aes_key": userConfig.AESKey,
"refresh_token": userConfig.RefreshToken,
Expand Down
11 changes: 9 additions & 2 deletions src/miniProgram/encryptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,15 @@ func NewEncryptor(appID, token, aesKey string) (*Encryptor, error) {

func (encryptor Encryptor) DecryptData(encrypted string, sessionKey string, iv string) ([]byte, *support.CryptError) {
encryptData := encrypted
_key, _ := base64.StdEncoding.DecodeString(sessionKey)
_iv, _ := base64.StdEncoding.DecodeString(iv)
_key, errDecode := base64.StdEncoding.DecodeString(sessionKey)
if errDecode != nil {
return nil, support.NewCryptError(-1, errDecode.Error())
}

_iv, errDecode := base64.StdEncoding.DecodeString(iv)
if errDecode != nil {
return nil, support.NewCryptError(-1, errDecode.Error())
}

aes := support.AES{}
data, err := aes.Decrypt(encryptData, _key, _iv)
Expand Down
37 changes: 37 additions & 0 deletions src/miniProgram/virtualPayment/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package virtualPayment

import (
"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel"
)

type Client struct {
App *kernel.ApplicationInterface
*kernel.BaseClient

appKey string
offerId string
}

func NewClient(app *kernel.ApplicationInterface) (*Client, error) {
config := (*app).GetConfig()

baseClient, err := kernel.NewBaseClient(app, nil)

if err != nil {

return nil, err
}

client := &Client{
BaseClient: baseClient,
App: app,
appKey: config.GetString("app_key", ""),
offerId: config.GetString("offer_id", ""),
}

client.OverrideGetMiddlewares()
client.RegisterHttpMiddlewares()

return client, nil

}
15 changes: 15 additions & 0 deletions src/miniProgram/virtualPayment/provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package virtualPayment

import (
"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel"
)

func RegisterProvider(app kernel.ApplicationInterface) (*Client, error) {
client, err := NewClient(&app)
if err != nil {
return nil, err
}

return client, nil

}
22 changes: 22 additions & 0 deletions src/miniProgram/virtualPayment/request/request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package request

type VirtualPaymentOrderRequest struct {
SessionKey string `json:"session_key"` // 文档已更新,现在允许存session key 过期时间官方写的是三天,配合前端checkSessionKey组件经测试 官方有bug
ProductId int64 `json:"product_id"` // 商品id
Price int64 `json:"price"` // 金额 单位分
OutTradeNo string `json:"out_trade_no"` // 订单号
Attach string `json:"attach"` // 附加信息
}

type UploadProductsRequest struct {
Env int8 `json:"env"` // 0正式 1测试
UploadItem []*GoodItem `json:"upload_item"`
}

type GoodItem struct {
Id string `json:"id"` // 商品id 同product id 长度(0,64],字符只允许使用字母、数字、'_'、'-'
Name string `json:"name"` // 商品名称 长度(0,1024] 不能有特殊字符
Price int64 `json:"price"` // 道具单价,单位分,需要大于0
Remake string `json:"remake"` // 道具备注,长度(0,1024]
ItemUrl string `json:"item_url"` // 道具图片的url地址,当前仅支持jpg,png等格式 使用查询上传结果接口会发现,经常会报错上传图片失败 需要多传几次
}
21 changes: 21 additions & 0 deletions src/miniProgram/virtualPayment/response/response.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package response

type UploadItem struct {
Id string `json:"id"`
ItemUrl string `json:"item_url"`
Name string `json:"name"`
Price int `json:"price"`
UploadStatus int `json:"upload_status"`
ErrMsg string `json:"errmsg"` // 这个upload status与官方文档返回不一致 可以直接判断errmsg是否为空 代表是否有错误
}

type UploadProductSearchResponse struct {
Cost int `json:"cost"`
End int `json:"end"`
Errcode int `json:"errcode"`
Errmsg string `json:"errmsg"`
Progress int `json:"progress"`
Status int `json:"status"`
Total int `json:"total"`
UploadItem []*UploadItem `json:"upload_item"`
}
82 changes: 82 additions & 0 deletions src/miniProgram/virtualPayment/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package virtualPayment

import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/ArtisanCloud/PowerLibs/v3/object"
"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel"
"github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram/virtualPayment/request"
"github.com/ArtisanCloud/PowerWeChat/v3/src/officialAccount/device/response"
)

// 小程序虚拟支付
// uri固定写死为 requestVirtualPayment
// 官方示例是写死为这个postbody 没有任何相关文档表示这个postbody是怎么来的 以及各个参数的意义,只能直译
// https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/virtual-payment.html#_2-4-%E7%AD%BE%E5%90%8D%E8%AF%A6%E8%A7%A3

func (client *Client) TransactionVirtual(ctx context.Context, in *request.VirtualPaymentOrderRequest) (result *object.StringMap, err error) {

if client.offerId == "" {

return nil, errors.New("offerId is empty")
}

if client.appKey == "" {

return nil, errors.New("appKey is empty")
}

if in.SessionKey == "" {

return nil, errors.New("sessionKey is empty")
}

uri := "requestVirtualPayment"

postBody := fmt.Sprintf(`{"offerId":"%s","buyQuantity":1,"env":0,"currencyType":"CNY","platform":"android","productId":"%d","goodsPrice":%d,"outTradeNo":"%s","attach":"%s"}`, client.offerId, in.ProductId, in.Price, in.OutTradeNo, in.Attach)

paySign := kernel.CalcPaySig(uri, postBody, client.appKey)
signature := kernel.CalcSignature(postBody, in.SessionKey)

// todo 是否要变为强类型
return &object.StringMap{
"post_body": postBody,
"pay_sign": paySign,
"signature": signature,
}, nil

}

// 商品上传
// https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/virtual-payment.html#_2-3-%E6%9C%8D%E5%8A%A1%E5%99%A8API

func (client *Client) StartUploadGoods(ctx context.Context, params *request.UploadProductsRequest) (result *response.BaseResp, err error) {

fmt.Printf("appid: %s, app_key: %s, offer_id: %s \n", (*client.App).GetConfig().GetString("app_id", ""), client.appKey, client.offerId)

if err != nil {

return nil, err
}

postBody, err := json.Marshal(params)

if err != nil {

return nil, err
}

signPost := string(postBody)

// paySign
endpoint := "/xpay/start_upload_goods"
paySign := kernel.CalcPaySig(endpoint, signPost, client.appKey)

uri := fmt.Sprintf("%s?pay_sig=%s", endpoint, paySign)

_, err = client.HttpPostJson(ctx, uri, params, nil, nil, &result)

return result, err
}
19 changes: 15 additions & 4 deletions src/openPlatform/server/guard.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ func (guard *Guard) Notify(request *http.Request, closure func(content *openplat
}

// read body content
requestXML, _ := ioutil.ReadAll(request.Body)
request.Body = ioutil.NopCloser(bytes.NewBuffer(requestXML))
requestXML, _ := io.ReadAll(request.Body)
request.Body = io.NopCloser(bytes.NewBuffer(requestXML))
//println(string(requestXML))

// convert to openplatform event
Expand All @@ -85,14 +85,25 @@ func (guard *Guard) Notify(request *http.Request, closure func(content *openplat
err = xml.Unmarshal(bufDecrypted, msg)
result := closure(callbackEvent, bufDecrypted, msg.InfoType)

var buffResult []byte
// 如果是字符串, "success", "failed"
if str, ok := result.(string); ok {
buffResult = []byte(str)
} else {
strResult, err := object.JsonEncode(result)
if err != nil {
return nil, err
}
buffResult = []byte(strResult)
}

// convert the result to http response
strResult, err := object.JsonEncode(result)
if err != nil {
return nil, err
}
httpRS = &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(bytes.NewBuffer([]byte(strResult))),
Body: io.NopCloser(bytes.NewBuffer(buffResult)),
}

return httpRS, err
Expand Down
Loading

0 comments on commit a1dbae4

Please sign in to comment.