admin管理员组

文章数量:1559076

😊 @ 作者: 一恍过去 💖 @ 九游会下载主页: https://blog.csdn/zhuocailing3390 🎊 @ 社区: java技术栈交流 🎉 @ 主题: 微信支付统一支付接口(h5、jsapi、h5、app、小程序) ⏱️ @ 创作时间: 2022年07月10日

目录

  • 前言
  • 1、引入pom
  • 2、配置yaml
  • 3、配置密钥文件
  • 4、配置payconfig
  • 5、定义统一枚举
  • 6、封装统一请求处理
  • 7、封装统一代码
    • 7.1、统一下单处理
    • 7.2 、其他接口处理(退款、查询、取消订单等)

前言

对微信支付的h5、jsapi、h5、app、小程序九游会下载的支付方式进行统一,此封装接口适用于普通商户模式支付,如果要进行服务商模式支付可以结合服务商官方api进行参数修改(未验证可行性)。

1、引入pom

        
        <dependency>
            <groupid>com.github.wechatpay-apiv3groupid>
            <artifactid>wechatpay-apache-httpclientartifactid>
            <version>0.4.7version>
        dependency>

2、配置yaml

wxpay:
  #应用编号
  appid: xxxx
  #商户号
  mchid: xxx
  # apiv2密钥
  apikey: xxxx
  # apiv3密钥
  apiv3key: xxx
  # 微信支付v3-url前缀
  baseurl: https://api.mch.weixin.qq/v3
  # 支付通知回调, pjm6m9.natappfree 为内网穿透地址
  notifyurl: http://pjm6m9.natappfree/pay/paynotify
  # 退款通知回调, pjm6m9.natappfree 为内网穿透地址
  refundnotifyurl: http://pjm6m9.natappfree/pay/refundnotify
  # 密钥路径,resources根目录下
  keypempath: apiclient_key.pem
  #商户证书序列号
  serialno: xxxxx

3、配置密钥文件

在商户/服务商平台的”账户中心" => “api安全” 进行api证书、密钥的设置,api证书主要用于获取“商户证书序列号”以及“p12”、“key.pem”、”cert.pem“证书文件,j将获取的apiclient_key.pem文件放在项目的resources目录下。

4、配置payconfig

wechatpayconfig:


import com.wechat.pay.contrib.apache.httpclient.wechatpayhttpclientbuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.privatekeysigner;
import com.wechat.pay.contrib.apache.httpclient.auth.verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.wechatpay2credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.wechatpay2validator;
import com.wechat.pay.contrib.apache.httpclient.cert.certificatesmanager;
import com.wechat.pay.contrib.apache.httpclient.exception.httpcodeexception;
import com.wechat.pay.contrib.apache.httpclient.exception.notfoundexception;
import com.wechat.pay.contrib.apache.httpclient.util.pemutil;
import lombok.data;
import lombok.extern.slf4j.slf4j;
import org.apache.http.impl.client.closeablehttpclient;
import org.springframework.boot.context.properties.configurationproperties;
import org.springframework.context.annotation.bean;
import org.springframework.stereotype.component;
import java.io.ioexception;
import java.io.inputstream;
import java.nio.charset.standardcharsets;
import java.security.generalsecurityexception;
import java.security.privatekey;
/**
 * @author:
 * @description:
 **/
