首先,对账单是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
}