1、数据库支付配置项增加dw_refund_notify_url;

2、优化退货接收接口,退款时判断帐户(明慧/迪为);
3、新增迪为帐户证书;
This commit is contained in:
chenlin 2025-05-28 16:58:46 +08:00
parent e1a7d9cc91
commit bbbc61f857
7 changed files with 134 additions and 47 deletions

View File

@ -2,6 +2,7 @@ package mallmanage
import (
"errors"
"fmt"
"github.com/codinl/go-logger"
"github.com/gin-gonic/gin"
"github.com/rs/zerolog/log"
@ -136,8 +137,8 @@ func GoodsOrderRefundSendReceive(c *gin.Context) {
err = req.Receive()
if err != nil {
logger.Errorf("err:%#v", err)
msg := "订单发货失败"
app.Error(c, http.StatusInternalServerError, errors.New("order detail err"), msg)
app.Error(c, http.StatusInternalServerError, errors.New("order detail err"),
fmt.Sprintf("退货失败:%s", err.Error()))
return
}

View File

@ -111,19 +111,17 @@ const (
HmPubKeyJBLPub = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDIjqYgPO1kj3NI0WEfOCnKYUHu4EkARnbiJ2FKosajpP8eceaL1u4JOelNG+RN7cldvmWEtefZCPNOHAHddQLfEnRZ3xyzdRdV0A3vXykyY6UMWgRlPnHOslAm8OUpOWubDzQTmfr88R38EUrHG4HYvRVmQb/s/LQjsuS863vSbwIDAQAB"
HmPubKeyJBLPubFp = "./config/hm_pay/jbl_pub_private_key.pem"
TestHmPubKeyJBLPubFp = "/Users/max/Documents/code/deovo/mh_goadmin_server/config/hm_pay/jbl_pub_private_key.pem"
)
const (
mchID string = "1609877389" // 商户号
mchCertificateSerialNumber string = "7540301D8FD52CCF7D6267DCF7CD2BC0AB467EFF" // 商户证书序列号
mchAPIv3Key string = "DeovoMingHuiRengTianTang45675123" // 商户APIv3密钥
)
// mchID 小程序-明慧帐户
mchID = "1609877389" // 商户号
mchCertificateSerialNumber = "7540301D8FD52CCF7D6267DCF7CD2BC0AB467EFF" // 商户证书序列号
mchAPIv3Key = "DeovoMingHuiRengTianTang45675123" // 商户APIv3密钥
//var (
// mchID string = "1609877389" // 商户号
// mchCertificateSerialNumber string = "7540301D8FD52CCF7D6267DCF7CD2BC0AB467EFF" // 商户证书序列号
// mchAPIv3Key string = "DeovoMingHuiRengTianTang45675123" // 商户APIv3密钥
//)
// MchIDDw 小程序-迪为帐户
MchIDDw = "1494954322" // 商户号
MchCertificateSerialNumberDw = "5D98B7F99C24BFD8649E2045635AFBBCDD5B29C1" // 商户证书序列号
MchAPIv3KeyDw = "hTCTqF9jHsWlFOO8ZuL05BDo2UlrJwVv" // 商户APIv3密钥
)
type WxTransferResp struct {
ReturnCode string `xml:"return_code,CDATA"`
@ -464,17 +462,22 @@ type OrderRefundAmount struct {
Currency string `json:"currency"`
}
func TransactionOrderRefund(orderRefund OrderRefund) error {
var (
mchID string = "1609877389" // 商户号
mchCertificateSerialNumber string = "7540301D8FD52CCF7D6267DCF7CD2BC0AB467EFF" // 商户证书序列号
mchAPIv3Key string = "DeovoMingHuiRengTianTang45675123" // 商户APIv3密钥
)
func TransactionOrderRefund(orderRefund OrderRefund, accountNum uint32) error {
var cMchID, cMchCertificateSerialNumber, cMchAPIv3Key, privatePath string
if accountNum == 2 { // 迪为帐户
cMchID = MchIDDw
cMchCertificateSerialNumber = MchCertificateSerialNumberDw
cMchAPIv3Key = MchAPIv3KeyDw
privatePath = "./config/dw_merchant/apiclient_key.pem"
} else { // 明慧帐户
cMchID = mchID
cMchCertificateSerialNumber = mchCertificateSerialNumber
cMchAPIv3Key = mchAPIv3Key
privatePath = "./config/merchant/apiclient_key.pem"
}
// WxKeyFile = "./config/merchant/apiclient_key.pem"
// mchPrivateKey, err := wechatpayutils.LoadPrivateKeyWithPath("/Users/li/mh/mh_admin_server/app/admin/apis/pay/merchant/apiclient_key.pem")
privatePath := "./config/merchant/apiclient_key.pem"
//WxKeyFile = "./config/merchant/apiclient_key.pem"
//mchPrivateKey, err := wechatpayutils.LoadPrivateKeyWithPath("/Users/li/mh/mh_admin_server/app/admin/apis/pay/merchant/apiclient_key.pem")
//privatePathTest := "/www/wwwroot/dev.admin.deovo.com/admin_server/go-admin/config/merchant/apiclient_key.pem" // TODO 测试
//privatePath := "/www/wwwroot/admin.deovo.com/admin_server/go-admin/config/merchant/apiclient_key.pem" // TODO 正式
@ -490,7 +493,7 @@ func TransactionOrderRefund(orderRefund OrderRefund) error {
ctx := context.Background()
// 使用商户私钥等初始化 client并使它具有自动定时获取微信支付平台证书的能力
opts := []core.ClientOption{
option.WithWechatPayAutoAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchAPIv3Key),
option.WithWechatPayAutoAuthCipher(cMchID, cMchCertificateSerialNumber, mchPrivateKey, cMchAPIv3Key),
}
client, err := core.NewClient(ctx, opts...)
if err != nil {
@ -518,6 +521,7 @@ func TransactionOrderRefund(orderRefund OrderRefund) error {
if err != nil {
// 处理错误
log.Printf("call Create err:%s", err)
return err
} else {
// 处理返回结果
log.Printf("status=%d resp=%s", result.Response.StatusCode, resp)

View File

@ -30,10 +30,11 @@ func (m *Config) TableName() string {
}
type PayConfig struct {
MemberFee uint32 `json:"member_fee"`
DepositFee uint32 `json:"deposit_fee"`
NotifyUrl string `json:"notify_url"`
RefundNotifyUrl string `json:"refund_notify_url"`
MemberFee uint32 `json:"member_fee"`
DepositFee uint32 `json:"deposit_fee"`
NotifyUrl string `json:"notify_url"`
RefundNotifyUrl string `json:"refund_notify_url"`
DwRefundNotifyUrl string `json:"dw_refund_notify_url"`
}
const (

View File

@ -72,7 +72,7 @@ type Goods struct {
PriceRm uint32 `json:"price_rm"` // 人民币价格
//Stock uint32 `json:"stock"` // 库存
ShowDiscount int8 `json:"show_discount"` // 是否展示折扣价
GoodsAccountNum uint32 `json:"goods_account_num"` // 收款账户编号
GoodsAccountNum uint32 `json:"goods_account_num"` // 收款账户编号 1 明慧2 迪为
GoodsCat *GoodsCat `json:"goods_cat" gorm:"-"`
GoodsDiscount *GoodsDiscount `json:"goods_discount" gorm:"-"`
@ -1316,13 +1316,20 @@ func (r *GoodsOrderRefundSendReceiveReq) Receive() error {
log.Error().Msgf("goods order err:%#v", err)
return err
}
//combo, err := GetCombo(goodsOrder.GoodsAttributeComboId)
//if err != nil {
// logger.Errorf("get err:",)
//}
if goodsOrder.State == GoodsOrderStateRefundedCancel {
logger.Errorf("state err")
return errors.New("state err")
switch goodsOrder.State {
case GoodsOrderStateUnPay: //待付款
logger.Errorf("退货失败:订单未付款")
return errors.New("订单未付款")
case GoodsOrderStateDelivered: //已发货
logger.Errorf("退货失败:订单已发货,需用户接收后发起退货")
return errors.New("订单已发货,需用户接收后发起退货")
case GoodsOrderStateRefundedCancel: // 已取消
logger.Errorf("退货失败:订单已取消")
return errors.New("订单已取消")
case GoodsOrderStateRefunded: // 已退货
logger.Errorf("退货失败:订单已退货,请勿重复操作")
return errors.New("订单已退货,请勿重复操作")
}
begin := orm.Eloquent.Begin()
@ -1346,26 +1353,44 @@ func (r *GoodsOrderRefundSendReceiveReq) Receive() error {
log.Error().Msgf("express fee refund record err:%#v", err)
return err
}
memberRecord := &UserOpenMemberRecord{OpenNo: GetOrderSn(), OrderType: 8, GoodsOrder: &goodsOrder}
err = memberRecord.MallGoodsOrderRefund(outTradeNo)
if err != nil {
log.Error().Msgf("order refund err:%#v", err)
return err
if outTradeNo == "" {
log.Error().Msgf("未查询到商户订单号")
return errors.New("未查询到商户订单号")
}
memberRecord := &UserOpenMemberRecord{OpenNo: GetOrderSn(), OrderType: 8, GoodsOrder: &goodsOrder}
// 判断收款帐户goods.GoodsAccountNum1 明慧 2迪为
var goods Goods
err = orm.Eloquent.Table("goods").Where("goods_id=?", goodsOrder.GoodsId).Find(&goods).Error
if err != nil || goods.ID == 0 {
log.Error().Msgf("get goods err:%#v", err)
return errors.New("未查询到商品信息")
}
if goodsOrder.Rm != 0 { // 如果有支付,则走退款流程
err = memberRecord.MallGoodsOrderRefund(outTradeNo, goods.GoodsAccountNum)
if err != nil {
log.Error().Msgf("order refund err:%#v", err)
return err
}
}
// 更新订单为"已退货"状态
err = begin.Table("goods_order").Where("order_id=?", r.OrderId).Update("state", GoodsOrderStateRefunded).Error
if err != nil {
begin.Rollback()
log.Error().Msgf("update state err:%#v", err)
return err
}
// 更新库存
err = OrderUpdateGoodsStockBack(goodsAttribute.ID, goodsOrder.Quantity, begin)
if err != nil {
begin.Rollback()
log.Error().Msgf("goods stock back err:%#v", err)
return err
}
if goodsOrder.Amount == 0 { // 目前只有主机预售券可以退款;普通的积分兑换不支持退货
if goodsOrder.Vm == 0 && strings.Contains(goods.Name, "预售") { // 目前只有主机预售券可以退款;普通的积分兑换不支持退货
err = begin.Table("user_coupon").Where("uid = ? and activity_id = ?", goodsOrder.Uid, 10).
Updates(map[string]interface{}{
"state": 4,

View File

@ -3427,7 +3427,7 @@ func (o *UserOpenMemberRecord) TableName() string {
return "user_open_member_record"
}
func (m *UserOpenMemberRecord) Refund(outTradeNo string, amount uint32) error {
func (m *UserOpenMemberRecord) Refund(outTradeNo string, amount, accountNum uint32) error {
configInfo, err := PayConfigInfo()
if err != nil {
logger.Error("config info err:", logger.Field("err", err))
@ -3451,7 +3451,10 @@ func (m *UserOpenMemberRecord) Refund(outTradeNo string, amount uint32) error {
Currency: "CNY",
},
}
err = pay.TransactionOrderRefund(orderRefund)
if accountNum == 2 { // 迪为帐户
orderRefund.NotifyUrl = configInfo.DwRefundNotifyUrl
}
err = pay.TransactionOrderRefund(orderRefund, accountNum)
if err != nil {
logger.Error("err:", logger.Field("err", err))
return err
@ -3460,17 +3463,17 @@ func (m *UserOpenMemberRecord) Refund(outTradeNo string, amount uint32) error {
return nil
}
func (m *UserOpenMemberRecord) MallGoodsOrderRefund(outTradeNo string) error {
func (m *UserOpenMemberRecord) MallGoodsOrderRefund(outTradeNo string, accountNum uint32) error {
if m.GoodsOrder == nil {
return errors.New("goods order is nil")
}
if m.OpenNo == "" {
m.OpenNo = GetOrderSn()
}
m.Uid = uint32(m.GoodsOrder.Uid)
m.Uid = m.GoodsOrder.Uid
m.OrderId = m.GoodsOrder.ID
err := m.Refund(outTradeNo, m.GoodsOrder.Rm)
err := m.Refund(outTradeNo, m.GoodsOrder.Rm, accountNum)
if err != nil {
logger.Error("refund err:", logger.Field("err", err))
return err

View File

@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIEJDCCAwygAwIBAgIUXZi3+Zwkv9hkniBFY1r7vN1bKcEwDQYJKoZIhvcNAQEL
BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
Q0EwHhcNMjUwNTI4MDYxODE2WhcNMzAwNTI3MDYxODE2WjB+MRMwEQYDVQQDDAox
NDk0OTU0MzIyMRswGQYDVQQKDBLlvq7kv6HllYbmiLfns7vnu58xKjAoBgNVBAsM
Iea3seWcs+W4gui/quS4uumAmuS/oeaciemZkOWFrOWPuDELMAkGA1UEBhMCQ04x
ETAPBgNVBAcMCFNoZW5aaGVuMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEA9HzVvnJvm+LXPJmx3uo/rlV6OwY0ovw6nd2g1aqZyVXSenW99uO2oDFhMeBk
o3khFPviOeF73vQQSDPaK8UMnj6Z7e3NXlyjd8g5HVmp8gzcNMArVvdlVPECquYc
mIhYQND5r3Hk1gV+yezUxYcU4Y9Tvoql6ZVV7UAI3Nsm7XbijBdhfJQid1U+DJc1
iaLiXhYd+6iHAhl7jc9+CgTjOetPgyeHTtge8haDz6yYyrR+5QqsL4QIvgrGFNFz
U1pKNtVyXkKa2c92NdkB7Gx2Inn6RC19kOs1E4RAvrSbt7QcvhFT1jpzitTZxHDJ
6XeIrqi530lXzNyX1YCltlzZjQIDAQABo4G5MIG2MAkGA1UdEwQCMAAwCwYDVR0P
BAQDAgP4MIGbBgNVHR8EgZMwgZAwgY2ggYqggYeGgYRodHRwOi8vZXZjYS5pdHJ1
cy5jb20uY24vcHVibGljL2l0cnVzY3JsP0NBPTFCRDQyMjBFNTBEQkMwNEIwNkFE
Mzk3NTQ5ODQ2QzAxQzNFOEVCRDImc2c9SEFDQzQ3MUI2NTQyMkUxMkIyN0E5RDMz
QTg3QUQxQ0RGNTkyNkUxNDAzNzEwDQYJKoZIhvcNAQELBQADggEBAJHNMqHju2Um
IG3a5CRgjkyMm17Tli4oH03Y1eVuhYjudmjb20fk/KQwDK7rJSbmAfpVb+fmyYYd
fZpz1xqGMeF8SDfp3hhsuKRXv0t0Hhe9iWqs7vbCUUcpp7uC0qApc7tOuPhZUt0z
6UMTQakQRUG8aWJmZAotpjNGA4hNmgLt2ousmjkUsdgl1OYphlrO0rBaIBlT8TzZ
OfIyuDKKNoTs3nJJfsunjrJCfvadcjeWp/iUIHLcoxVNQx/OVf13xBcvFUilSiqI
IX+bqiyBev53atYLcxguKucPsYAuolCBQgGY1V5jCk7YiDTA93o22slg97D9rHTz
IiQqBwHM5VY=
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQD0fNW+cm+b4tc8
mbHe6j+uVXo7BjSi/Dqd3aDVqpnJVdJ6db3247agMWEx4GSjeSEU++I54Xve9BBI
M9orxQyePpnt7c1eXKN3yDkdWanyDNw0wCtW92VU8QKq5hyYiFhA0PmvceTWBX7J
7NTFhxThj1O+iqXplVXtQAjc2ybtduKMF2F8lCJ3VT4MlzWJouJeFh37qIcCGXuN
z34KBOM560+DJ4dO2B7yFoPPrJjKtH7lCqwvhAi+CsYU0XNTWko21XJeQprZz3Y1
2QHsbHYiefpELX2Q6zUThEC+tJu3tBy+EVPWOnOK1NnEcMnpd4iuqLnfSVfM3JfV
gKW2XNmNAgMBAAECggEBAMbBTYp7wZv693t+fwl3abmyJ6IgU15Equp3qC7LeCYj
LO79FSfGy0rDmf5QK8Bx0qEafDkNxAPmae4oqS54DOAAiGbSsegKa6IPFc90eyIZ
rlL7/r++c5RwMfj5BTpDbWuT98fXUW9kHETP0dGbFTChsjTuxh50ltw++HWDe8wD
CTHPRdWXmiUYtg1NK0gDwPIkYXxHqULMugH32KmgM60+jTv3Rc0bKk9MfQUYDPsg
/mPOUcw71fccVofwcK0t/Z50DiTw/hQMbuMahVGeAE3EtlG6MIryPltEsLC5LOUS
6D4CDCxwGDfawIdgu4qI3SPNsW3HIin2OT+TcbKqWgkCgYEA/RjJMd+hmSYWOAEZ
tQq3Z6ZRPZ/Ot9ZxW9cqL/rGWe4EE1G2VcH+PvUwqSjFixWW5beYirr3iUtlVs4h
vXIbn3YXKdPDCxsmQlv6+4FvmGt1+S+YFDmVCUHuUK8K6yfSIy8ZEypwqepLYgtQ
GYfcm6i3t9hND3WaA0oo2Uc9t4cCgYEA90rEsDC4/j+FcxbS+tWqAISZZKCTaKik
4aYge4RqHHxHXIJMStn3aME3U26cn7wkADsi+arWWXlreBPYT0Q0fu3m8LXwgx8z
Q7WwR5KmwGLp9fWljhCkUqK93guZNd7PcznVZ+9ftahnI5ZVYroD1HKCyQYDHZSx
257gb8Yyg0sCgYEAo94oZeIk/sNqH1iXDGCj3rgQYZXWgbCWNKBa8DWttwwkF14Q
b/XqZ1jY6K09OZAA4zq7yRbErl/ifRF5Ru9acyRCWxKAdfBeFRj4p15moojxGSV3
kaC2ngfSBOtQZnCh3aXg1kfkKh9IL3KjqeQNMbtLN/0T5658ySR6zjMSAoECgYEA
qv/SvpsScQ1vtEPGJJl28uC+SW0YQbjMOxTrRT4tOgFeP0QCiYfro05oeZL1TyeE
JXy/FLHjgv+JJwFiBH3/Zv4PQcgtxh3fDyUl/ngUQtijO+dfUbuERXgvnsmaajvm
a6FERpiqMC2BFISyCpDicRe2BFfZ86yMyHc2b1Q4Pb0CgYBBIyJ4K/HFNBdVuPjx
61fKzrZ0aKN26o7KTf0r9QcrvxmbVKLEJwm/b/lIVClX7ZP701/S1fDOSOanqSfi
71bM6A5JXqgyWW9RNVB9F5hMf7tqa9lp8jhVAWJuN+bB2S30G4AoOuJGdnwQNpt8
LNKg2ZGGRxBWPA9XdVnNHPfg9w==
-----END PRIVATE KEY-----