mirror of
				https://github.com/yangjian102621/geekai.git
				synced 2025-11-04 08:13:43 +08:00 
			
		
		
		
	opt: verify the order in notify callback
This commit is contained in:
		@@ -8,6 +8,7 @@
 | 
			
		||||
* 功能新增:新增网站公告,可以在管理后台自定义配置
 | 
			
		||||
* 功能新增:新增阿里通义千问大模型支持
 | 
			
		||||
* Bug修复:修复 MJ 放大任务失败时候 img_call 会增加的 Bug
 | 
			
		||||
* 功能优化:新增虎皮椒和PayJS订单状态校验功能,增加安全性
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## v3.2.5
 | 
			
		||||
 
 | 
			
		||||
@@ -38,16 +38,6 @@ type SmtpConfig struct {
 | 
			
		||||
	Password string // 发件人邮箱密码
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// JPayConfig PayJs 支付配置
 | 
			
		||||
type JPayConfig struct {
 | 
			
		||||
	Enabled    bool
 | 
			
		||||
	Name       string // 支付名称,默认 wechat
 | 
			
		||||
	AppId      string // 商户 ID
 | 
			
		||||
	PrivateKey string // 私钥
 | 
			
		||||
	ApiURL     string // API 网关
 | 
			
		||||
	NotifyURL  string // 异步回调地址
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ChatPlusApiConfig struct {
 | 
			
		||||
	ApiURL string
 | 
			
		||||
	AppId  string
 | 
			
		||||
@@ -105,10 +95,20 @@ type HuPiPayConfig struct { //虎皮椒第四方支付配置
 | 
			
		||||
	Name      string // 支付名称,如:wechat/alipay
 | 
			
		||||
	AppId     string // App ID
 | 
			
		||||
	AppSecret string // app 密钥
 | 
			
		||||
	PayURL    string // 支付网关
 | 
			
		||||
	ApiURL    string // 支付网关
 | 
			
		||||
	NotifyURL string // 异步通知回调
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// JPayConfig PayJs 支付配置
 | 
			
		||||
type JPayConfig struct {
 | 
			
		||||
	Enabled    bool
 | 
			
		||||
	Name       string // 支付名称,默认 wechat
 | 
			
		||||
	AppId      string // 商户 ID
 | 
			
		||||
	PrivateKey string // 私钥
 | 
			
		||||
	ApiURL     string // API 网关
 | 
			
		||||
	NotifyURL  string // 异步回调地址
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type XXLConfig struct { // XXL 任务调度配置
 | 
			
		||||
	Enabled      bool
 | 
			
		||||
	ServerAddr   string
 | 
			
		||||
 
 | 
			
		||||
@@ -101,30 +101,12 @@ func (h *PaymentHandler) DoPay(c *gin.Context) {
 | 
			
		||||
			NotifyURL:    h.App.Config.HuPiPayConfig.NotifyURL,
 | 
			
		||||
			WapName:      "极客学长",
 | 
			
		||||
		}
 | 
			
		||||
		res, err := h.huPiPayService.Pay(params)
 | 
			
		||||
		r, err := h.huPiPayService.Pay(params)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			resp.ERROR(c, "error with generate pay url: "+err.Error())
 | 
			
		||||
			resp.ERROR(c, err.Error())
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var r struct {
 | 
			
		||||
			Openid    interface{} `json:"openid"`
 | 
			
		||||
			UrlQrcode string      `json:"url_qrcode"`
 | 
			
		||||
			URL       string      `json:"url"`
 | 
			
		||||
			ErrCode   int         `json:"errcode"`
 | 
			
		||||
			ErrMsg    string      `json:"errmsg,omitempty"`
 | 
			
		||||
		}
 | 
			
		||||
		err = utils.JsonDecode(res, &r)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logger.Debugf(res)
 | 
			
		||||
			resp.ERROR(c, "error with decode payment result: "+err.Error())
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if r.ErrCode != 0 {
 | 
			
		||||
			resp.ERROR(c, "error with generate pay url: "+r.ErrMsg)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		c.Redirect(302, r.URL)
 | 
			
		||||
	}
 | 
			
		||||
	resp.ERROR(c, "Invalid operations")
 | 
			
		||||
@@ -288,7 +270,7 @@ func (h *PaymentHandler) PayQrcode(c *gin.Context) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 异步通知回调公共逻辑
 | 
			
		||||
func (h *PaymentHandler) notify(orderNo string) error {
 | 
			
		||||
func (h *PaymentHandler) notify(orderNo string, tradeNo string) error {
 | 
			
		||||
	var order model.Order
 | 
			
		||||
	res := h.db.Where("order_no = ?", orderNo).First(&order)
 | 
			
		||||
	if res.Error != nil {
 | 
			
		||||
@@ -329,7 +311,7 @@ func (h *PaymentHandler) notify(orderNo string) error {
 | 
			
		||||
			user.ImgCalls += remark.ImgCalls
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	} else { // 非 VIP 用户
 | 
			
		||||
	} else {                 // 非 VIP 用户
 | 
			
		||||
		if remark.Days > 0 { // vip 套餐:days > 0, calls == 0
 | 
			
		||||
			user.ExpiredTime = time.Now().AddDate(0, 0, remark.Days).Unix()
 | 
			
		||||
			user.Calls += h.App.SysConfig.VipMonthCalls
 | 
			
		||||
@@ -353,6 +335,7 @@ func (h *PaymentHandler) notify(orderNo string) error {
 | 
			
		||||
	// 更新订单状态
 | 
			
		||||
	order.PayTime = time.Now().Unix()
 | 
			
		||||
	order.Status = types.OrderPaidSuccess
 | 
			
		||||
	order.TradeNo = tradeNo
 | 
			
		||||
	res = h.db.Updates(&order)
 | 
			
		||||
	if res.Error != nil {
 | 
			
		||||
		err := fmt.Errorf("error with update order info: %v", res.Error)
 | 
			
		||||
@@ -390,9 +373,14 @@ func (h *PaymentHandler) HuPiPayNotify(c *gin.Context) {
 | 
			
		||||
 | 
			
		||||
	orderNo := c.Request.Form.Get("trade_order_id")
 | 
			
		||||
	logger.Infof("收到订单支付回调,订单 NO:%s", orderNo)
 | 
			
		||||
	// TODO 是否要保存订单交易流水号
 | 
			
		||||
 | 
			
		||||
	err = h.notify(orderNo)
 | 
			
		||||
	tradeNo := c.Request.Form.Get("transaction_id")
 | 
			
		||||
	if err = h.huPiPayService.Check(tradeNo); err != nil {
 | 
			
		||||
		logger.Error("订单校验失败:", err)
 | 
			
		||||
		c.String(http.StatusOK, "fail")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	err = h.notify(orderNo, tradeNo)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.String(http.StatusOK, "fail")
 | 
			
		||||
		return
 | 
			
		||||
@@ -409,16 +397,17 @@ func (h *PaymentHandler) AlipayNotify(c *gin.Context) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// TODO:这里最好用支付宝的公钥签名签证一下交易真假
 | 
			
		||||
	//res := h.alipayService.TradeVerify(c.Request.Form)
 | 
			
		||||
	r := h.alipayService.TradeQuery(c.Request.Form.Get("out_trade_no"))
 | 
			
		||||
	logger.Infof("验证支付结果:%+v", r)
 | 
			
		||||
	if !r.Success() {
 | 
			
		||||
	// TODO:验证交易签名
 | 
			
		||||
	res := h.alipayService.TradeVerify(c.Request.Form)
 | 
			
		||||
	logger.Infof("验证支付结果:%+v", res)
 | 
			
		||||
	if !res.Success() {
 | 
			
		||||
		logger.Error("订单校验失败:", res.Message)
 | 
			
		||||
		c.String(http.StatusOK, "fail")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = h.notify(r.OutTradeNo)
 | 
			
		||||
	tradeNo := c.Request.Form.Get("trade_no")
 | 
			
		||||
	err = h.notify(res.OutTradeNo, tradeNo)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.String(http.StatusOK, "fail")
 | 
			
		||||
		return
 | 
			
		||||
@@ -443,7 +432,16 @@ func (h *PaymentHandler) PayJsNotify(c *gin.Context) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = h.notify(orderNo)
 | 
			
		||||
	// 校验订单支付状态
 | 
			
		||||
	tradeNo := c.Request.Form.Get("payjs_order_id")
 | 
			
		||||
	err = h.js.Check(tradeNo)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Error("订单校验失败:", err)
 | 
			
		||||
		c.String(http.StatusOK, "fail")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = h.notify(orderNo, tradeNo)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.String(http.StatusOK, "fail")
 | 
			
		||||
		return
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ import (
 | 
			
		||||
	"chatplus/core/types"
 | 
			
		||||
	"chatplus/utils"
 | 
			
		||||
	"crypto/md5"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
@@ -17,14 +18,14 @@ import (
 | 
			
		||||
type HuPiPayService struct {
 | 
			
		||||
	appId     string
 | 
			
		||||
	appSecret string
 | 
			
		||||
	host      string
 | 
			
		||||
	apiURL    string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewHuPiPay(config *types.AppConfig) *HuPiPayService {
 | 
			
		||||
	return &HuPiPayService{
 | 
			
		||||
		appId:     config.HuPiPayConfig.AppId,
 | 
			
		||||
		appSecret: config.HuPiPayConfig.AppSecret,
 | 
			
		||||
		host:      config.HuPiPayConfig.PayURL,
 | 
			
		||||
		apiURL:    config.HuPiPayConfig.ApiURL,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -42,8 +43,16 @@ type HuPiPayReq struct {
 | 
			
		||||
	NonceStr     string `json:"nonce_str"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type HuPiResp struct {
 | 
			
		||||
	Openid    interface{} `json:"openid"`
 | 
			
		||||
	UrlQrcode string      `json:"url_qrcode"`
 | 
			
		||||
	URL       string      `json:"url"`
 | 
			
		||||
	ErrCode   int         `json:"errcode"`
 | 
			
		||||
	ErrMsg    string      `json:"errmsg,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Pay 执行支付请求操作
 | 
			
		||||
func (s *HuPiPayService) Pay(params HuPiPayReq) (string, error) {
 | 
			
		||||
func (s *HuPiPayService) Pay(params HuPiPayReq) (HuPiResp, error) {
 | 
			
		||||
	data := url.Values{}
 | 
			
		||||
	simple := strconv.FormatInt(time.Now().Unix(), 10)
 | 
			
		||||
	params.AppId = s.appId
 | 
			
		||||
@@ -58,21 +67,34 @@ func (s *HuPiPayService) Pay(params HuPiPayReq) (string, error) {
 | 
			
		||||
	encode = utils.JsonEncode(params)
 | 
			
		||||
	m = make(map[string]string)
 | 
			
		||||
	_ = utils.JsonDecode(encode, &m)
 | 
			
		||||
	data.Add("hash", s.Sign(m))
 | 
			
		||||
	resp, err := http.PostForm(s.host, data)
 | 
			
		||||
	data.Add("hash", s.sign(m))
 | 
			
		||||
	apiURL := fmt.Sprintf("%s/payment/do.html", s.apiURL)
 | 
			
		||||
	logger.Info(apiURL)
 | 
			
		||||
	resp, err := http.PostForm(apiURL, data)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "error", err
 | 
			
		||||
		return HuPiResp{}, fmt.Errorf("error with requst api: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
	all, err := io.ReadAll(resp.Body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "error", err
 | 
			
		||||
		return HuPiResp{}, fmt.Errorf("error with reading response: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	return string(all), err
 | 
			
		||||
 | 
			
		||||
	var res HuPiResp
 | 
			
		||||
	err = utils.JsonDecode(string(all), &res)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return HuPiResp{}, fmt.Errorf("error with decode payment result: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if res.ErrCode != 0 {
 | 
			
		||||
		return HuPiResp{}, fmt.Errorf("error with generate pay url: %s", res.ErrMsg)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return res, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sign 签名方法
 | 
			
		||||
func (s *HuPiPayService) Sign(params map[string]string) string {
 | 
			
		||||
func (s *HuPiPayService) sign(params map[string]string) string {
 | 
			
		||||
	var data string
 | 
			
		||||
	keys := make([]string, 0, 0)
 | 
			
		||||
	for key := range params {
 | 
			
		||||
@@ -90,3 +112,50 @@ func (s *HuPiPayService) Sign(params map[string]string) string {
 | 
			
		||||
	sign := fmt.Sprintf("%x", m.Sum(nil))
 | 
			
		||||
	return sign
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Check 校验订单状态
 | 
			
		||||
func (s *HuPiPayService) Check(tradeNo string) error {
 | 
			
		||||
	data := url.Values{}
 | 
			
		||||
	data.Add("appid", s.appId)
 | 
			
		||||
	data.Add("open_order_id", tradeNo)
 | 
			
		||||
	stamp := strconv.FormatInt(time.Now().Unix(), 10)
 | 
			
		||||
	data.Add("time", stamp)
 | 
			
		||||
	data.Add("nonce_str", stamp)
 | 
			
		||||
	// 生成签名
 | 
			
		||||
	encode := utils.JsonEncode(data)
 | 
			
		||||
	m := make(map[string]string)
 | 
			
		||||
	err := utils.JsonDecode(encode, &m)
 | 
			
		||||
	data.Add("sign", s.sign(m))
 | 
			
		||||
 | 
			
		||||
	apiURL := fmt.Sprintf("%s/payment/query.html", s.apiURL)
 | 
			
		||||
	resp, err := http.PostForm(apiURL, data)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("error with http reqeust: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
	body, err := io.ReadAll(resp.Body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("error with reading response: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var r struct {
 | 
			
		||||
		ErrCode int `json:"errcode"`
 | 
			
		||||
		Data    struct {
 | 
			
		||||
			Status      string `json:"status"`
 | 
			
		||||
			OpenOrderId string `json:"open_order_id"`
 | 
			
		||||
		} `json:"data,omitempty"`
 | 
			
		||||
		ErrMsg string `json:"errmsg"`
 | 
			
		||||
		Hash   string `json:"hash"`
 | 
			
		||||
	}
 | 
			
		||||
	err = utils.JsonDecode(string(body), &r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("error with decode response: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if r.ErrCode == 0 && r.Data.Status == "OD" {
 | 
			
		||||
		return nil
 | 
			
		||||
	} else {
 | 
			
		||||
		return errors.New("order not paid")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ import (
 | 
			
		||||
	"chatplus/utils"
 | 
			
		||||
	"crypto/md5"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
@@ -55,10 +56,11 @@ func (js *PayJS) Pay(param JPayReq) JPayReps {
 | 
			
		||||
	}
 | 
			
		||||
	p.Add("mchid", js.config.AppId)
 | 
			
		||||
 | 
			
		||||
	p.Add("sign", sign(p, js.config.PrivateKey))
 | 
			
		||||
	p.Add("sign", js.sign(p))
 | 
			
		||||
 | 
			
		||||
	cli := http.Client{}
 | 
			
		||||
	r, err := cli.PostForm(js.config.ApiURL, p)
 | 
			
		||||
	apiURL := fmt.Sprintf("%s/api/native", js.config.ApiURL)
 | 
			
		||||
	r, err := cli.PostForm(apiURL, p)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return JPayReps{ReturnMsg: err.Error()}
 | 
			
		||||
	}
 | 
			
		||||
@@ -76,7 +78,7 @@ func (js *PayJS) Pay(param JPayReq) JPayReps {
 | 
			
		||||
	return data
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func sign(params url.Values, priKey string) string {
 | 
			
		||||
func (js *PayJS) sign(params url.Values) string {
 | 
			
		||||
	params.Del(`sign`)
 | 
			
		||||
	var keys = make([]string, 0, 0)
 | 
			
		||||
	for key := range params {
 | 
			
		||||
@@ -94,9 +96,45 @@ func sign(params url.Values, priKey string) string {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	var src = strings.Join(pList, "&")
 | 
			
		||||
	src += "&key=" + priKey
 | 
			
		||||
	src += "&key=" + js.config.PrivateKey
 | 
			
		||||
 | 
			
		||||
	md5bs := md5.Sum([]byte(src))
 | 
			
		||||
	md5res := hex.EncodeToString(md5bs[:])
 | 
			
		||||
	return strings.ToUpper(md5res)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Check 查询订单支付状态
 | 
			
		||||
// @param tradeNo 支付平台交易 ID
 | 
			
		||||
func (js *PayJS) Check(tradeNo string) error {
 | 
			
		||||
	apiURL := fmt.Sprintf("%s/api/check", js.config.ApiURL)
 | 
			
		||||
	params := url.Values{}
 | 
			
		||||
	params.Add("payjs_order_id", tradeNo)
 | 
			
		||||
	params.Add("sign", js.sign(params))
 | 
			
		||||
	data := strings.NewReader(params.Encode())
 | 
			
		||||
	resp, err := http.Post(apiURL, "application/x-www-form-urlencoded", data)
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("error with http reqeust: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
	body, err := io.ReadAll(resp.Body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("error with reading response: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var r struct {
 | 
			
		||||
		ReturnCode int `json:"return_code"`
 | 
			
		||||
		Status     int `json:"status"`
 | 
			
		||||
	}
 | 
			
		||||
	err = utils.JsonDecode(string(body), &r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("error with decode response: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if r.ReturnCode == 1 && r.Status == 1 {
 | 
			
		||||
		return nil
 | 
			
		||||
	} else {
 | 
			
		||||
		return errors.New("order not paid")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ type Order struct {
 | 
			
		||||
	ProductId uint
 | 
			
		||||
	Username  string
 | 
			
		||||
	OrderNo   string
 | 
			
		||||
	TradeNo   string
 | 
			
		||||
	Subject   string
 | 
			
		||||
	Amount    float64
 | 
			
		||||
	Status    types.OrderStatus
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ type Order struct {
 | 
			
		||||
	ProductId uint              `json:"product_id"`
 | 
			
		||||
	Username  string            `json:"username"`
 | 
			
		||||
	OrderNo   string            `json:"order_no"`
 | 
			
		||||
	TradeNo   string            `json:"trade_no"`
 | 
			
		||||
	Subject   string            `json:"subject"`
 | 
			
		||||
	Amount    float64           `json:"amount"`
 | 
			
		||||
	Status    types.OrderStatus `json:"status"`
 | 
			
		||||
 
 | 
			
		||||
@@ -1,2 +1,4 @@
 | 
			
		||||
ALTER TABLE `chatgpt_mj_jobs` ADD `publish` TINYINT(1) NOT NULL COMMENT '是否发布' AFTER `use_proxy`;
 | 
			
		||||
ALTER TABLE `chatgpt_sd_jobs` ADD `publish` TINYINT(1) NOT NULL COMMENT '是否发布' AFTER `progress`;
 | 
			
		||||
ALTER TABLE `chatgpt_sd_jobs` ADD `publish` TINYINT(1) NOT NULL COMMENT '是否发布' AFTER `progress`;
 | 
			
		||||
 | 
			
		||||
ALTER TABLE `chatgpt_orders` ADD `trade_no` VARCHAR(60) NOT NULL COMMENT '支付平台交易流水号' AFTER `order_no`;
 | 
			
		||||
@@ -101,7 +101,7 @@ WeChatBot = false
 | 
			
		||||
  Name = "wechat"
 | 
			
		||||
  AppId = ""
 | 
			
		||||
  AppSecret = ""
 | 
			
		||||
  PayURL = "https://api.xunhupay.com/payment/do.html"
 | 
			
		||||
  ApiURL = "https://api.xunhupay.com"
 | 
			
		||||
  NotifyURL = "https://ai.r9it.com/api/payment/hupipay/notify"
 | 
			
		||||
 | 
			
		||||
[SmtpConfig] # 注意,阿里云服务器禁用了25号端口,所以如果需要使用邮件功能,请别用阿里云服务器
 | 
			
		||||
@@ -116,5 +116,5 @@ WeChatBot = false
 | 
			
		||||
  Name = "wechat" # 请不要改动
 | 
			
		||||
  AppId = "" # 商户 ID
 | 
			
		||||
  PrivateKey = "" # 秘钥
 | 
			
		||||
  ApiURL = "https://payjs.cn/api/native"
 | 
			
		||||
  ApiURL = "https://payjs.cn"
 | 
			
		||||
  NotifyURL = "https://ai.r9it.com/api/payment/payjs/notify" # 异步回调地址,域名改成你自己的
 | 
			
		||||
		Reference in New Issue
	
	Block a user