1、新增微信退款通知(迪为)接口;
2、商城订单明慧帐户收费改为微信帐户收费; 3、新增微信退款接口(暂时未使用);
This commit is contained in:
parent
f11bf3409c
commit
e1cb2b7662
|
@ -3382,9 +3382,9 @@ type WxPayRefundPlaintext struct {
|
|||
UserReceivedAccount string `json:"user_received_account"`
|
||||
}
|
||||
|
||||
// 0 元购 微信推送支付通知
|
||||
// PushWXPayRefundNotice 微信退款消息通知(明慧)
|
||||
func PushWXPayRefundNotice(c *gin.Context) {
|
||||
fmt.Println("微信推送支付通知")
|
||||
fmt.Println("微信推送退款通知")
|
||||
//body, err := ioutil.ReadAll(c.Request.Body)
|
||||
//if err != nil {
|
||||
// logger.Error(err)
|
||||
|
@ -3393,11 +3393,103 @@ func PushWXPayRefundNotice(c *gin.Context) {
|
|||
mchID := "1609877389"
|
||||
mchAPIv3Key := "DeovoMingHuiRengTianTang45675123" // 商户APIv3密钥
|
||||
mchCertificateSerialNumber := "7540301D8FD52CCF7D6267DCF7CD2BC0AB467EFF" // 商户证书序列号
|
||||
|
||||
mchPrivateKey, err := wechatpayutils.LoadPrivateKeyWithPath("./configs/merchant/apiclient_key.pem")
|
||||
if err != nil {
|
||||
log.Print("load merchant private key error")
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
// 1. 使用 `RegisterDownloaderWithPrivateKey` 注册下载器
|
||||
err = downloader.MgrInstance().RegisterDownloaderWithPrivateKey(ctx, mchPrivateKey, mchCertificateSerialNumber, mchID, mchAPIv3Key)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
// 2. 获取商户号对应的微信支付平台证书访问器
|
||||
certVisitor := downloader.MgrInstance().GetCertificateVisitor(mchID)
|
||||
// 3. 使用证书访问器初始化 `notify.Handler`
|
||||
handler := notify.NewNotifyHandler(mchAPIv3Key, verifiers.NewSHA256WithRSAVerifier(certVisitor))
|
||||
|
||||
transaction := new(payments.Transaction)
|
||||
notifyReq, err := handler.ParseNotifyRequest(context.Background(), c.Request, transaction)
|
||||
// 如果验签未通过,或者解密失败
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
// 处理通知内容
|
||||
//fmt.Println(notifyReq.Summary)
|
||||
//fmt.Println(transaction.TransactionId)
|
||||
|
||||
//transactionJson, _ := json.Marshal(transaction)
|
||||
//fmt.Println("transactionJson:", string(transactionJson))
|
||||
//notifyReqJson, _ := json.Marshal(notifyReq)
|
||||
//fmt.Println("notifyReqJson:", string(notifyReqJson))
|
||||
if notifyReq.EventType == "REFUND.SUCCESS" {
|
||||
plaintext := new(WxPayRefundPlaintext)
|
||||
err = json.Unmarshal([]byte(notifyReq.Resource.Plaintext), plaintext)
|
||||
if err != nil {
|
||||
logger.Error("unmarshal plaintext err:", err)
|
||||
return
|
||||
}
|
||||
count, err := model.NewFundRecordQuerySet(model.DB).RefundIdEq(plaintext.RefundId).Count()
|
||||
if err != nil {
|
||||
logger.Error("count refund id err:", err)
|
||||
return
|
||||
}
|
||||
plaintextJson, _ := json.Marshal(plaintext)
|
||||
fmt.Println("plaintextJson:", string(plaintextJson))
|
||||
|
||||
if count == 0 {
|
||||
openMemberRecord := new(model.UserOpenMemberRecord)
|
||||
err = model.NewUserOpenMemberRecordQuerySet(model.DB).OpenNoEq(plaintext.OutRefundNo).One(openMemberRecord)
|
||||
if err != nil {
|
||||
logger.Error("user open member record err:", err)
|
||||
return
|
||||
}
|
||||
//fundType := model.FundTypeExpressFeeRefund
|
||||
//if openMemberRecord.OrderType == 6 {
|
||||
// fundType = model.FundTypeBuyGoodsRefund
|
||||
//}
|
||||
fundRecord := &model.FundRecord{
|
||||
Uid: openMemberRecord.Uid,
|
||||
FundType: GetFundRecordFundType(openMemberRecord.OrderType),
|
||||
Amount: int64(plaintext.Amount.Refund) * (-1),
|
||||
TransactionId: plaintext.TransactionId,
|
||||
OutTradeNo: plaintext.OutTradeNo,
|
||||
RefundId: plaintext.RefundId,
|
||||
Status: 2,
|
||||
Remark: GetFundRecordRemark(openMemberRecord.OrderType),
|
||||
}
|
||||
err = model.DB.Create(fundRecord).Error
|
||||
if err != nil {
|
||||
logger.Error("create fund record err:", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RespNotice(c, "SUCCESS", "成功")
|
||||
return
|
||||
//logger.Error("xml Request.Body1:", string(body))
|
||||
}
|
||||
|
||||
// DwPushWXPayRefundNotice 微信退款消息通知(迪为)
|
||||
func DwPushWXPayRefundNotice(c *gin.Context) {
|
||||
fmt.Println("微信推送退款通知")
|
||||
//body, err := ioutil.ReadAll(c.Request.Body)
|
||||
//if err != nil {
|
||||
// logger.Error(err)
|
||||
//}
|
||||
|
||||
mchID := "1494954322"
|
||||
mchAPIv3Key := "hTCTqF9jHsWlFOO8ZuL05BDo2UlrJwVv" // 商户APIv3密钥
|
||||
mchCertificateSerialNumber := "5D98B7F99C24BFD8649E2045635AFBBCDD5B29C1" // 商户证书序列号
|
||||
mchPrivateKey, err := wechatpayutils.LoadPrivateKeyWithPath("./configs/dw_merchant/apiclient_key.pem")
|
||||
if err != nil {
|
||||
log.Print("load merchant private key error")
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
// 1. 使用 `RegisterDownloaderWithPrivateKey` 注册下载器
|
||||
err = downloader.MgrInstance().RegisterDownloaderWithPrivateKey(ctx, mchPrivateKey, mchCertificateSerialNumber, mchID, mchAPIv3Key)
|
||||
|
|
|
@ -481,7 +481,7 @@ func MallOrderCreate(c *gin.Context) {
|
|||
|
||||
RespOK(c, ret)
|
||||
} else { // 其他则默认明慧账户收费
|
||||
webPay, err := wxpay.HmJsPayUnifiedOrderForBuyGoods(order.SerialNo, order.Rm, user.WxOpenID, configInfo.NotifyUrl)
|
||||
webPay, err := wxpay.WebPay(order.SerialNo, order.Rm, user.WxOpenID, "N", wxpay.WxPayBuyGoods, configInfo.NotifyUrl, false)
|
||||
if err != nil {
|
||||
logger.Error(errors.New("WebPay err"))
|
||||
RespJson(c, status.InternalServerError, nil)
|
||||
|
@ -495,6 +495,21 @@ func MallOrderCreate(c *gin.Context) {
|
|||
}
|
||||
|
||||
RespOK(c, ret)
|
||||
|
||||
//webPay, err := wxpay.HmJsPayUnifiedOrderForBuyGoods(order.SerialNo, order.Rm, user.WxOpenID, configInfo.NotifyUrl)
|
||||
//if err != nil {
|
||||
// logger.Error(errors.New("WebPay err"))
|
||||
// RespJson(c, status.InternalServerError, nil)
|
||||
// return
|
||||
//}
|
||||
//
|
||||
//ret := map[string]interface{}{
|
||||
// "web_pay": webPay,
|
||||
// "order_id": order.ID,
|
||||
// "order": order,
|
||||
//}
|
||||
//
|
||||
//RespOK(c, ret)
|
||||
}
|
||||
|
||||
return
|
||||
|
@ -643,6 +658,7 @@ func MallOrderRefund(c *gin.Context) {
|
|||
RespJson(c, status.InternalServerError, nil)
|
||||
return
|
||||
}
|
||||
|
||||
if goodsOrder.Amount == 0 && goodsOrder.DeliverStoreId == 0 { // 新机预售券
|
||||
// 查询新机预售券状态,只有未使用才能退
|
||||
var newMachineCoupon model.UserCoupon
|
||||
|
@ -697,6 +713,7 @@ func MallOrderRefund(c *gin.Context) {
|
|||
RespJson(c, status.InternalServerError, nil)
|
||||
return
|
||||
}
|
||||
|
||||
RespOK(c, goodsOrder)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ import (
|
|||
mathrand "math/rand"
|
||||
"mh-server/config"
|
||||
"mh-server/lib/utils"
|
||||
|
||||
"net/http"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
@ -70,7 +69,7 @@ const (
|
|||
TestHmPubKeySwitchPubFp = "/Users/max/Documents/code/deovo/mh_server/pack/configs/hm_pay/switch_pub_private_key.pem"
|
||||
)
|
||||
|
||||
// web 微信支付
|
||||
// WebPay web 微信支付
|
||||
func WebPay(orderId string, totalFee uint32, openId, profitSharing, attach, notifyUrl string, flag bool) (*Sextuple, error) {
|
||||
now := time.Now().Local()
|
||||
strTime := fmt.Sprintf("%04d%02d%02d%02d%02d%02d", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
|
||||
|
@ -176,9 +175,69 @@ func WebPay(orderId string, totalFee uint32, openId, profitSharing, attach, noti
|
|||
return &sextuple, nil
|
||||
}
|
||||
|
||||
// WechatRefund 微信退款
|
||||
func WechatRefund(orderId, refundId string, totalFee uint32, attach, notifyUrl string, flag bool) (*RefundResp, error) {
|
||||
if notifyUrl == "" {
|
||||
logger.Error("NotifyUrl is null")
|
||||
return nil, errors.New("NotifyUrl is null")
|
||||
}
|
||||
|
||||
fmt.Println("MchId:", config.AppConfig.WxMchID)
|
||||
fmt.Println("AppId:", config.AppConfig.WxAppId)
|
||||
fmt.Println("MchSecret:", config.AppConfig.WxMchSecret)
|
||||
|
||||
nonce := utils.GenRandStr(NonceStringLength)
|
||||
refundReq := RefundReq{
|
||||
AppId: config.AppConfig.WxAppId,
|
||||
MchId: config.AppConfig.WxMchID,
|
||||
NonceStr: nonce,
|
||||
Sign: "",
|
||||
SignType: "MD5",
|
||||
OutTradeNo: orderId,
|
||||
OutRefundNo: refundId,
|
||||
TotalFee: int(totalFee),
|
||||
RefundFee: int(totalFee),
|
||||
RefundFeeType: "CNY",
|
||||
RefundDesc: attach,
|
||||
NotifyUrl: notifyUrl,
|
||||
}
|
||||
if flag {
|
||||
refundReq.MchId = config.AppConfig.WxDwMchID
|
||||
}
|
||||
|
||||
fmt.Println("OutTradeNo:", refundReq.OutTradeNo)
|
||||
|
||||
m, err := struct2Map(refundReq)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
payKey := config.AppConfig.WxMchSecret
|
||||
if flag {
|
||||
payKey = config.AppConfig.WxDwMchSecret
|
||||
}
|
||||
|
||||
sign, err := GenWxPaySign(m, payKey)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
refundReq.Sign = strings.ToUpper(sign)
|
||||
|
||||
refundResp, err := WxRefund(refundReq)
|
||||
if err != nil {
|
||||
logger.Errorf("WxUnifiedOrder unified order error %#v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &refundResp, nil
|
||||
}
|
||||
|
||||
const (
|
||||
NonceStringLength = 32
|
||||
UnifiedOrderUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder"
|
||||
RefundUrl = "https://api.mch.weixin.qq.com/secapi/pay/refund"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -312,6 +371,57 @@ type (
|
|||
Timestamp string `json:"timestamp,omitempty"`
|
||||
Sign string `json:"sign,omitempty"`
|
||||
}
|
||||
|
||||
RefundReq struct {
|
||||
AppId string `xml:"appid" json:"appid"` //微信分配的小程序ID,必须
|
||||
MchId string `xml:"mch_id" json:"mch_id"` //微信支付分配的商户号,必须
|
||||
NonceStr string `xml:"nonce_str" json:"nonce_str"` //随机字符串,必须
|
||||
Sign string `xml:"sign" json:"sign"` //签名,必须
|
||||
SignType string `xml:"sign_type" json:"sign_type"` //"HMAC-SHA256"或者"MD5",非必须,默认MD5
|
||||
TransactionId string `xml:"transaction_id" json:"transaction_id"` //微信支付订单号
|
||||
OutTradeNo string `xml:"out_trade_no" json:"out_trade_no"` //商户系统内部订单号,transaction_id、out_trade_no二选一
|
||||
OutRefundNo string `xml:"out_refund_no" json:"out_refund_no"` //商户退款单号,必须
|
||||
TotalFee int `xml:"total_fee" json:"total_fee"` //订单金额,单位分,必须
|
||||
RefundFee int `xml:"refund_fee" json:"refund_fee"` //退款金额,单位分,必须
|
||||
RefundFeeType string `xml:"refund_fee_type" json:"refund_fee_type"` //退款货币种类,非必须
|
||||
RefundDesc string `xml:"refund_desc" json:"refund_desc"` //退款原因,非必须
|
||||
RefundAccount string `xml:"refund_account" json:"refund_account"` //退款资金来源,非必须
|
||||
NotifyUrl string `xml:"notify_url" json:"notify_url"` //退款结果通知url,非必须
|
||||
}
|
||||
|
||||
RefundResp struct {
|
||||
ReturnCode string `xml:"return_code"`
|
||||
ReturnMsg string `xml:"return_msg"`
|
||||
ResultCode string `xml:"result_code"`
|
||||
ErrCode string `xml:"err_code"`
|
||||
ErrCodeDes string `xml:"err_code_des"`
|
||||
AppId string `xml:"appid"` //微信分配的小程序ID,必须
|
||||
MchId string `xml:"mch_id"` //微信支付分配的商户号,必须
|
||||
NonceStr string `xml:"nonce_str"` //随机字符串,必须
|
||||
Sign string `xml:"sign"` //签名,必须
|
||||
TransactionId string `xml:"transaction_id"` //微信支付订单号,必须
|
||||
OutTradeNo string `xml:"out_trade_no"` //商户订单号,必须
|
||||
OutRefundNo string `xml:"out_refund_no"` //商户退款单号,必须
|
||||
RefundId string `xml:"refund_id"` //微信退款单号,必须
|
||||
RefundFee int `xml:"refund_fee"` //退款金额,单位分,必须
|
||||
SettlementRefundFee int `xml:"settlement_refund_fee"` //应结退款金额,非必须
|
||||
TotalFee int `xml:"total_fee"` //标价金额,单位分,必须
|
||||
SettlementTotalFee int `xml:"settlement_total_fee"` //应结订单金额,非必须
|
||||
FeeType string `xml:"fee_type,omitempty"` // 标价币种
|
||||
CashFee int `xml:"cash_fee"` // 现金支付金额
|
||||
CashFeeType string `xml:"cash_fee_type,omitempty"` // 现金支付币种
|
||||
CashRefundFee int `xml:"cash_refund_fee,omitempty"` // 现金退款金额
|
||||
CouponRefundFee int `xml:"coupon_refund_fee,omitempty"` // 代金券退款总金额
|
||||
CouponRefundCount int `xml:"coupon_refund_count,omitempty"` // 退款代金券使用数量
|
||||
Coupons []RefundCoupon `xml:"coupons,omitempty"` // 代金券明细
|
||||
}
|
||||
|
||||
// RefundCoupon 表示单个代金券的退款明细(可多个)
|
||||
RefundCoupon struct {
|
||||
CouponType string `xml:"coupon_type"` // 代金券类型:CASH/NO_CASH
|
||||
CouponID string `xml:"coupon_refund_id"` // 退款代金券ID
|
||||
CouponAmount int `xml:"coupon_refund_fee"` // 单个代金券退款金额
|
||||
}
|
||||
)
|
||||
|
||||
type T struct {
|
||||
|
@ -543,6 +653,51 @@ func WxUnifiedOrder(r UnifiedOrderReq) (UnifiedOrderResp, error) {
|
|||
return payResp, nil
|
||||
}
|
||||
|
||||
func WxRefund(r RefundReq) (RefundResp, error) {
|
||||
var rResp RefundResp
|
||||
|
||||
data, err := xml.Marshal(r)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return rResp, err
|
||||
}
|
||||
|
||||
logger.Error("xml:", string(data))
|
||||
client := http.Client{}
|
||||
req, err := http.NewRequest("POST", RefundUrl, bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return rResp, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/xml; charset=utf-8")
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return rResp, err
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return rResp, err
|
||||
}
|
||||
fmt.Println("body:", string(body))
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
err = xml.Unmarshal(body, &rResp)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return rResp, err
|
||||
}
|
||||
|
||||
if rResp.ReturnCode != "SUCCESS" {
|
||||
return rResp, errors.New(rResp.ReturnMsg)
|
||||
}
|
||||
|
||||
return rResp, nil
|
||||
}
|
||||
|
||||
func WxPayTransactionOrderClose(outTradeNo, mchid string) error {
|
||||
// url := fmt.Sprintf("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/%s/close", outTradeNo)
|
||||
// para := map[string]interface{}{
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/codinl/go-logger"
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/rs/zerolog/log"
|
||||
"math/rand"
|
||||
"mh-server/lib/utils"
|
||||
"mh-server/lib/wxpay"
|
||||
"sort"
|
||||
|
@ -884,6 +885,23 @@ func (m *UserInviteListReq) InviteUserList() (*UserInviteListResp, error) {
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
// GenerateRefundOrderNo 生成退款订单号(例如:R202505271545301234567890)
|
||||
func GenerateRefundOrderNo() string {
|
||||
// 设置随机种子
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
// 当前时间:年月日时分秒(14位)
|
||||
timestamp := time.Now().Format("20060102150405")
|
||||
|
||||
// 生成 10 位随机数:范围是 1000000000 ~ 9999999999
|
||||
randomNum := rand.Int63n(9000000000) + 1000000000 // int63 支持更大范围
|
||||
|
||||
// 拼接最终退款订单号
|
||||
refundOrderNo := fmt.Sprintf("R%s%d", timestamp, randomNum)
|
||||
|
||||
return refundOrderNo
|
||||
}
|
||||
|
||||
func GetOrderSn() string {
|
||||
var orderSn string
|
||||
for {
|
||||
|
|
|
@ -34,7 +34,8 @@ func ConfigAppRouter(r gin.IRouter) {
|
|||
// TODO两边都改
|
||||
api.GET("wxpay/notice", controller.HmPushWXPayNotice) // 河马付推送支付通知
|
||||
api.POST("wxpay/notice", controller.PushWXPayNotice) // 微信推送支付通知
|
||||
api.POST("wxpay_refund/notice", controller.PushWXPayRefundNotice) // 微信推送支付退款通知
|
||||
api.POST("wxpay_refund/notice", controller.PushWXPayRefundNotice) // 微信推送支付退款通知(明慧)
|
||||
api.POST("wxpay_refund/dw/notice", controller.DwPushWXPayRefundNotice) // 微信推送支付退款通知(迪为)
|
||||
api.POST("aliyun/sts_token", controller.AliyunStsTokenGet) // 阿里云上传图片token
|
||||
api.POST("auto_reply/focus", controller.AutoReplyFocusMsg) // 自动回复
|
||||
api.GET("auto_reply/focus", controller.CustomerServiceMessageCheck) // 客服校验
|
||||
|
|
Loading…
Reference in New Issue
Block a user