@component
@data
@slf4j
@configurationproperties(prefix = "wxpay")
public class wechatpayconfig {
    /**
     * 应用编号
     */
    private string appid;
    /**
     * 商户号
     */
    private string mchid;
    /**
     * 服务商商户号
     */
    private string slmchid;
    /**
     * apiv2密钥
     */
    private string apikey;
    /**
     * apiv3密钥
     */
    private string apiv3key;
    /**
     * 支付通知回调地址
     */
    private string notifyurl;
    /**
     * 退款回调地址
     */
    private string refundnotifyurl;
    /**
     * api 证书中的 key.pem
     */
    private string keypempath;
    /**
     * 商户序列号
     */
    private string serialno;
    /**
     * 微信支付v3-url前缀
     */
    private string baseurl;
    /**
     * 获取商户的私钥文件
     * @param keypempath
     * @return
     */
    public privatekey getprivatekey(string keypempath){
            inputstream inputstream = this.getclass().getclassloader().getresourceasstream(keypempath);
            if(inputstream==null){
                throw new runtimeexception("私钥文件不存在");
            }
            return pemutil.loadprivatekey(inputstream);
    }
    /**
     * 获取证书管理器实例
     * @return
     */
    @bean
    public  verifier getverifier() throws generalsecurityexception, ioexception, httpcodeexception, notfoundexception {
        log.info("获取证书管理器实例");
        //获取商户私钥
        privatekey privatekey = getprivatekey(keypempath);
        //私钥签名对象
        privatekeysigner privatekeysigner = new privatekeysigner(serialno, privatekey);
        //身份认证对象
        wechatpay2credentials wechatpay2credentials = new wechatpay2credentials(mchid, privatekeysigner);
        // 使用定时更新的签名验证器,不需要传入证书
        certificatesmanager certificatesmanager = certificatesmanager.getinstance();
        certificatesmanager.putmerchant(mchid,wechatpay2credentials,apiv3key.getbytes(standardcharsets.utf_8));
        return certificatesmanager.getverifier(mchid);
    }
    /**
     * 获取支付http请求对象
     * @param verifier
     * @return
     */
    @bean(name = "wxpayclient")
    public closeablehttpclient getwxpayclient(verifier verifier)  {
        //获取商户私钥
        privatekey privatekey = getprivatekey(keypempath);
        wechatpayhttpclientbuilder builder = wechatpayhttpclientbuilder.create()
                .withmerchant(mchid, serialno, privatekey)
                .withvalidator(new wechatpay2validator(verifier));
        // 通过wechatpayhttpclientbuilder构造的httpclient,会自动的处理签名和验签,并进行证书自动更新
        return builder.build();
    }
    /**
     * 获取httpclient,无需进行应答签名验证,跳过验签的流程
     */
    @bean(name = "wxpaynosignclient")
    public closeablehttpclient getwxpaynosignclient(){
        //获取商户私钥
        privatekey privatekey = getprivatekey(keypempath);
        //用于构造httpclient
        wechatpayhttpclientbuilder builder = wechatpayhttpclientbuilder.create()
                //设置商户信息
                .withmerchant(mchid, serialno, privatekey)
                //无需进行签名验证、通过withvalidator((response) -> true)实现
                .withvalidator((response) -> true);
        // 通过wechatpayhttpclientbuilder构造的httpclient,会自动的处理签名和验签,并进行证书自动更新
        return builder.build();
    }
}

5、定义统一枚举

wechatpayurlenum:


@allargsconstructor
@getter
public enum wechatpayurlenum {
	/**
	 * native
	 */
	native("native"),
	/**
	 * app
	 */
	app("app"),
	/**
	 * h5
	 */
	h5("h5"),
	/**
	 *  jsapi
	 */
	jsapi("jsapi"),
	/**
	 *  小程序jsapi
	 */
	sub_jsapi("sub_jsapi"),
	/**
	 * native下单
	 */
	pay_transactions("/pay/transactions/"),
	/**
	 * native下单
	 */
	native_pay_v2("/pay/unifiedorder"),
	/**
	 * 查询订单
	 */
	order_query_by_no("/pay/transactions/out-trade-no/"),
	/**
	 * 关闭订单
	 */
	close_order_by_no("/pay/transactions/out-trade-no/%s/close"),
	/**
	 * 申请退款
	 */
	domestic_refunds("/refund/domestic/refunds"),
	/**
	 * 查询单笔退款
	 */
	domestic_refunds_query("/refund/domestic/refunds/"),
	/**
	 * 申请交易账单
	 */
	trade_bills("/bill/tradebill"),
	/**
	 * 申请资金账单
	 */
	fund_flow_bills("/bill/fundflowbill");
	/**
	 * 类型
	 */
	private final string type;
}

6、封装统一请求处理

wechatpayrequest:


