Skip to content

Commit 6804107

Browse files
committed
Merge remote-tracking branch 'fred/main'
2 parents d63f7eb + 2a52a5b commit 6804107

File tree

7 files changed

+147
-91
lines changed

7 files changed

+147
-91
lines changed

api.go

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ func (s *ApiServer) handleCodepayCreateHelper(sessionId string, w http.ResponseW
227227
if err != nil {
228228
// print error
229229
slog.Error("failed to generate qr code", "error", err)
230-
resBuf, err := json.MarshalIndent(map[string]interface{}{
230+
resBuf, err := json.MarshalIndent(map[string]any{
231231
"success": false,
232232
"message": "failed to generate qr code: server internal error",
233233
}, "", " ")
@@ -319,12 +319,12 @@ func (s *ApiServer) handleCodepayQueryHelper(sessionId string, w http.ResponseWr
319319
return
320320
}
321321

322-
var response map[string]interface{}
322+
var response map[string]any
323323
// check if monDealCur exists
324324
if _, ok := res["monDealCur"]; ok {
325325
// monDealCur exists, it's a completed deal
326326
delete(codepayInstances, code)
327-
response = map[string]interface{}{
327+
response = map[string]any{
328328
"status": 1,
329329
"message": "payment completed",
330330
"money": res["monDealCur"],
@@ -335,13 +335,13 @@ func (s *ApiServer) handleCodepayQueryHelper(sessionId string, w http.ResponseWr
335335
if time.Now().Unix()-codepay.Creation > 30 {
336336
// 30s limit exceeded, remove the codepay instance
337337
delete(codepayInstances, code)
338-
response = map[string]interface{}{
338+
response = map[string]any{
339339
"status": 2,
340340
"message": "payment code expired",
341341
}
342342
} else {
343343
// 30s limit not exceeded, keep the codepay instance
344-
response = map[string]interface{}{
344+
response = map[string]any{
345345
"status": 0,
346346
"message": "pending",
347347
}
@@ -388,6 +388,63 @@ func (s *ApiServer) handleCodepayQueryPath(w http.ResponseWriter, r *http.Reques
388388
s.handleCodepayQueryHelper(sessionId, w, r)
389389
}
390390

391+
func (s *ApiServer) handleRecentTransactions(w http.ResponseWriter, r *http.Request) {
392+
w.Header().Set("Access-Control-Allow-Origin", "*")
393+
if r.Method == http.MethodOptions {
394+
return
395+
}
396+
397+
vars := mux.Vars(r)
398+
sessionId := vars["sessionId"]
399+
if sessionId == "" {
400+
http.Error(w, "sessionId required in path", http.StatusBadRequest)
401+
return
402+
}
403+
404+
user := s.cfg.SelectUserFromSessionId(sessionId)
405+
if user == nil {
406+
http.Error(w, "user with sessionId="+sessionId+" not found", http.StatusNotFound)
407+
return
408+
}
409+
410+
_, transactions, err := xfb.CardQuerynoPage(user.SessionId, user.YmUserId, time.Now())
411+
if err != nil {
412+
http.Error(w, "unable to fetch recent transactions: "+err.Error(), http.StatusInternalServerError)
413+
return
414+
}
415+
416+
// Limit to at most 3 transactions
417+
if len(transactions) > 3 {
418+
transactions = transactions[len(transactions)-3:]
419+
}
420+
421+
resBuf, err := json.MarshalIndent(transactions, "", " ")
422+
if err != nil {
423+
http.Error(w, err.Error(), http.StatusInternalServerError)
424+
return
425+
}
426+
427+
w.Header().Set("Content-Type", "application/json")
428+
w.WriteHeader(http.StatusOK)
429+
w.Write(resBuf)
430+
}
431+
432+
func (s *ApiServer) handleRecentTransactionsPath(w http.ResponseWriter, r *http.Request) {
433+
w.Header().Set("Access-Control-Allow-Origin", "*")
434+
if r.Method == http.MethodOptions {
435+
return
436+
}
437+
438+
vars := mux.Vars(r)
439+
sessionId := vars["sessionId"]
440+
if sessionId == "" {
441+
http.Error(w, "sessionId required in path", http.StatusBadRequest)
442+
return
443+
}
444+
445+
s.handleRecentTransactions(w, r)
446+
}
447+
391448
func CreateApiServer(cfg *Config) *mux.Router {
392449
r := mux.NewRouter()
393450
s := &ApiServer{
@@ -405,10 +462,12 @@ func CreateApiServer(cfg *Config) *mux.Router {
405462
// Codepay endpoints
406463
r.HandleFunc("/api/v1/codepay/create", s.handleCodepayCreate).Methods(http.MethodPost, http.MethodOptions)
407464
r.HandleFunc("/api/v1/codepay/query", s.handleCodepayQuery).Methods(http.MethodGet, http.MethodOptions)
465+
r.HandleFunc("/api/v1/codepay/recentTransactions", s.handleRecentTransactions).Methods(http.MethodGet, http.MethodOptions)
408466

409467
// Codepay endpoints with sessionId embedded in path
410468
r.HandleFunc("/api/v1/codepay/{sessionId}/create", s.handleCodepayCreatePath).Methods(http.MethodGet, http.MethodPost, http.MethodOptions)
411469
r.HandleFunc("/api/v1/codepay/{sessionId}/query", s.handleCodepayQueryPath).Methods(http.MethodGet, http.MethodOptions)
470+
r.HandleFunc("/api/v1/codepay/{sessionId}/recentTransactions", s.handleRecentTransactionsPath).Methods(http.MethodGet, http.MethodOptions)
412471

413472
r.Use(mux.CORSMethodMiddleware(r))
414473
return r

cmd/main/hello.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -80,18 +80,18 @@ func sendNotify(key string, t *xfb.Trans) error {
8080
return nil
8181
}
8282
bot := notification.WeComBot{Key: key}
83-
msg := map[string]interface{}{
83+
msg := map[string]any{
8484
"msgtype": "template_card",
85-
"template_card": map[string]interface{}{
85+
"template_card": map[string]any{
8686
"card_type": "text_notice",
87-
"source": map[string]interface{}{
87+
"source": map[string]any{
8888
"desc": "校园卡账单",
8989
},
90-
"main_title": map[string]interface{}{
90+
"main_title": map[string]any{
9191
"title": t.Address,
9292
"desc": t.FeeName,
9393
},
94-
"emphasis_content": map[string]interface{}{
94+
"emphasis_content": map[string]any{
9595
"title": formatExpense(t.Money),
9696
},
9797
"horizontal_content_list": []map[string]string{
@@ -112,7 +112,7 @@ func sendNotify(key string, t *xfb.Trans) error {
112112
"value": t.Time,
113113
},
114114
},
115-
"card_action": map[string]interface{}{
115+
"card_action": map[string]any{
116116
"type": 1,
117117
"url": cfg.AuthLocalUrl,
118118
},
@@ -126,14 +126,14 @@ func sendError(key string, err error, u *xfbbroker.User) error {
126126
return nil
127127
}
128128
bot := notification.WeComBot{Key: key}
129-
msg := map[string]interface{}{
129+
msg := map[string]any{
130130
"msgtype": "template_card",
131-
"template_card": map[string]interface{}{
131+
"template_card": map[string]any{
132132
"card_type": "text_notice",
133-
"source": map[string]interface{}{
133+
"source": map[string]any{
134134
"desc": "校园卡账单",
135135
},
136-
"main_title": map[string]interface{}{
136+
"main_title": map[string]any{
137137
"title": "请求错误",
138138
"desc": u.Name,
139139
},
@@ -144,7 +144,7 @@ func sendError(key string, err error, u *xfbbroker.User) error {
144144
"value": u.YmUserId,
145145
},
146146
},
147-
"card_action": map[string]interface{}{
147+
"card_action": map[string]any{
148148
"type": 1,
149149
"url": cfg.AuthLocalUrl,
150150
},

xfb/app.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package xfb
22

33
// func GetThirdUserAuthorize(ymToken, ymUserId string) {
4-
// PostJSON(XfbApp+"/app/login/getThirdUserAuthorize", "", map[string]interface{}{
4+
// PostJSON(XfbApp+"/app/login/getThirdUserAuthorize", "", map[string]any{
55
// "schoolCode": "20090820",
66
// "platform": "WECHAT_H5",
77
// "ymToken": ymToken,

xfb/codepay.go

Lines changed: 10 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
11
package xfb
22

33
import (
4-
"crypto/tls"
5-
"encoding/json"
64
"fmt"
75
"image/png"
8-
"io"
9-
"net/http"
106
"net/url"
117
"os"
12-
"strings"
138
"time"
149

1510
"github.com/skip2/go-qrcode"
@@ -23,57 +18,29 @@ const (
2318
type QrPayCode struct {
2419
QRCode string
2520
SessionID string
26-
client *http.Client
2721
Creation int64
2822
}
2923

3024
func GenerateQrPayCode(sessionId string) (*QrPayCode, error) {
31-
client := &http.Client{
32-
Transport: &http.Transport{
33-
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
34-
},
35-
}
36-
3725
form := url.Values{
3826
"platform": []string{"WECHAT_H5"},
3927
"schoolCode": []string{"20090820"}}
4028

41-
req, err := http.NewRequest("POST", XfbWebApp+qrCodeEndpointUrl, strings.NewReader(form.Encode()))
42-
if err != nil {
43-
return nil, err
44-
}
29+
var result XfbResponse
4530

46-
req.AddCookie(&http.Cookie{Name: "shiroJID", Value: sessionId})
47-
resp, err := client.Do(req)
31+
_, err := PostForm(XfbWebApp+qrCodeEndpointUrl, sessionId, form, &result)
4832
if err != nil {
4933
return nil, err
5034
}
51-
defer resp.Body.Close()
5235

53-
if resp.StatusCode != http.StatusOK {
54-
bodyCnt, _ := io.ReadAll(resp.Body)
55-
return nil, fmt.Errorf("unexpected status code: %d; response body: %s", resp.StatusCode, string(bodyCnt))
56-
}
57-
58-
var result struct {
59-
Success bool `json:"success"`
60-
StatusCode int `json:"statusCode"`
61-
Data string `json:"data"`
62-
}
63-
64-
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
65-
return nil, err
66-
}
67-
68-
if !result.Success || result.StatusCode != 0 {
69-
return nil, fmt.Errorf("API error: success=%v, statusCode=%d",
70-
result.Success, result.StatusCode)
36+
if result.StatusCode != 0 {
37+
return nil, fmt.Errorf("API error: statusCode=%d, statusCode=%d",
38+
result.StatusCode, result.StatusCode)
7139
}
7240

7341
return &QrPayCode{
74-
QRCode: result.Data,
42+
QRCode: result.Data.(string),
7543
SessionID: sessionId,
76-
client: client,
7744
Creation: time.Now().Unix(),
7845
}, nil
7946
}
@@ -102,30 +69,18 @@ func (q *QrPayCode) GetQrPngBuf(size int) ([]byte, error) {
10269
return qr.PNG(size)
10370
}
10471

105-
func (q *QrPayCode) GetResult() (map[string]interface{}, error) {
72+
func (q *QrPayCode) GetResult() (map[string]any, error) {
10673
form := url.Values{}
10774
form.Add("qrCode", q.QRCode)
10875

109-
req, err := http.NewRequest("POST", XfbWebApp+qrResultEndpointUrl, strings.NewReader(form.Encode()))
110-
if err != nil {
111-
return nil, err
112-
}
76+
var result XfbResponse
11377

114-
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
115-
req.AddCookie(&http.Cookie{Name: "shiroJID", Value: q.SessionID})
116-
117-
resp, err := q.client.Do(req)
78+
_, err := PostForm(XfbWebApp+qrResultEndpointUrl, q.SessionID, form, &result)
11879
if err != nil {
11980
return nil, err
12081
}
121-
defer resp.Body.Close()
122-
123-
var result map[string]interface{}
124-
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
125-
return nil, err
126-
}
12782

128-
return result, nil
83+
return result.Data.(map[string]any), nil
12984
}
13085

13186
// func main() {

xfb/http.go

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package xfb
22

33
import (
4+
"crypto/tls"
45
"errors"
56
"fmt"
67
"net/http"
8+
"net/url"
9+
"strings"
710
"time"
811

912
"github.com/yiffyi/gorad/radhttp"
@@ -16,6 +19,9 @@ const XfbApp = "https://application.xiaofubao.com"
1619
var client = &http.Client{
1720
Timeout: time.Second * 30,
1821
CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse },
22+
Transport: &http.Transport{
23+
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
24+
},
1925
}
2026

2127
// func Get(url string, sessionId string, v XfbBaseResponse) (newSessionId string, err error) {
@@ -54,7 +60,43 @@ var client = &http.Client{
5460
// }
5561
// }
5662

57-
func Post(url string, sessionId string, payload map[string]interface{}, v XfbBaseResponse) (newSessionId string, err error) {
63+
func PostForm(url string, sessionId string, form url.Values, v XfbBaseResponse) (newSessionId string, err error) {
64+
req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(form.Encode()))
65+
if err != nil {
66+
return
67+
}
68+
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
69+
if len(sessionId) > 0 {
70+
req.AddCookie(&http.Cookie{Name: "shiroJID", Value: sessionId})
71+
}
72+
73+
resp, b, err := radhttp.JSONDo(client, req, v)
74+
if resp == nil {
75+
return
76+
}
77+
78+
if resp.StatusCode != http.StatusOK {
79+
err = fmt.Errorf("bad HTTP Status: %s\n\t%s", resp.Status, string(b))
80+
return
81+
}
82+
83+
newSessionId = ""
84+
for _, v := range resp.Cookies() {
85+
if v.Name == "shiroJID" {
86+
newSessionId = v.Value
87+
}
88+
}
89+
90+
if code := v.GetStatusCode(); code == 0 {
91+
err = nil
92+
return
93+
} else {
94+
err = fmt.Errorf("bad statusCode from xfb: %d", code)
95+
return
96+
}
97+
}
98+
99+
func Post(url string, sessionId string, payload map[string]any, v XfbBaseResponse) (newSessionId string, err error) {
58100
req, err := radhttp.NewJSONPostRequest(url, payload)
59101
if err != nil {
60102
return

xfb/model.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ type XfbBaseResponse interface {
44
GetStatusCode() int
55
}
66
type XfbResponse struct {
7-
StatusCode int `json:"statusCode"`
8-
Message string `json:"message"`
9-
Data interface{} `json:"data"`
10-
SessionId string `json:"-"`
7+
StatusCode int `json:"statusCode"`
8+
Message string `json:"message"`
9+
Data any `json:"data"`
10+
SessionId string `json:"-"`
1111
}
1212

1313
func (r *XfbResponse) GetStatusCode() int {

0 commit comments

Comments
 (0)