wechatpay_download_certs.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. // Copyright 2021 Tencent Inc. All rights reserved.
  2. package main
  3. import (
  4. "context"
  5. "crypto/x509"
  6. "flag"
  7. "fmt"
  8. "os"
  9. "path/filepath"
  10. "git.nanodreamtech.com/sg/wechatpay-go/core"
  11. "git.nanodreamtech.com/sg/wechatpay-go/core/downloader"
  12. "git.nanodreamtech.com/sg/wechatpay-go/core/option"
  13. "git.nanodreamtech.com/sg/wechatpay-go/utils"
  14. )
  15. var (
  16. mchID string
  17. mchSerialNo string
  18. mchPrivateKeyPath string
  19. mchAPIv3Key string
  20. wechatPayCertificatePath string
  21. outputPath string
  22. )
  23. const errCodeParamError = 1
  24. const errCodeRunError = 2
  25. func init() {
  26. flag.StringVar(&mchID, "m", "", "【必传】`商户号`")
  27. flag.StringVar(&mchSerialNo, "s", "", "【必传】`商户证书序列号`")
  28. flag.StringVar(&mchPrivateKeyPath, "p", "", "【必传】`商户私钥路径`")
  29. flag.StringVar(&mchAPIv3Key, "k", "", "【必传】`商户APIv3密钥`")
  30. flag.StringVar(&wechatPayCertificatePath, "c", "", "【可选】`商户平台证书路径`,用于验签。省略则跳过验签")
  31. flag.StringVar(&outputPath, "o", "./", "【可选】`证书下载保存目录`")
  32. }
  33. func main() {
  34. flag.Parse()
  35. flag.Usage = printUsageAndExit
  36. if err := checkArgs(); err != nil {
  37. reportError("参数有误:", err)
  38. printUsageAndExit()
  39. }
  40. ctx := context.Background()
  41. client, err := createClient(ctx)
  42. if err != nil {
  43. reportError("初始化失败:", err)
  44. os.Exit(errCodeRunError)
  45. }
  46. d, err := downloader.NewCertificateDownloaderWithClient(ctx, client, mchAPIv3Key)
  47. if err != nil {
  48. reportError("下载证书失败:", err)
  49. os.Exit(errCodeRunError)
  50. }
  51. err = saveCertificates(ctx, d)
  52. if err != nil {
  53. reportError("保存证书失败:", err)
  54. os.Exit(errCodeRunError)
  55. }
  56. os.Exit(0)
  57. }
  58. func reportError(message string, err error) {
  59. _, _ = fmt.Fprintf(os.Stderr, message+" %v\n", err)
  60. }
  61. func printUsageAndExit() {
  62. _, _ = fmt.Fprintf(os.Stderr, "usage of wechatpay_download_certs:\n")
  63. flag.PrintDefaults()
  64. os.Exit(errCodeParamError)
  65. }
  66. type paramError struct {
  67. name string
  68. value string
  69. message string
  70. }
  71. // Error 输出 paramError
  72. func (e paramError) Error() string {
  73. if e.value != "" {
  74. return fmt.Sprintf("%v(%v) %v", e.name, e.value, e.message)
  75. }
  76. return fmt.Sprintf("%v %v", e.name, e.message)
  77. }
  78. // revive:disable:cyclomatic
  79. func checkArgs() error {
  80. if mchID == "" {
  81. return paramError{"商户号", mchID, "必传"}
  82. }
  83. if mchSerialNo == "" {
  84. return paramError{"商户证书序列号", mchSerialNo, "必传"}
  85. }
  86. if mchPrivateKeyPath == "" {
  87. return paramError{"商户私钥路径", mchPrivateKeyPath, "必传"}
  88. }
  89. fileInfo, err := os.Stat(mchPrivateKeyPath)
  90. if err != nil {
  91. return paramError{"商户私钥路径", mchPrivateKeyPath, fmt.Sprintf("有误: %v", err)}
  92. }
  93. if fileInfo.IsDir() {
  94. return paramError{"商户私钥路径", mchPrivateKeyPath, "不是合法的文件路径"}
  95. }
  96. if mchAPIv3Key == "" {
  97. return paramError{"商户APIv3密钥", mchAPIv3Key, "必传"}
  98. }
  99. if wechatPayCertificatePath != "" {
  100. fileInfo, err := os.Stat(wechatPayCertificatePath)
  101. if err != nil {
  102. return paramError{"商户平台证书路径", wechatPayCertificatePath, fmt.Sprintf("有误:%v", err)}
  103. }
  104. if fileInfo.IsDir() {
  105. return paramError{"商户平台证书路径", wechatPayCertificatePath, "不是合法的文件路径"}
  106. }
  107. }
  108. err = os.MkdirAll(outputPath, os.ModePerm)
  109. if err != nil {
  110. return paramError{"证书下载保存目录", outputPath, fmt.Sprintf("创建失败:%v", err)}
  111. }
  112. return nil
  113. }
  114. // revive:enable:cyclomatic
  115. func saveCertificates(ctx context.Context, d *downloader.CertificateDownloader) error {
  116. for serialNo, certContent := range d.ExportAll(ctx) {
  117. outputFilePath := filepath.Join(outputPath, fmt.Sprintf("wechatpay_%v.pem", serialNo))
  118. f, err := os.Create(outputFilePath)
  119. if err != nil {
  120. return fmt.Errorf("创建证书文件`%v`失败:%v", outputFilePath, err)
  121. }
  122. _, err = f.WriteString(certContent + "\n")
  123. if err != nil {
  124. return fmt.Errorf("写入证书到`%v`失败: %v", outputFilePath, err)
  125. }
  126. fmt.Printf("写入证书到`%v`成功\n", outputFilePath)
  127. }
  128. return nil
  129. }
  130. func createClient(ctx context.Context) (*core.Client, error) {
  131. privateKey, err := utils.LoadPrivateKeyWithPath(mchPrivateKeyPath)
  132. if err != nil {
  133. return nil, fmt.Errorf("商户私钥有误:%v", err)
  134. }
  135. var client *core.Client
  136. if wechatPayCertificatePath != "" {
  137. wechatPayCertificate, err := utils.LoadCertificateWithPath(wechatPayCertificatePath)
  138. if err != nil {
  139. return nil, fmt.Errorf("平台证书有误:%v", err)
  140. }
  141. client, err = core.NewClient(
  142. ctx, option.WithMerchantCredential(mchID, mchSerialNo, privateKey),
  143. option.WithWechatPayCertificate([]*x509.Certificate{wechatPayCertificate}),
  144. )
  145. if err != nil {
  146. return nil, fmt.Errorf("创建 Client 失败:%v", err)
  147. }
  148. } else {
  149. client, err = core.NewClient(
  150. ctx, option.WithMerchantCredential(mchID, mchSerialNo, privateKey), option.WithoutValidator(),
  151. )
  152. if err != nil {
  153. return nil, fmt.Errorf("创建 Client 失败:%v", err)
  154. }
  155. }
  156. return client, nil
  157. }