使用golang快速开发微信公众平台(九):下载对账单

首先,对账单是csv格式的;其次,对账单不是非常标准非常标准的csv格式——你直接解析会挂掉。
所以我采用的方式为:把对账单分为2部分,先写第1部分,再把第2部分拼进去。
唉卧槽他大爷的微信。

package controllers

import (
    "github.com/astaxie/beego"
    "monkeyServer/shopUtils/RandomStrUtil"
    "encoding/xml"
    "strings"
    "net/http"
    "bytes"
    "io/ioutil"
    "fmt"
    "os"
    "encoding/csv"
    "io"
    "log"
)

type AdminWXBillsController struct {
    beego.Controller
}

func (c *AdminWXBillsController) Post() {
    c.EnableRender = false

    //查询哪一天的对账单
    date := c.GetString("wxDate", "")
    if len(date) != 8 || date == "" {
        c.Ctx.WriteString("日期参数长度错误")
    }

    //https://api.mch.weixin.qq.com/pay/downloadbill
    //商户可以通过该接口下载历史交易清单。比如掉单、系统错误等导致商户侧和微信侧数据不一致,通过对账单核对后可校正支付状态。
    //注意:
    //1、微信侧未成功下单的交易不会出现在对账单中。支付成功后撤销的交易会出现在对账单中,跟原支付单订单号一致;
    //2、微信在次日9点启动生成前一天的对账单,建议商户10点后再获取;
    //3、对账单中涉及金额的字段单位为“元”。
    //4、对账单接口只能下载三个月以内的账单。

    //首先定义一个UnifyOrderReq用于填入我们要传入的参数。
    type WXBillReq struct {
        Appid     string `xml:"appid"`     //公众账号ID
        Mch_id    string `xml:"mch_id"`    //商户号
        Nonce_str string `xml:"nonce_str"` //随机字符串
        Sign      string `xml:"sign"`      //签名
        BillDate  string `xml:"bill_date"` //下载对账单的日期,格式:20140603
        BillType  string `xml:"bill_type"` //ALL,返回当日所有订单信息,默认值 SUCCESS,返回当日成功支付的订单 REFUND,返回当日退款订单 RECHARGE_REFUND,返回当日充值退款订单(相比其他对账单多一栏“返还手续费”)
    }

    type WXBillResp struct {
        Return_code string `xml:"return_code"`
        Return_msg  string `xml:"return_msg"`
    }

    var yourReq WXBillReq
    yourReq.Appid = beego.AppConfig.String("APPID")
    yourReq.Mch_id = beego.AppConfig.String("Mchid")
    yourReq.Nonce_str = RandomStrUtil.GetRandomString(32)
    yourReq.BillDate = date
    yourReq.BillType = "ALL"

    var m map[string]interface{}
    m = make(map[string]interface{}, 0)
    m["appid"] = yourReq.Appid
    m["mch_id"] = yourReq.Mch_id
    m["nonce_str"] = yourReq.Nonce_str
    m["bill_date"] = yourReq.BillDate
    m["bill_type"] = yourReq.BillType
    yourReq.Sign = wxpayCalcSign(m, WX_PAY_API_KEY) //这个key 微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全

    bytes_req, err := xml.Marshal(yourReq)
    if err != nil {
        beego.Error("wxpay转换为xml错误:", err)
        return
    }

    str_req := strings.Replace(string(bytes_req), "UnifyOrderReq", "xml", -1)
    //beego.Error("请求UnifiedOrder 拼接 xml 完成--------", str_req)

    bytes_req = []byte(str_req)
    //发送unified order请求.
    req, err := http.NewRequest("POST", "https://api.mch.weixin.qq.com/pay/downloadbill", bytes.NewReader(bytes_req))
    if err != nil {
        beego.Error("New Http Request发生错误,原因:", err)
        return
    }
    req.Header.Set("Accept", "application/xml")
    //这里的http header的设置是必须设置的.
    req.Header.Set("Content-Type", "application/xml;charset=utf-8")

    client := http.Client{}
    resp, _err := client.Do(req)
    if _err != nil {
        beego.Error("请求微信对账单接口发送错误, 原因:", _err)
        return
    }
    beego.Error("请求微信对账单接口已经执行完成")

    respBytes, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        beego.Error("解析返回body错误", err)
        return
    }
    defer resp.Body.Close()

    xmlResp := WXBillResp{}
    _err = xml.Unmarshal(respBytes, &xmlResp)

    if xmlResp.Return_code == "FAIL" {
        fmt.Printf("请求微信对账单失败 %+v\n:", xmlResp)
    } else {

        newPath := createWxBillCSVFile(date)

        in := string(respBytes)
        in = strings.Replace(in, "`", "", -1)

        spliteAfter := strings.Split(in, "总交易单数")
        r := csv.NewReader(strings.NewReader(spliteAfter[0]))

        resultStr := make([][]string, 0)
        for {
            record, err := r.Read()
            if err == io.EOF {
                break
            }
            if err != nil {
                log.Fatal(err)
            }

            resultStr = append(resultStr, record)
        }

        //写入文件
        f, err := os.Create(newPath)//创建文件
        if err != nil {
            beego.Error("创建csv文件错误", err)
        }
        defer f.Close()

        w := csv.NewWriter(f)//创建一个新的写入文件流
        err = w.WriteAll(resultStr)//写入数据
        w.Flush()

        //再读取文件 把因为分割缺少的文字补进来
        r2 := csv.NewReader(strings.NewReader(strings.Replace(spliteAfter[1], ",总交易额", "总交易单数,总交易额", -1)))
        resultStr2 := make([][]string, 0)
        for {
            record2, err := r2.Read()

            if err == io.EOF {
                break
            }
            if err != nil {
                log.Fatal(err)
            }

            resultStr2 = append(resultStr2, record2)
        }

        //写入剩下2行
        w2 := csv.NewWriter(f)  //创建一个新的写入文件流
        w2.WriteAll(resultStr2) //写入数据
        w2.Flush()

        c.Ctx.WriteString(string(respBytes))
    }
}

func createWxBillCSVFile(date string) string {

    dirPath := "static/wxcsv/"
    if !IsFileExist(dirPath) {
        err := os.MkdirAll(dirPath, 0)
        beego.Error("创建csv文件夹存在错误 ", err)
    }
    newPath := dirPath + date + ".csv"
    if IsFileExist(newPath) {
        err := os.Remove(newPath)
        if err != nil {
            beego.Error("删除旧的csv文件错误", err)
        }
    }
    return newPath
}
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页