package main import ( "context" "encoding/json" "errors" "flag" "fmt" "os" "strings" "time" pay007 "gitea.s1f.ren/shiran/007pay-sdk.git" ) type globalConfig struct { BaseURL string PID string Key string Timeout time.Duration } func main() { if err := run(os.Args[1:]); err != nil { fmt.Fprintln(os.Stderr, "error:", err) os.Exit(1) } } func run(args []string) error { global := flag.NewFlagSet("007pay", flag.ContinueOnError) global.SetOutput(os.Stderr) cfg := globalConfig{} global.StringVar(&cfg.BaseURL, "base-url", envAny("PAY007_BASE_URL", "007PAY_BASE_URL"), "007Pay service base URL") global.StringVar(&cfg.PID, "pid", envAny("PAY007_PID", "007PAY_PID"), "merchant app pid/token") global.StringVar(&cfg.Key, "key", envAny("PAY007_KEY", "007PAY_KEY"), "merchant app secret key") global.DurationVar(&cfg.Timeout, "timeout", 15*time.Second, "HTTP request timeout") global.Usage = usage if err := global.Parse(args); errors.Is(err, flag.ErrHelp) { return nil } else if err != nil { return err } rest := global.Args() if len(rest) == 0 { usage() return errors.New("missing command") } cmd, cmdArgs := rest[0], rest[1:] switch cmd { case "submit": return submit(cfg, cmdArgs) case "query": return query(cfg, cmdArgs) case "refund": return refund(cfg, cmdArgs) case "orders": return orders(cfg, cmdArgs) case "manual": return manual(cfg, cmdArgs) case "stats": return stats(cfg, cmdArgs) case "sign": return signParams(cfg, cmdArgs) case "verify": return verifyParams(cfg, cmdArgs) case "help", "-h", "--help": usage() return nil default: usage() return fmt.Errorf("unknown command %q", cmd) } } func newClient(cfg globalConfig) (*pay007.Client, error) { return pay007.NewClient(cfg.BaseURL, cfg.PID, cfg.Key, pay007.WithTimeout(cfg.Timeout)) } func submit(cfg globalConfig, args []string) error { fs := flag.NewFlagSet("submit", flag.ContinueOnError) req := pay007.SubmitRequest{} fs.StringVar(&req.OutTradeNo, "out-trade-no", "", "merchant order number") fs.StringVar(&req.Name, "name", "", "order subject") fs.StringVar(&req.Money, "money", "", "amount in yuan, for example 9.90") fs.StringVar(&req.Type, "type", "", "channel code, for example alipay or wechat") fs.StringVar(&req.ClientType, "clienttype", "", "client type: pc or mobile") fs.StringVar(&req.NotifyURL, "notify-url", "", "merchant async notify URL") fs.StringVar(&req.ReturnURL, "return-url", "", "merchant sync return URL") fs.StringVar(&req.Body, "body", "", "order description") fs.StringVar(&req.Attach, "attach", "", "extra data") fs.StringVar(&req.ExpireTime, "expire-time", "", "order lifetime in minutes or an absolute expiration time (default 10 minutes)") if err := fs.Parse(args); err != nil { return err } client, err := newClient(cfg) if err != nil { return err } resp, err := client.Submit(context.Background(), req) if err != nil { return err } return printJSON(resp) } func query(cfg globalConfig, args []string) error { fs := flag.NewFlagSet("query", flag.ContinueOnError) req := pay007.QueryRequest{} fs.StringVar(&req.TradeNo, "trade-no", "", "007Pay order number") fs.StringVar(&req.OutTradeNo, "out-trade-no", "", "merchant order number") if err := fs.Parse(args); err != nil { return err } client, err := newClient(cfg) if err != nil { return err } resp, err := client.Query(context.Background(), req) if err != nil { return err } return printJSON(resp) } func refund(cfg globalConfig, args []string) error { fs := flag.NewFlagSet("refund", flag.ContinueOnError) req := pay007.RefundRequest{} fs.StringVar(&req.TradeNo, "trade-no", "", "007Pay order number") fs.StringVar(&req.OutTradeNo, "out-trade-no", "", "merchant order number") fs.StringVar(&req.Money, "money", "", "refund amount in yuan; omit to refund full order") fs.StringVar(&req.Reason, "reason", "", "refund reason") if err := fs.Parse(args); err != nil { return err } client, err := newClient(cfg) if err != nil { return err } resp, err := client.Refund(context.Background(), req) if err != nil { return err } return printJSON(resp) } func orders(cfg globalConfig, args []string) error { fs := flag.NewFlagSet("orders", flag.ContinueOnError) req := pay007.OrdersRequest{} status := fs.Int("status", -1, "order status: 0 pending, 1 paid, 2 closed, 3 refunded") fs.StringVar(&req.OrderNo, "order-no", "", "007Pay or merchant order number") fs.IntVar(&req.Page, "page", 1, "page number") fs.IntVar(&req.PageSize, "page-size", 20, "page size") if err := fs.Parse(args); err != nil { return err } if *status >= 0 { req.Status = status } client, err := newClient(cfg) if err != nil { return err } resp, err := client.Orders(context.Background(), req) if err != nil { return err } return printJSON(resp) } func manual(cfg globalConfig, args []string) error { fs := flag.NewFlagSet("manual", flag.ContinueOnError) req := pay007.ManualRequest{} fs.StringVar(&req.OutTradeNo, "out-trade-no", "", "merchant order number") fs.StringVar(&req.Name, "name", "", "order subject") fs.StringVar(&req.Money, "money", "", "amount in yuan") fs.StringVar(&req.Type, "type", "", "channel code") fs.StringVar(&req.Attach, "attach", "", "extra data") if err := fs.Parse(args); err != nil { return err } client, err := newClient(cfg) if err != nil { return err } resp, err := client.Manual(context.Background(), req) if err != nil { return err } return printJSON(resp) } func stats(cfg globalConfig, args []string) error { fs := flag.NewFlagSet("stats", flag.ContinueOnError) req := pay007.StatsRequest{} fs.StringVar(&req.Begin, "begin", "", "begin time: 2006-01-02 15:04:05") fs.StringVar(&req.End, "end", "", "end time: 2006-01-02 15:04:05") if err := fs.Parse(args); err != nil { return err } client, err := newClient(cfg) if err != nil { return err } resp, err := client.Stats(context.Background(), req) if err != nil { return err } return printJSON(resp) } func signParams(cfg globalConfig, args []string) error { if strings.TrimSpace(cfg.Key) == "" { return errors.New("--key or PAY007_KEY is required") } params, err := parseKV(args) if err != nil { return err } if cfg.PID != "" { params["pid"] = cfg.PID } params["sign_type"] = "MD5" params["sign"] = pay007.BuildSign(params, cfg.Key) return printJSON(params) } func verifyParams(cfg globalConfig, args []string) error { if strings.TrimSpace(cfg.Key) == "" { return errors.New("--key or PAY007_KEY is required") } params, err := parseKV(args) if err != nil { return err } return printJSON(map[string]any{ "ok": pay007.VerifySign(params, cfg.Key), "sign": pay007.BuildSign(params, cfg.Key), }) } func parseKV(args []string) (map[string]string, error) { params := map[string]string{} for _, arg := range args { k, v, ok := strings.Cut(arg, "=") if !ok || strings.TrimSpace(k) == "" { return nil, fmt.Errorf("invalid param %q, expected key=value", arg) } params[k] = v } return params, nil } func printJSON(v any) error { data, err := json.MarshalIndent(v, "", " ") if err != nil { return err } fmt.Println(string(data)) return nil } func envAny(keys ...string) string { for _, key := range keys { if v := strings.TrimSpace(os.Getenv(key)); v != "" { return v } } return "" } func usage() { fmt.Fprintf(os.Stderr, `007Pay merchant SDK CLI Global flags: --base-url 007Pay service base URL, or PAY007_BASE_URL --pid merchant app pid/token, or PAY007_PID --key merchant app secret key, or PAY007_KEY --timeout HTTP timeout, default 15s Commands: submit create a payment order query query and channel-check an order refund refund an order orders list merchant orders manual create a manual paid order stats query merchant stats sign sign key=value params verify verify key=value params with sign=... Examples: 007pay --base-url http://127.0.0.1:8081 --pid app-token --key secret submit --out-trade-no T1001 --name test --money 0.01 --type alipay 007pay query --out-trade-no T1001 007pay sign out_trade_no=T1001 name=test money=0.01 type=alipay `) }