import lombok.extern.slf4j.slf4j;
import org.apache.http.httpentity;
import org.apache.http.httpstatus;
import org.apache.http.client.methods.closeablehttpresponse;
import org.apache.http.client.methods.httpget;
import org.apache.http.client.methods.httppost;
import org.apache.http.entity.stringentity;
import org.apache.http.impl.client.closeablehttpclient;
import org.apache.http.util.entityutils;
import org.springframework.stereotype.component;
import javax.annotation.resource;
import java.io.ioexception;
/**
 * @author: 
 * @description:
 **/
@component
@slf4j
public class wechatpayrequest {
    @resource
    private closeablehttpclient wxpayclient;
    public  string wechathttpget(string url) {
        try {
            // 拼接请求参数
            httpget httpget = new httpget(url);
            httpget.setheader("accept", "application/json");
            //完成签名并执行请求
            closeablehttpresponse response = wxpayclient.execute(httpget);
            return getresponsebody(response);
        }catch (exception e){
            throw new runtimeexception(e.getmessage());
        }
    }
    public  string wechathttppost(string url,string paramsstr) {
        try {
            httppost httppost = new httppost(url);
            stringentity entity = new stringentity(paramsstr, "utf-8");
            entity.setcontenttype("application/json");
            httppost.setentity(entity);
            httppost.setheader("accept", "application/json");
            closeablehttpresponse response = wxpayclient.execute(httppost);
            return getresponsebody(response);
        }catch (exception e){
            throw new runtimeexception(e.getmessage());
        }
    }
   private string getresponsebody(closeablehttpresponse response) throws ioexception {
            //响应体
           httpentity entity = response.getentity();
           string body = entity==null?"":entityutils.tostring(entity);
            //响应状态码
            int statuscode = response.getstatusline().getstatuscode();
            //处理成功,204是,关闭订单时微信返回的正常状态码
            if (statuscode == httpstatus.sc_ok || statuscode == httpstatus.sc_no_content) {
                log.info("成功, 返回结果 = "  body);
            } else {
                string msg = "微信支付请求失败,响应码 = "  statuscode  ",返回结果 = "  body;
                log.error(msg);
                throw new runtimeexception(msg);
            }
            return body;
    }
}

7、封装统一代码

对支付接口进行统一的封装,通过传入不同类型(h5、jsapi、app)的九游会下载的支付方式,封装公共的请求参数,对不同类型特定参数进行单独的处理。

商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易会话标识后再按native、jsapi、app、h5等不同场景生成交易串调起支付。

7.1、统一下单处理

paycontroller:


import com.alibaba.fastjson.json;
import com.alibaba.fastjson.jsonobject;
import com.alibaba.fastjson.typereference;
import com.github.xiaoymin.knife4j.annotations.apioperationsupport;
import com.lhz.demo.pay.wechatpayconfig;
import com.lhz.demo.model.enums.wechatpayurlenum;
import com.lhz.demo.pay.wechatpayrequest;
import io.swagger.annotations.api;
import io.swagger.annotations.apioperation;
import lombok.extern.slf4j.slf4j;
import org.apache.http.client.methods.closeablehttpresponse;
import org.apache.http.client.methods.httpget;
import org.apache.http.impl.client.closeablehttpclient;
import org.apache.http.util.entityutils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.resource;
import javax.crypto.mac;
import javax.crypto.spec.secretkeyspec;
import java.io.*;
import java.nio.charset.standardcharsets;
import java.util.*;
/**
 * @author: 
 * @description:
 **/
