You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
339 lines
11 KiB
339 lines
11 KiB
package pay_service
|
|
|
|
import (
|
|
"encoding/xml"
|
|
"fmt"
|
|
pay_core "github.com/chanxuehong/wechat/mch/core"
|
|
"github.com/chanxuehong/wechat/mch/pay"
|
|
"hudongzhuanjia/logger"
|
|
"hudongzhuanjia/models"
|
|
"hudongzhuanjia/utils"
|
|
"hudongzhuanjia/utils/define"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
const CallbackOrderUrl = "https://api.ouxuanhudong.com/PcClient/common/WeChatOauthCtl/callbackOrder"
|
|
|
|
func UnifiedOrder(content, openId string, fee, goodType, userId, activityId, expireAt int64) (map[string]interface{}, error) {
|
|
client, err := Client()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
outTradeNo := utils.RandomStr(32)
|
|
nonceStr := utils.RandomStr(32)
|
|
body := make(map[string]string, 0)
|
|
body["body"] = content
|
|
body["out_trade_no"] = outTradeNo
|
|
body["total_fee"] = fmt.Sprint(fee)
|
|
body["notify_url"] = CallbackOrderUrl
|
|
body["trade_type"] = "JSAPI"
|
|
body["nonce_str"] = nonceStr
|
|
body["sign_type"] = pay_core.SignType_MD5
|
|
body["openid"] = openId
|
|
//body["time_expire"] = pay_core.FormatTime(time.Unix(expireAt, 0))
|
|
|
|
resp, err := pay.UnifiedOrder(client, body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 记录这次订单
|
|
userOrder := new(models.UserOrder)
|
|
userOrder.Body = content
|
|
userOrder.UserId = userId
|
|
userOrder.ActivityId = activityId
|
|
userOrder.FeeType = "CNY"
|
|
userOrder.GoodType = goodType
|
|
userOrder.OpenId = openId
|
|
userOrder.OutTradeNo = outTradeNo
|
|
userOrder.ExpireAt = expireAt
|
|
userOrder.TotalFee = fee
|
|
userOrder.TradeType = "JSAPI"
|
|
userOrder.PrepayId = resp["prepay_id"]
|
|
userOrder.Status = 0
|
|
if _, err = models.Add(userOrder); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
//获取H5支付需要的paySign
|
|
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
|
|
pac := "prepay_id=" + resp["prepay_id"]
|
|
paySign := pay_core.JsapiSign(client.AppId(), timestamp, nonceStr, pac, pay_core.SignType_MD5, define.ApiKey)
|
|
|
|
//go PutOrderDelayQueue(userOrder)
|
|
return map[string]interface{}{
|
|
"appid": client.AppId(),
|
|
"timestamp": timestamp,
|
|
"nonce_str": nonceStr,
|
|
"package": pac,
|
|
"sign_type": pay_core.SignType_MD5,
|
|
"pay_sign": paySign,
|
|
"out_trade_no": outTradeNo,
|
|
"user_order_id": userOrder.Id,
|
|
}, nil
|
|
}
|
|
|
|
func ReOrder(outTradeNo string) (map[string]interface{}, error) {
|
|
userOrder := new(models.UserOrder)
|
|
exist, err := userOrder.GetByOutTradeNo(outTradeNo)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !exist {
|
|
return nil, fmt.Errorf("订单不存在")
|
|
}
|
|
|
|
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
|
|
nonceStr := utils.RandomStr(32)
|
|
//获取H5支付需要的paySign
|
|
pac := "prepay_id=" + userOrder.PrepayId
|
|
paySign := pay_core.JsapiSign(define.AppId, timestamp, nonceStr, pac, pay_core.SignType_MD5, define.ApiKey)
|
|
//go PutOrderDelayQueue(userOrder)
|
|
return map[string]interface{}{
|
|
"appid": define.AppId,
|
|
"timestamp": timestamp,
|
|
"nonce_str": nonceStr,
|
|
"package": pac,
|
|
"sign_type": pay_core.SignType_MD5,
|
|
"pay_sign": paySign,
|
|
"out_trade_no": outTradeNo,
|
|
"user_order_id": userOrder.Id,
|
|
}, nil
|
|
|
|
}
|
|
|
|
// Notify
|
|
type NotifyOrderParam struct {
|
|
XMLName struct{} `xml:"xml" json:"-"`
|
|
|
|
ReturnCode string `xml:"return_code,omitempty" json:"return_code,omitempty"`
|
|
ReturnMsg string `xml:"return_msg,omitempty" json:"return_msg,omitempty"`
|
|
ResultCode string `xml:"result_code,omitempty" json:"result_code,omitempty"`
|
|
ErrCode string `xml:"err_code,omitempty" json:"err_code,omitempty"`
|
|
ErrCodeDes string `xml:"err_code_des,omitempty" json:"err_code_des,omitempty"`
|
|
Appid string `xml:"appid,omitempty" json:"appid,omitempty"`
|
|
MchId string `xml:"mch_id,omitempty" json:"mch_id,omitempty"`
|
|
DeviceInfo string `xml:"device_info,omitempty" json:"device_info,omitempty"`
|
|
NonceStr string `xml:"nonce_str,omitempty" json:"nonce_str,omitempty"`
|
|
Sign string `xml:"sign,omitempty" json:"sign,omitempty"`
|
|
SignType string `xml:"sign_type,omitempty" json:"sign_type,omitempty"`
|
|
Openid string `xml:"openid,omitempty" json:"openid,omitempty"`
|
|
IsSubscribe string `xml:"is_subscribe,omitempty" json:"is_subscribe,omitempty"`
|
|
TradeType string `xml:"trade_type,omitempty" json:"trade_type,omitempty"`
|
|
BankType string `xml:"bank_type,omitempty" json:"bank_type,omitempty"`
|
|
TotalFee int `xml:"total_fee,omitempty" json:"total_fee,omitempty"`
|
|
SettlementTotalFee int `xml:"settlement_total_fee,omitempty" json:"settlement_total_fee,omitempty"`
|
|
FeeType string `xml:"fee_type,omitempty" json:"fee_type,omitempty"`
|
|
CashFee int `xml:"cash_fee,omitempty" json:"cash_fee,omitempty"`
|
|
CashFeeType string `xml:"cash_fee_type,omitempty" json:"cash_fee_type,omitempty"`
|
|
TransactionId string `xml:"transaction_id,omitempty" json:"transaction_id,omitempty"`
|
|
OutTradeNo string `xml:"out_trade_no,omitempty" json:"out_trade_no,omitempty"`
|
|
TimeEnd string `xml:"time_end,omitempty" json:"time_end,omitempty"`
|
|
}
|
|
|
|
func NotifyOrder(body string) (*models.UserOrder, error) {
|
|
res := NotifyOrderParam{}
|
|
err := xml.Unmarshal([]byte(body), &res)
|
|
if err != nil {
|
|
err = fmt.Errorf("xml unmarsal error:%+v", err)
|
|
return nil, err
|
|
}
|
|
|
|
if res.ReturnCode != define.CODE_SUCCESS {
|
|
err = fmt.Errorf("network error, retrun_code: %+v and return_msg: %+v", res.ReturnCode, res.ReturnMsg)
|
|
return nil, err
|
|
}
|
|
|
|
if res.ResultCode != define.CODE_SUCCESS {
|
|
err = fmt.Errorf("trade error, result_code: %+v and err_code: %+v and err_desc: %+v",
|
|
res.ResultCode, res.ErrCode, res.ErrCodeDes)
|
|
return nil, err
|
|
}
|
|
|
|
order := new(models.UserOrder)
|
|
exist, err := order.GetByOutTradeNo(res.OutTradeNo)
|
|
if err != nil || !exist {
|
|
err = fmt.Errorf("user order get by out_trade_no: %+v, error: %+v, exist: %+v", res.OutTradeNo, err, exist)
|
|
return nil, err
|
|
}
|
|
|
|
order.TransactionId = res.TransactionId
|
|
order.Status = 1
|
|
_, err = models.Update(order.Id, order, "status", "transaction_id")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = HandleSuccess(order)
|
|
if err != nil {
|
|
logger.Error(fmt.Sprintf("callback 错误处理: %v", err))
|
|
return nil, err
|
|
}
|
|
return order, nil
|
|
|
|
}
|
|
|
|
func OrderQuery(outTradeNo string) (map[string]string, error) {
|
|
client, err := Client()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
body := make(map[string]string)
|
|
body["out_trade_no"] = outTradeNo
|
|
body["nonce_str"] = utils.RandomStr(32)
|
|
body["sign_type"] = pay_core.SignType_MD5
|
|
|
|
res, err := pay.OrderQuery(client, body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
func Close(outTradeNo string) error {
|
|
client, err := Client()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
body := make(map[string]string)
|
|
body["out_trade_no"] = outTradeNo
|
|
body["nonce_str"] = utils.RandomStr(32)
|
|
body["sign_type"] = pay_core.SignType_MD5
|
|
|
|
_, err = pay.CloseOrder(client, body)
|
|
// 请求关闭订单,成功后得到结果
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
const CallbackRefundUrl = "https://api.ouxuanhudong.com/PcClient/common/WeChatOauthCtl/callbackRefund"
|
|
|
|
func Refund(reason, outTradeNo string) (map[string]string, error) {
|
|
userOrder := new(models.UserOrder)
|
|
exist, err := userOrder.GetByOutTradeNo(outTradeNo)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !exist {
|
|
return nil, fmt.Errorf("订单不存在")
|
|
}
|
|
client, err := Client()
|
|
|
|
outRefundNo := utils.RandomStr(64)
|
|
nonceStr := utils.RandomStr(32)
|
|
body := make(map[string]string, 0)
|
|
body["out_trade_no"] = outTradeNo
|
|
body["out_refund_no"] = outRefundNo
|
|
body["total_fee"] = fmt.Sprint(userOrder.TotalFee)
|
|
body["refund_fee"] = fmt.Sprint(userOrder.TotalFee)
|
|
body["nonce_str"] = nonceStr
|
|
body["sign_type"] = pay_core.SignType_MD5
|
|
body["refund_desc"] = reason
|
|
body["notify_url"] = CallbackRefundUrl
|
|
|
|
res, err := pay.Refund(client, body)
|
|
//
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
userOrder.Status = 3
|
|
_, err = models.Update(userOrder.Id, userOrder, "status")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
//go PutOrderDelayQueue(userOrder) // 退款查询
|
|
return res, nil
|
|
}
|
|
|
|
func QueryRefund(outTradeNo string) (*pay.RefundQueryResponse, error) {
|
|
client, err := Client()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
res, err := pay.RefundQuery2(client, &pay.RefundQueryRequest{
|
|
OutTradeNo: outTradeNo,
|
|
NonceStr: utils.RandomStr(32),
|
|
SignType: pay_core.SignType_MD5,
|
|
})
|
|
//请求申请退款
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
type NotifyRefundParam struct {
|
|
XMLName struct{} `xml:"xml" json:"-"`
|
|
|
|
ReturnCode string `xml:"return_code,omitempty" json:"return_code,omitempty"`
|
|
ReturnMsg string `xml:"return_msg,omitempty" json:"return_msg,omitempty"`
|
|
ResultCode string `xml:"result_code,omitempty" json:"result_code,omitempty"`
|
|
ErrCode string `xml:"err_code,omitempty" json:"err_code,omitempty"`
|
|
ErrCodeDes string `xml:"err_code_des,omitempty" json:"err_code_des,omitempty"`
|
|
Appid string `xml:"appid,omitempty" json:"appid,omitempty"`
|
|
MchId string `xml:"mch_id,omitempty" json:"mch_id,omitempty"`
|
|
NonceStr string `xml:"nonce_str,omitempty" json:"nonce_str,omitempty"`
|
|
ReqInfo string `xml:"req_info,omitempty" json:"req_info,omitempty"`
|
|
TransactionId string `xml:"transaction_id,omitempty" json:"transaction_id,omitempty"`
|
|
OutTradeNo string `xml:"out_trade_no,omitempty" json:"out_trade_no,omitempty"`
|
|
RefundId string `xml:"refund_id,omitempty" json:"refund_id,omitempty"`
|
|
OutRefundNo string `xml:"out_refund_no,omitempty" json:"out_refund_no,omitempty"`
|
|
TotalFee int `xml:"total_fee,omitempty" json:"total_fee,omitempty"`
|
|
SettlementTotalFee int `xml:"settlement_total_fee,omitempty" json:"settlement_total_fee,omitempty"`
|
|
RefundFee int `xml:"refund_fee,omitempty" json:"refund_fee,omitempty"`
|
|
SettlementRefundFee int `xml:"settlement_refund_fee,omitempty" json:"settlement_refund_fee,omitempty"`
|
|
RefundStatus string `xml:"refund_status,omitempty" json:"refund_status,omitempty"`
|
|
SuccessTime string `xml:"success_time,omitempty" json:"success_time,omitempty"`
|
|
RefundRecvAccout string `xml:"refund_recv_accout,omitempty" json:"refund_recv_accout,omitempty"`
|
|
RefundAccount string `xml:"refund_account,omitempty" json:"refund_account,omitempty"`
|
|
RefundRequestSource string `xml:"refund_request_source,omitempty" json:"refund_request_source,omitempty"`
|
|
}
|
|
|
|
func NotifyRefund(body string) (*models.UserOrder, error) {
|
|
res := NotifyRefundParam{}
|
|
err := xml.Unmarshal([]byte(body), &res)
|
|
if err != nil {
|
|
err = fmt.Errorf("xml unmarsal error: %+v", err)
|
|
return nil, err
|
|
}
|
|
|
|
logger.Error("return-->", res.ReturnCode == define.CODE_SUCCESS)
|
|
logger.Error("result-->", res.ResultCode == define.CODE_SUCCESS)
|
|
logger.Error(res)
|
|
if res.ReturnCode != define.CODE_SUCCESS {
|
|
err = fmt.Errorf("network error, retrun_code: %+v and return_msg: %+v", res.ReturnCode, res.ReturnMsg)
|
|
return nil, err
|
|
}
|
|
|
|
if res.ResultCode != define.CODE_SUCCESS {
|
|
err = fmt.Errorf("trade error, result_code: %+v and err_code: %+v and err_desc: %+v",
|
|
res.ResultCode, res.ErrCode, res.ErrCodeDes)
|
|
return nil, err
|
|
}
|
|
|
|
order := new(models.UserOrder)
|
|
exist, err := order.GetByOutTradeNo(res.OutTradeNo)
|
|
if err != nil || !exist {
|
|
err = fmt.Errorf("user order get by out_trade_no: %+v, error: %+v, exist: %+v", res.OutTradeNo, err, exist)
|
|
return nil, err
|
|
}
|
|
|
|
order.RefundRecvAccount = res.RefundRecvAccout
|
|
order.RefundAccount = res.RefundAccount
|
|
order.TransactionId = res.TransactionId
|
|
order.Status = 4
|
|
_, err = models.Update(order.Id, order, "status", "transaction_id")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return order, nil
|
|
}
|