|
|
|
|
package alipay
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"github.com/jinzhu/gorm"
|
|
|
|
|
"github.com/shopspring/decimal"
|
|
|
|
|
"io/ioutil"
|
|
|
|
|
"log"
|
|
|
|
|
"net/http"
|
|
|
|
|
"net/url"
|
|
|
|
|
"recook/internal/api/mobile/pay/public"
|
|
|
|
|
"recook/internal/model/aftersales"
|
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type RefundPublicParam struct {
|
|
|
|
|
AppID string `json:"app_id"` // 支付宝分配给开发者的应用ID
|
|
|
|
|
Method string `json:"method"` // 接口名称
|
|
|
|
|
Charset string `json:"charset"` // 请求使用的编码格式,如utf-8
|
|
|
|
|
SignType string `json:"sign_type"` // 商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2
|
|
|
|
|
Sign string `json:"sign"` // 商户请求参数的签名串
|
|
|
|
|
Timestamp string `json:"timestamp"` // 发送请求的时间,格式"yyyy-MM-dd HH:mm:ss"
|
|
|
|
|
Version string `json:"version"` // 调用的接口版本,固定为:1.0
|
|
|
|
|
BizContent string `json:"biz_content"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func newPublicParam(d *RefundDetailParam) RefundPublicParam {
|
|
|
|
|
ts := time.Now().Format("2006-01-02 15:04:05")
|
|
|
|
|
biz, _ := json.Marshal(d)
|
|
|
|
|
|
|
|
|
|
return RefundPublicParam{
|
|
|
|
|
AppID: AppID,
|
|
|
|
|
Method: "alipay.trade.refund",
|
|
|
|
|
Charset: "utf-8",
|
|
|
|
|
SignType: "RSA2",
|
|
|
|
|
Version: "1.0",
|
|
|
|
|
Timestamp: ts,
|
|
|
|
|
BizContent: string(biz),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *RefundPublicParam) generateRequestParam() string {
|
|
|
|
|
r.Sign = generateSign(r)
|
|
|
|
|
|
|
|
|
|
m := struct2MapWhenAlipay(r)
|
|
|
|
|
p := url.Values{}
|
|
|
|
|
|
|
|
|
|
for k, v := range m {
|
|
|
|
|
if len(v) > 0 {
|
|
|
|
|
p.Add(k, v)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return p.Encode()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type RefundDetailParam struct {
|
|
|
|
|
OutTradeNo string `json:"out_trade_no"`
|
|
|
|
|
RefundAmount string `json:"refund_amount"`
|
|
|
|
|
OutRequestNo string `json:"out_request_no"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
{
|
|
|
|
|
"alipay_trade_refund_response": {
|
|
|
|
|
"code": "20000",
|
|
|
|
|
"msg": "Service Currently Unavailable",
|
|
|
|
|
"sub_code": "isp.unknow-error",
|
|
|
|
|
"sub_msg": "系统繁忙"
|
|
|
|
|
},
|
|
|
|
|
"sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
"alipay_trade_refund_response": {
|
|
|
|
|
"code": "10000",
|
|
|
|
|
"msg": "Success",
|
|
|
|
|
"trade_no": "支付宝交易号",
|
|
|
|
|
"out_trade_no": "6823789339978248",
|
|
|
|
|
"buyer_logon_id": "159****5620",
|
|
|
|
|
"fund_change": "Y",
|
|
|
|
|
"refund_fee": 88.88,
|
|
|
|
|
"refund_currency": "USD",
|
|
|
|
|
"gmt_refund_pay": "2014-11-27 15:45:57",
|
|
|
|
|
"refund_detail_item_list": [
|
|
|
|
|
{
|
|
|
|
|
"fund_channel": "ALIPAYACCOUNT",
|
|
|
|
|
"bank_code": "CEB",
|
|
|
|
|
"amount": 10,
|
|
|
|
|
"real_amount": 11.21,
|
|
|
|
|
"fund_type": "DEBIT_CARD"
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"store_name": "望湘园联洋店",
|
|
|
|
|
"buyer_user_id": "2088101117955611",
|
|
|
|
|
"refund_preset_paytool_list": {
|
|
|
|
|
"amount": [
|
|
|
|
|
12.21
|
|
|
|
|
],
|
|
|
|
|
"assert_type_code": "盒马礼品卡:HEMA;抓猫猫红包:T_CAT_COUPON"
|
|
|
|
|
},
|
|
|
|
|
"refund_settlement_id": "2018101610032004620239146945",
|
|
|
|
|
"present_refund_buyer_amount": "88.88",
|
|
|
|
|
"present_refund_discount_amount": "88.88",
|
|
|
|
|
"present_refund_mdiscount_amount": "88.88"
|
|
|
|
|
},
|
|
|
|
|
"sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
|
|
|
|
|
}
|
|
|
|
|
异常示例
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
type RefundResult struct {
|
|
|
|
|
Response struct {
|
|
|
|
|
Code string `json:"code"`
|
|
|
|
|
} `json:"alipay_trade_refund_response"`
|
|
|
|
|
Sign string `json:"sign"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Refund(tx *gorm.DB, asGoods *aftersales.Goods) error {
|
|
|
|
|
//这边判断是否有金额
|
|
|
|
|
if asGoods.RefundAmount.GreaterThan(decimal.NewFromFloat(0.0)) {
|
|
|
|
|
detailParam := RefundDetailParam{
|
|
|
|
|
OutTradeNo: asGoods.TradeNo,
|
|
|
|
|
RefundAmount: asGoods.RefundAmount.Truncate(2).String(),
|
|
|
|
|
OutRequestNo: asGoods.RefundNo,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub := newPublicParam(&detailParam)
|
|
|
|
|
if err := requestOrderRefund(&pub); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return public.SyncRefundSuccessCallback(tx, asGoods)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func requestOrderRefund(param *RefundPublicParam) error {
|
|
|
|
|
client := &http.Client{}
|
|
|
|
|
request, err := http.NewRequest("POST", RefundURL, strings.NewReader(param.generateRequestParam()))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
request.Header.Set("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
|
|
|
|
|
response, err := client.Do(request)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
defer func() { _ = response.Body.Close() }()
|
|
|
|
|
result, err := ioutil.ReadAll(response.Body)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Println("===============================")
|
|
|
|
|
fmt.Println(string(result))
|
|
|
|
|
|
|
|
|
|
var r RefundResult
|
|
|
|
|
err = json.Unmarshal(result, &r)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if r.Response.Code != "10000" {
|
|
|
|
|
log.Println(r)
|
|
|
|
|
return errors.New("支付宝退款请求结果不对")
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|