@api(tags = "支付接口(api3)")
@restcontroller
@requestmapping("/test")
@slf4j
public class paycontroller {
    @resource
    private wechatpayconfig wechatpayconfig;
    @resource
    private wechatpayrequest wechatpayrequest;
    /**
     * 无需应答签名
     */
    @resource
    private closeablehttpclient wxpaynosignclient;
    /**
     * type:h5、jsapi、app、native、sub_jsapi
     * @param type
     * @return
     */
    @apioperation(value = "统一下单-统一接口", notes = "统一下单-统一接口")
    @apioperationsupport(order = 10)
    @getmapping("/transactions")
    public map<string,object> transactions(string type) {
        log.info("统一下单api,九游会下载的支付方式:{}",type);
        // 统一参数封装
        map<string, object> params = new hashmap<>(8);
        params.put("appid", wechatpayconfig.getappid());
        params.put("mchid", wechatpayconfig.getmchid());
        params.put("description", "测试商品");
        int outtradeno = new random().nextint(999999999);
        params.put("out_trade_no", outtradeno  "");
        params.put("notify_url", wechatpayconfig.getnotifyurl());
        map<string, object> amountmap = new hashmap<>(4);
        // 金额单位为分
        amountmap.put("total", 1);
        amountmap.put("currency", "cny");
        params.put("amount", amountmap);
        // 场景信息
        map<string, object> sceneinfomap = new hashmap<>(4);
        // 客户端ip
        sceneinfomap.put("payer_client_ip", "127.0.0.1");
        // 商户端设备号(门店号或收银设备id)
        sceneinfomap.put("device_id", "127.0.0.1");
        // 除h5与jsapi有特殊参数外,其他的九游会下载的支付方式都一样
        if (type.equals(wechatpayurlenum.h5.gettype())) {
            map<string, object> h5infomap = new hashmap<>(4);
            // 场景类型:ios, android, wap
            h5infomap.put("type", "ios");
            sceneinfomap.put("h5_info", h5infomap);
        } else if (type.equals(wechatpayurlenum.jsapi.gettype()) || type.equals(wechatpayurlenum.sub_jsapi.gettype())) {
            map<string, object> payermap = new hashmap<>(4);
            payermap.put("openid", "123123123");
            params.put("payer", payermap);
        }
        params.put("scene_info", sceneinfomap);
        string paramsstr = json.tojsonstring(params);
        log.info("请求参数 ===> {}"  paramsstr);
        // 重写type值,因为小程序会多一个下划线(sub_type)
        string[] split = type.split("_");
        string  newtype = split[split.length - 1];
        string resstr = wechatpayrequest.wechathttppost(wechatpayconfig.getbaseurl().concat(wechatpayurlenum.pay_transactions.gettype().concat(newtype)), paramsstr);
        map<string, object> resmap = jsonobject.parseobject(resstr, new typereference<map<string, object>>(){});
        map<string, object> signmap = paysignmsg(resmap, type);
        resmap.put("type",type);
        resmap.put("signmap",signmap);
        return resmap;
    }
    private  map<string, object>  paysignmsg(map<string, object> map,string type){
        // 设置签名信息,native与h5不需要
        if(type.equals(wechatpayurlenum.h5.gettype()) || type.equals(wechatpayurlenum.native.gettype()) ){
            return null;
        }
        long timemillis = system.currenttimemillis();
        string appid = wechatpayconfig.getappid();
        string timestamp = timemillis/1000"";
        string noncestr = timemillis"";
        string prepayid = map.get("prepay_id").tostring();
        string packagestr = "prepay_id="prepayid;
        // 公共参数
        map<string, object> resmap = new hashmap<>();
        resmap.put("noncestr",noncestr);
        resmap.put("timestamp",timestamp);
        // jsapi、sub_jsapi(小程序)
        if(type.equals(wechatpayurlenum.jsapi.gettype()) || type.equals(wechatpayurlenum.sub_jsapi.gettype()) ) {
            resmap.put("appid",appid);
            resmap.put("package", packagestr);
            // 使用字段appid、timestamp、noncestr、package进行签名
            string paysign = createsign(resmap);
            resmap.put("paysign", paysign);
            resmap.put("signtype", "hmac-sha256");
        }
        // app
        if(type.equals(wechatpayurlenum.app.gettype())) {
            resmap.put("appid",appid);
            resmap.put("prepayid", prepayid);
            // 使用字段appid、timestamp、noncestr、prepayid进行签名
            string sign = createsign(resmap);
            resmap.put("package", "sign=wxpay");
            resmap.put("partnerid", wechatpayconfig.getmchid());
            resmap.put("sign", sign);
            resmap.put("signtype", "hmac-sha256");
        }
        return resmap;
    }
    /**
     * 获取加密数据
     */
    private  string createsign(map<string, object> params){
        try {
            map<string, object> treemap = new treemap<>(params);
            list<string> signlist = new arraylist<>(5);
            for (map.entry<string, object> entry : treemap.entryset())
            {
                signlist.add(entry.getkey()  "="  entry.getvalue());
            }
            string signstr = string.join("&", signlist);
            signstr = signstr"&key="wechatpayconfig.getapiv3key();
            system.out.println(signstr);
            mac sha = mac.getinstance("hmacsha256");
            secretkeyspec secretkey = new secretkeyspec(wechatpayconfig.getapiv3key().getbytes(standardcharsets.utf_8), "hmacsha256");
            sha.init(secretkey);
            byte[] array = sha.dofinal(signstr.getbytes(standardcharsets.utf_8));
            stringbuilder sb = new stringbuilder();
            for (byte item : array) {
                sb.append(integer.tohexstring((item & 0xff) | 0x100), 1, 3);
            }
            signstr = sb.tostring().touppercase();
            system.out.println(signstr);
            return signstr;
        }catch (exception e){
            throw new runtimeexception("加密失败!");
        }
    }
}

7.2 、其他接口处理(退款、查询、取消订单等)

paycontroller:


import com.alibaba.fastjson.json;
import com.alibaba.fastjson.jsonobject;
import com.alibaba.fastjson.typereference;
import com.github.xiaoymin.knife4j.annotations.apioperationsupport;
import com.lhz.demo.pay.wechatpayconfig;
import com.lhz.demo.model.enums.wechatpayurlenum;
import com.lhz.demo.pay.wechatpayrequest;
import io.swagger.annotations.api;
import io.swagger.annotations.apioperation;
import lombok.extern.slf4j.slf4j;
import org.apache.http.client.methods.closeablehttpresponse;
import org.apache.http.client.methods.httpget;
import org.apache.http.impl.client.closeablehttpclient;
import org.apache.http.util.entityutils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.resource;
import javax.crypto.mac;
import javax.crypto.spec.secretkeyspec;
import java.io.*;
import java.nio.charset.standardcharsets;
import java.util.*;
/**
 * @author: lihuazhi
 * @description:
 **/
@api(tags = "支付接口(api3)")
@restcontroller
@requestmapping("/test")
@slf4j
public class paycontroller {
    @resource
    private wechatpayconfig wechatpayconfig;
    @resource
    private wechatpayrequest wechatpayrequest;
    /**
     * 无需应答签名
     */
    @resource
    private closeablehttpclient wxpaynosignclient;
    @apioperation(value = "根据订单号查询订单-统一接口", notes = "根据订单号查询订单-统一接口")
    @apioperationsupport(order = 15)
    @getmapping("/transactions/{orderno}")
    public  map<string, object> transactionsbyorderno(@pathvariable("orderno") string orderno) {
        // todo 如果是扫码支付时,该接口就很有必要,应该前端通过轮询的方式请求该接口查询订单是否支付成功
        log.info("根据订单号查询订单,订单号: {}", orderno);
        string url = wechatpayconfig.getbaseurl().concat(wechatpayurlenum.order_query_by_no.gettype().concat(orderno))
                .concat("?mchid=").concat(wechatpayconfig.getmchid());
        string res = wechatpayrequest.wechathttpget(url);
        log.info("查询订单结果:{}",res);
        map<string, object> resmap = jsonobject.parseobject(res, new typereference<map<string, object>>(){});
        string outtradeno = resmap.get("out_trade_no").tostring();
        string appid = resmap.get("appid").tostring();
        string mchid = resmap.get("mchid").tostring();
        /**
         *         交易状态,枚举值:
         *         success:支付成功
         *         refund:转入退款
         *         notpay:未支付
         *         closed:已关闭
         *         revoked:已撤销(仅付款码支付会返回)
         *         userpaying:用户支付中(仅付款码支付会返回)
         *         payerror:支付失败(仅付款码支付会返回)
         */
        string tradestate = resmap.get("trade_state").tostring();
        log.info("outtradeno:"outtradeno);
        log.info("appid:"appid);
        log.info("mchid:"mchid);
        log.info("tradestate:"tradestate);
        return resmap;
    }
    /**
     * 关闭(取消)订单
     * @param orderno
     * @return
     */
    @apioperation(value = "关闭(取消)订单-统一接口", notes = "关闭(取消)订单-统一接口")
    @apioperationsupport(order = 20)
    @postmapping("/closeorder/{orderno}")
    public void closeorder(@pathvariable("orderno") string orderno) {
        // todo 用于在客户下单后,不进行支付,取消订单的场景
        log.info("根据订单号取消订单,订单号: {}", orderno);
        string url = string.format(wechatpayurlenum.close_order_by_no.gettype(), orderno);
        url = wechatpayconfig.getbaseurl().concat(url);
        // 设置参数
        map<string, string> params = new hashmap<>(2);
        params.put("mchid", wechatpayconfig.getmchid());
        string paramsstr = json.tojsonstring(params);
        log.info("请求参数 ===> {}"  paramsstr);
        string res = wechatpayrequest.wechathttppost(url,paramsstr);
    }
    /**
     * 申请退款
     * @param orderno
     */
    @apioperation(value = "申请退款-统一接口", notes = "申请退款-统一接口")
    @apioperationsupport(order = 25)
    @postmapping("/refundorder/{orderno}")
    public void refundorder(@pathvariable("orderno") string orderno) {
        log.info("根据订单号申请退款,订单号: {}", orderno);
        string url = wechatpayconfig.getbaseurl().concat(wechatpayurlenum.domestic_refunds.gettype());
        // 设置参数
        map<string, object> params = new hashmap<>(2);
        // 订单编号
        params.put("out_trade_no", orderno);
        // 退款单编号 - 自定义
        int outrefundno = new random().nextint(999999999);
        log.info("退款申请号:{}",outrefundno);
        params.put("out_refund_no",outrefundno"");
        // 退款原因
        params.put("reason","申请退款");
        // 退款通知回调地址
        params.put("notify_url", wechatpayconfig.getrefundnotifyurl());
        map<string, object>  amountmap =new hashmap<>();
        //退款金额,单位:分
        amountmap.put("refund", 1);
        //原订单金额,单位:分
        amountmap.put("total", 1);
        //退款币种
        amountmap.put("currency", "cny");
        params.put("amount", amountmap);
        string paramsstr = json.tojsonstring(params);
        log.info("请求参数 ===> {}"  paramsstr);
        string res = wechatpayrequest.wechathttppost(url,paramsstr);
        log.info("退款结果:{}",res);
    }
    /**
     * 查询单笔退款信息
     * @param refundno
     * @return
     */
    @apioperation(value = "查询单笔退款信息-统一接口", notes = "查询单笔退款信息-统一接口")
    @apioperationsupport(order = 30)
    @getmapping("/queryrefundorder/{refundno}")
    public  map<string, object> queryrefundorder(@pathvariable("refundno") string refundno) {
        log.info("根据订单号查询退款订单,订单号: {}", refundno);
        string url = wechatpayconfig.getbaseurl().concat(wechatpayurlenum.domestic_refunds_query.gettype().concat(refundno));
        string res = wechatpayrequest.wechathttpget(url);
        log.info("查询退款订单结果:{}",res);
        map<string, object> resmap = jsonobject.parseobject(res, new typereference<map<string, object>>(){});
        string successtime = resmap.get("success_time").tostring();
        string refundid = resmap.get("refund_id").tostring();
        /**
         * 款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往商户平台-交易中心,手动处理此笔退款。
         * 枚举值:
         * success:退款成功
         * closed:退款关闭
         * processing:退款处理中
         * abnormal:退款异常
         */
        string status = resmap.get("status").tostring();
        /**
         * 枚举值:
         * original:原路退款
         * balance:退回到余额
         * other_balance:原账户异常退到其他余额账户
         * other_bankcard:原银行卡异常退到其他银行卡
         */
        string channel = resmap.get("channel").tostring();
        log.info("successtime:"successtime);
        log.info("channel:"channel);
        log.info("refundid:"refundid);
        log.info("status:"status);
        // todo 在查询单笔退款信息时,可以再去查询一次订单的状态,保证该订单已经退款完毕了
        return resmap;
    }
    /**
     * 申请交易账单
     * @param billdate 格式yyyy-mm-dd 仅支持三个月内的账单下载申请 ,如果传入日期未为当天则会出错
     * @param billtype 分为:all、success、refund
     * all:返回当日所有订单信息(不含充值退款订单)
     * success:返回当日成功支付的订单(不含充值退款订单)
     * refund:返回当日退款订单(不含充值退款订单)
     * @return
     */
    @apioperation(value = "申请交易账单-统一接口", notes = "申请交易账单-统一接口")
    @apioperationsupport(order = 35)
    @getmapping("/tradebill")
    public  string tradebill(@requestparam("billdate") string billdate, @requestparam("billtype")  string billtype) {
        log.info("申请交易账单,billdate:{},billtype:{}", billdate,billtype);
        string url = wechatpayconfig.getbaseurl().concat(wechatpayurlenum.trade_bills.gettype())
                .concat("?bill_date=").concat(billdate).concat("&bill_type=").concat(billtype);
        string res = wechatpayrequest.wechathttpget(url);
        log.info("查询退款订单结果:{}",res);
        map<string, object> resmap = jsonobject.parseobject(res, new typereference<map<string, object>>(){});
        string downloadurl = resmap.get("download_url").tostring();
        return downloadurl;
    }
    /**
     *
     * @param billdate 格式yyyy-mm-dd 仅支持三个月内的账单下载申请,如果传入日期未为当天则会出错
     * @param accounttype 分为:basic、operation、fees
     * basic:基本账户
     * operation:运营账户
     * fees:手续费账户
     * @return
     */
    @apioperation(value = "申请资金账单-统一接口", notes = "申请资金账单-统一接口")
    @apioperationsupport(order = 40)
    @getmapping("/fundflowbill")
    public string fundflowbill(@requestparam("billdate") string billdate, @requestparam("accounttype")  string accounttype) {
        log.info("申请交易账单,billdate:{},accounttype:{}", billdate,accounttype);
        string url = wechatpayconfig.getbaseurl().concat(wechatpayurlenum.fund_flow_bills.gettype())
                .concat("?bill_date=").concat(billdate).concat("&account_type=").concat(accounttype);
        string res = wechatpayrequest.wechathttpget(url);
        log.info("查询退款订单结果:{}",res);
        map<string, object> resmap = jsonobject.parseobject(res, new typereference<map<string, object>>(){});
        string downloadurl = resmap.get("download_url").tostring();
        return downloadurl;
    }
    @apioperation(value = "下载账单-统一接口", notes = "下载账单-统一接口")
    @apioperationsupport(order = 45)
    @getmapping("/downloadbill")
    public void downloadbill(string downloadurl) {
        log.info("下载账单,下载地址:{}",downloadurl);
        httpget httpget = new httpget(downloadurl);
        httpget.addheader("accept", "application/json");
        closeablehttpresponse response =null;
        try {
            //使用wxpayclient发送请求得到响应
            response =  wxpaynosignclient.execute(httpget);
            string body = entityutils.tostring(response.getentity());
            int statuscode = response.getstatusline().getstatuscode();
            if (statuscode == 200 || statuscode == 204) {
                log.info("下载账单,返回结果 = "  body);
            } else {
                throw new runtimeexception("下载账单异常, 响应码 = "  statuscode ", 下载账单返回结果 = "  body);
            }
            // todo 将body内容转为excel存入本地或者输出到浏览器,演示存入本地
            writestringtofile(body);
        }catch (exception e){
            throw new runtimeexception(e.getmessage());
        }
        finally {
            if(response!=null) {
                try {
                    response.close();
                } catch (ioexception e) {
                    e.printstacktrace();
                }
            }
        }
    }
    private void writestringtofile(string body) {
        filewriter fw = null;
        try {
            string filepath = "c:\\users\\lhz12\\desktop\\wxpay.txt";
             fw = new filewriter(filepath, true);
            bufferedwriter  bw = new bufferedwriter(fw);
             bw.write(body);
        } catch (exception e) {
            e.printstacktrace();
        }finally {
            try {
                if(fw!=null) {
                    fw.close();
                }
            } catch (ioexception e) {
                e.printstacktrace();
            }
        }
    }
}

本文标签: