微信JSAPI 统一下单 退款 回调,项目源代码可用

最近做微信支付,踩了一点坑。就留下代码防止各位入坑。哈不多说,上代码

service

package com.sjc.wx.service;

import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Map;

/**
 * 微信下单功能接口
 * @param
 */
@Service
public interface WxPayService {
    Map<String,String> pay(String openId,String body, BigDecimal total_fee, String orderId, String shopId,String time_expire,String url);

    Map<String, String> refunds(String orderSn, BigDecimal amount, BigDecimal orderAmount, String refundSn);
}

实现层

package com.sjc.wx.service.impl;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayConfig;
import com.github.wxpay.sdk.WXPayRequest;
import com.github.wxpay.sdk.WXPayUtil;
import com.sjc.wx.service.WxPayService;
import com.sjc.wx.config.MyConfig;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;

@Service
public class WxPayServiceImp implements WxPayService {
    @Resource
    private WXPayConfig config;
    @Override
    /**
     * body商品描述
     * total_fee总金额 单位分
     * orderId订单号
     * shopId商品id
     * body 商品信息
     * time_expire 最晚支付时间
     * url 回调地址
     */
    public Map<String, String> pay(String openId,String body, BigDecimal total_fee, String orderId, String shopId,String time_expire,String url) {
        Map resultMap=new HashMap();
        MyConfig config = null;
        WXPay wxpay =null;
        //获取客户端的ip地址
        //获取本机的ip地址
        InetAddress addr = null;
        try {
            config = new MyConfig();
            wxpay= new WXPay(config);
            addr = InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }catch (Exception e) {
            e.printStackTrace();
        }
        String spbill_create_ip = addr.getHostAddress();
        //统一下单接口参数
        HashMap<String, String> data = new HashMap<String, String>();
        /*data.put("appid",config.getAppID());
        data.put("mch_id",config.getMchID());
        //生成的随机字符串
        String nonce_str = WXPayUtil.generateNonceStr();
        data.put("nonce_str", nonce_str);*/

        data.put("body", body);//商品描述
        data.put("out_trade_no",orderId); //商户订单号
        //支付金额,需要转成字符串类型,否则后面的签名会失败
        data.put("total_fee",(total_fee.multiply(new BigDecimal(100)).intValue()+""));
        data.put("spbill_create_ip",spbill_create_ip);
        data.put("notify_url",url);
        data.put("trade_type","JSAPI");
        data.put("product_id",shopId);
        data.put("openid", openId);
        if(null!=time_expire && !"".equals(time_expire)){
            System.out.println("订单最晚支付时间:"+time_expire);
            data.put("time_expire",time_expire);
        }
        try {
            Map<String, String> rMap = wxpay.unifiedOrder(data);
            System.out.println("统一下单接口返回: " + rMap);
            String return_code = (String) rMap.get("return_code");
            String result_code = (String) rMap.get("result_code");
            String nonceStr = WXPayUtil.generateNonceStr();

            if ("SUCCESS".equals(return_code) && return_code.equals(result_code)) {
                String prepayid = rMap.get("prepay_id");
                resultMap.put("appId", config.getAppID());//二次签名一定要这几个参数一个不能删否则会报签名验证失败
                resultMap.put("package", "prepay_id="+prepayid);
                resultMap.put("signType", "MD5");
                //这边要将返回的时间戳转化成字符串,不然小程序端调用wx.requestPayment方法会报签名错误
                resultMap.put("nonceStr",nonceStr);
                //Long timeStamp = System.currentTimeMillis() / 1000;
                Long timeStamp = System.currentTimeMillis() / 1000;
                resultMap.put("timeStamp",timeStamp.toString());
                //再次签名,这个签名用于小程序端调用wx.requesetPayment方法
                String sign = WXPayUtil.generateSignature(resultMap, config.getKey());
                resultMap.put("paySign", sign);
                System.out.println("生成的签名paySign : "+ sign);
                return resultMap;
            }else{
                  return resultMap;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return  resultMap;
        }
    }

    /**
     * @param orderSn 支付单号
     * @param amount 退款金额
     * @param orderAmount 订单金额
     * @param refundSn 退单号
     * @return
     */
    @Override
    public Map<String, String> refunds(String orderSn, BigDecimal amount, BigDecimal orderAmount, String refundSn) {
        Map<String, String> parameterMap = new TreeMap<>();
        Map<String, String> map = new HashMap<>();
        parameterMap.put("appid", config.getAppID());//公众账号ID
        parameterMap.put("mch_id", config.getMchID());//商户号
        parameterMap.put("nonce_str", DigestUtils.md5Hex(UUID.randomUUID() + RandomStringUtils.randomAlphabetic(30)));//随机字符串
        parameterMap.put("out_refund_no", refundSn);//退单号
        //parameterMap.put("out_refund_no", "YC0780012019071313132000001001");//退单号
        parameterMap.put("out_trade_no", orderSn);//支付单号
        //parameterMap.put("out_trade_no", "YC0780012019071313132000001");//支付单号
        parameterMap.put("total_fee", orderAmount.intValue() + "");//订单金额
        parameterMap.put("refund_fee", amount.intValue() + "");//退款金额
        //parameterMap.put("total_fee", "1");//订单金额
        //parameterMap.put("refund_fee", "1");//退款金额
        String sign = null;
        Map<String, String> resultMap;
        try {
            sign = WXPayUtil.generateSignature(parameterMap, config.getKey());
            parameterMap.put("sign", sign);
            String xml = WXPayUtil.mapToXml(parameterMap);
            WXPayRequest wxPayRequest = new WXPayRequest(config);
            String result = wxPayRequest.requestOnce("api.mch.weixin.qq.com","/secapi/pay/refund","",xml,config.getHttpConnectTimeoutMs(),config.getHttpReadTimeoutMs(),true);
            resultMap = WXPayUtil.xmlToMap(result) ;
            String return_code = resultMap.get("return_code");
            String return_msg = resultMap.get("return_msg");
            String result_code = resultMap.get("result_code");
            if ("SUCCESS".equals(return_code) && result_code != null && "SUCCESS".equals(result_code)) {
                map.put("return_code", return_code);
                map.put("return_msg", return_msg);
                map.put("result_code", result_code);
                map.put("out_refund_no", resultMap.get("out_refund_no"));
                map.put("refund_fee", resultMap.get("refund_fee"));
            } else {
                map.put("return_code", return_code);
                map.put("return_msg", return_msg);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }
}

 

支付调用

/**'
     * 缴纳押金支付
     * @param
     * @param total_fee
     * @param userName
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/payWxDeposit", method = RequestMethod.POST)
    public R payWxDeposit(String openId, BigDecimal total_fee, String userName ) {
        GenerateBill generateBill = new GenerateBill();
        BaseResponse<Map> response = null;
        if (null == total_fee) {
            return R.error("支付失败,参数不正确");
        }
        String orderId = generateBill.generateContractNo("YJ", (((int) Math.random()) * 1000));
        Map<String, String> map = wxPayService.pay(openId, "缴纳押金", total_fee, orderId, "YJ", "","https://你的ip/wx/pay/depositCallback");
        map.put("orderId", orderId);
        redisTemplate.opsForValue().set(orderId,userName,3600,TimeUnit.SECONDS);
        System.out.println(redisTemplate.opsForValue().get(orderId));
        return R.ok("调用支付缴纳押金成功", map);
    }

    @ResponseBody
    @RequestMapping(value = "/payCoupon", method = RequestMethod.POST)
    public R payCoupon(String openId, BigDecimal total_fee, String userName,CouponDO couponDO) {
        GenerateBill generateBill = new GenerateBill();
        BaseResponse<Map> response = null;
        if (null == total_fee) {
            return R.error("支付失败,参数不正确");
        }
        String orderId = generateBill.generateContractNo("coupon", ((int) Math.random() * 1000));
        Map<String, String> map = wxPayService.pay(openId, "购买时间券", total_fee, orderId, "coupon", "","https://你的回调ip/你的回调方法/couponCallback");
        map.put("orderId", orderId);
        redisTemplate.opsForValue().set(orderId,couponDO,3600,TimeUnit.SECONDS);
        System.out.println(redisTemplate.opsForValue().get(orderId));
        return R.ok("调用购买时间券支付接口成功", map);
    }

 

回调 

@RequestMapping("depositCallback")
    public String depositCallback(HttpServletRequest request, HttpServletResponse response) {
        //System.out.println("微信支付成功,微信发送的callback信息,请注意修改订单信息");
        InputStream is = null;
        try {
            try {
                is = request.getInputStream();//获取请求的流信息(这里是微信发的xml格式所有只能使用流来读)
            } catch (IOException e) {
                e.printStackTrace();
            }
            StringBuilder sb = new StringBuilder();
            String line = "";
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            while (true) {
                try {
                    if (!((line = br.readLine()) != null)) break;
                } catch (IOException e) {
                    e.printStackTrace();
                }
                sb.append(line);
            }

            String xml = sb.toString();
            Map<String, String> notifyMap = null;//将微信发的xml转map
            try {
                notifyMap = WXPayUtil.xmlToMap(xml);
            } catch (Exception e) {
                e.printStackTrace();
            }

            if (notifyMap.get("return_code").equals("SUCCESS")) {
                if (notifyMap.get("result_code").equals("SUCCESS")) {
                    String ordersSn = notifyMap.get("out_trade_no");//商户订单号
                    String amountpaid = notifyMap.get("total_fee");//实际支付的订单金额:单位 分
                    BigDecimal amountPay = (new BigDecimal(amountpaid).divide(new BigDecimal("100"))).setScale(2);//将分转换成元-实际支付金额:元
                    //String openid = notifyMap.get("openid");  //如果有需要可以获取
                    //String trade_type = notifyMap.get("trade_type");
                    /*以下是自己的业务处理------仅做参考
                     * 更新order对应字段/已支付金额/状态码
                     */
                    Map map = new HashMap();
                    map.put("orderId", ordersSn);
                    List<DepositDO> list = depositService.list(map);
                    System.out.println(" wxPayController depositCallback 方法 313行代码 进入成功");
                    if (list.size() > 0) {
                        System.out.println(" wxPayController depositCallback 方法 313行代码 进入if成功");
                        //告诉微信服务器收到信息了,不要在调用回调action了========这里很重要回复微信服务器信息用流发送一个xml即可
                        response.getWriter().write("<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>");
                        is.close();
                    } else {
                        System.out.println(" wxPayController depositCallback 方法 320行代码 进入else成功");
                        String userName = redisTemplate.opsForValue().get(ordersSn).toString();
                        map.put("userName",userName);
                        List<MemberDO> memberDO = memberService.list(map);
                        DepositDO depositDO = new DepositDO();
                        depositDO.setUserId(memberDO.get(0).getUserId());
                        depositDO.setMoney(depositConfigService.list(new HashMap<>()).get(0).getMoney());
                        depositDO.setTotalMoney(memberDO.get(0).getDepositNum().add(depositDO.getMoney()));
                        depositDO.setOrderId(ordersSn);
                        depositDO.setMoneyState(0);
                        depositService.save(depositDO);
                        //告诉微信服务器收到信息了,不要在调用回调action了========这里很重要回复微信服务器信息用流发送一个xml即可
                        response.getWriter().write("<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>");
                        is.close();
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    @RequestMapping("couponCallback")
    public String couponCallback(HttpServletRequest request, HttpServletResponse response) {
        //System.out.println("微信支付成功,微信发送的callback信息,请注意修改订单信息");
        InputStream is = null;
        try {
            try {
                is = request.getInputStream();//获取请求的流信息(这里是微信发的xml格式所有只能使用流来读)
            } catch (IOException e) {
                e.printStackTrace();
            }
            StringBuilder sb = new StringBuilder();
            String line = "";
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            while (true) {
                try {
                    if (!((line = br.readLine()) != null)) break;
                } catch (IOException e) {
                    e.printStackTrace();
                }
                sb.append(line);
            }

            String xml = sb.toString();
            Map<String, String> notifyMap = null;//将微信发的xml转map
            try {
                notifyMap = WXPayUtil.xmlToMap(xml);
            } catch (Exception e) {
                e.printStackTrace();
            }

            if (notifyMap.get("return_code").equals("SUCCESS")) {
                if (notifyMap.get("result_code").equals("SUCCESS")) {
                    String ordersSn = notifyMap.get("out_trade_no");//商户订单号
                    String amountpaid = notifyMap.get("total_fee");//实际支付的订单金额:单位 分
                    BigDecimal amountPay = (new BigDecimal(amountpaid).divide(new BigDecimal("100"))).setScale(2);//将分转换成元-实际支付金额:元
                    //String openid = notifyMap.get("openid");  //如果有需要可以获取
                    //String trade_type = notifyMap.get("trade_type");
                    /*以下是自己的业务处理------仅做参考
                     * 更新order对应字段/已支付金额/状态码
                     */
                    Map map = new HashMap();
                    map.put("orderId", ordersSn);
                    List<CouponDO> couponDOList = couponService.list(map);
                    System.out.println(" wxPayController couponCallback 方法 388行代码 进入成功");
                    if (couponDOList.size() > 0) {
                        //告诉微信服务器收到信息了,不要在调用回调action了========这里很重要回复微信服务器信息用流发送一个xml即可
                        response.getWriter().write("<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>");
                        is.close();
                    } else {
                        System.out.println(" wxPayController couponCallback 方法 394行代码 进入else成功");
                        CouponDO coupon = (CouponDO) redisTemplate.opsForValue().get(ordersSn);
                        Date date = new Date();
                        MemberDO memberDO = memberService.get(coupon.getUserId());
                        int Totalcount = memberDO.getTimeCoupon();
                        Totalcount += coupon.getCount();
                        coupon.setTotalCount(Totalcount);
                        coupon.setTime(date);
                        coupon.setState(0);
                        coupon.setIntype(0);
                        coupon.setInstr("+");
                        coupon.setDesc("充值");
                        if(couponService.save(coupon)>0){
                            memberDO.setTimeCoupon(Totalcount);
                            memberService.update(memberDO);
                        }
                        //告诉微信服务器收到信息了,不要在调用回调action了========这里很重要回复微信服务器信息用流发送一个xml即可
                        response.getWriter().write("<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>");
                        is.close();
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

 

退款 

Map<String,String> resultMap = wxPayService.refunds(data.getOrderId(),data.getMoney().multiply(new BigDecimal(""+100)),data.getMoney().multiply(new BigDecimal(""+100))
						,data.getOrderId()+"OUT");
				String return_code = resultMap.get("return_code");
				String return_msg = resultMap.get("return_msg");
				String result_code = resultMap.get("result_code");
				if ("SUCCESS".equals(return_code) && result_code != null && "SUCCESS".equals(result_code)) {
					DepositDO depositDO = new DepositDO();
					depositDO.setId(data.getId());
					depositDO.setUpdateDate(date);
					depositDO.setTotalMoney(count);
					depositDO.setState(1);//设置状态为体现
					depositDO.setMoneyState(1);//设置状态为体现
					depositService.update(depositDO);
					memberService.update(memberDO);
					sp.setTotalMoney(count);//更新押金表总金额
					spService.update(sp);//退押金审批表数据更新
					return R.ok("退款成功");
				}else{
					TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
					return  R.error("微信退款失败");
				}

 

 

退款方法可直接用

/**
 * 支付交易订单号,amount退款金额,orderAmount订单金额,refundSn退货单号

 * @return
 * @throws Exception
 */
/*@ResponseBody
@PostMapping("refunds")
public Map<String, Object> refunds(String orderSn, BigDecimal amount, BigDecimal orderAmount, String refundSn)
        throws Exception {
    Map<String, String> parameterMap = new TreeMap<>();
    parameterMap.put("appid", config.getAppID());//公众账号ID
    parameterMap.put("mch_id", config.getMchID());//商户号
    parameterMap.put("nonce_str", DigestUtils.md5Hex(UUID.randomUUID() + RandomStringUtils.randomAlphabetic(30)));//随机字符串
    parameterMap.put("out_refund_no", refundSn);//退单号
    //parameterMap.put("out_refund_no", "YC0780012019071313132000001001");//退单号
    parameterMap.put("out_trade_no", orderSn);//支付单号
    //parameterMap.put("out_trade_no", "YC0780012019071313132000001");//支付单号
    parameterMap.put("total_fee", orderAmount.intValue() + "");//订单金额
    parameterMap.put("refund_fee", amount.intValue() + "");//退款金额
    //parameterMap.put("total_fee", "1");//订单金额
    //parameterMap.put("refund_fee", "1");//退款金额
    String sign = WXPayUtil.generateSignature(parameterMap, config.getKey());
    parameterMap.put("sign", sign);
    String xml = WXPayUtil.mapToXml(parameterMap);
    WXPayRequest wxPayRequest = new WXPayRequest(config);
    String result = wxPayRequest.requestOnce("api.mch.weixin.qq.com","/secapi/pay/refund","",xml,config.getHttpConnectTimeoutMs(),config.getHttpReadTimeoutMs(),true);
    Map<String, String> resultMap = WXPayUtil.xmlToMap(result) ;

    String return_code = resultMap.get("return_code");
    String return_msg = resultMap.get("return_msg");
    String result_code = resultMap.get("result_code");
    Map<String, Object> map = new HashMap<>();
    if ("SUCCESS".equals(return_code) && result_code != null && "SUCCESS".equals(result_code)) {
        map.put("return_code", return_code);
        map.put("return_msg", return_msg);
        map.put("result_code", result_code);
        map.put("out_refund_no", resultMap.get("out_refund_no"));
        map.put("refund_fee", resultMap.get("refund_fee"));
    } else {
        map.put("return_code", return_code);
        map.put("return_msg", return_msg);
    }
    return map;
}*/

 

我继承了一个抽象方法 这个是微信配置

 

package com.sjc.wx.config;


import com.github.wxpay.sdk.IWXPayDomain;
import com.github.wxpay.sdk.WXPayConfig;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.InputStream;

/**
 * 微信支付配置类填写重要信息
 */
@Component
public class MyConfig extends WXPayConfig {

    private byte[] certData;
    //private InputStream inputStream;

    public MyConfig(){
        //   String certPath = "classpath:/resources/apiclient_cert.p12";

        //     String certPath = Objects.requireNonNull(Token.class.getClassLoader().getResource("apiclient_cert.p12")).getPath();



        //   String property = System.getProperties().getProperty("os.name");
//        if (!property.startsWith("W")) {
//            certPath = "/usr/local/app/apiclient_cert.p12";
//        }
//        File file = new File(certPath);
//        //  Resource resource = new FileSystemResourceLoader().getResource("classpath:apiclient_cert.p12");
//        //String path = resource.getURL().getPath();
//        InputStream certStream = new FileInputStream(file);
//        this.certData = new byte[(int) file.length()];
//        certStream.read(this.certData);
//        certStream.close();
    }

    @Override
    public String getAppID() {
        return "你的微信商户appid需要填写";
    }

    @Override
    public String getMchID() {
        return "你的微信支付商户id需要填写";
    }

    @Override
    public String getKey() {
        return "apikey需要的";
    }

    public String getOpenId() {
        return "";//这个不需要可以弄做测试
    }
    /**
        这个是放cert安全证书的    
    */
    @Override
    public InputStream getCertStream() {
        org.springframework.core.io.ClassPathResource classPathResource = new ClassPathResource("apiclient_cert.p12");
        //    String certPath = classPathResource.getPath();
        try {
            return  classPathResource.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public int getHttpConnectTimeoutMs() {
        return 8000;
    }

    @Override
    public int getHttpReadTimeoutMs() {
        return 10000;
    }

    @Override
    public IWXPayDomain getWXPayDomain() {
        return new IWXPayDomain() {
            @Override
            public void report(String domain, long elapsedTimeMillis, Exception ex) {
            }

            @Override
            public DomainInfo getDomain(WXPayConfig config) {
                return new DomainInfo("api.mch.weixin.qq.com", false);
            }
        };
    }

    public static void main(String[] args) {
        String path = MyConfig.class.getProtectionDomain().getCodeSource().getLocation().getPath();

        System.out.println(path);
    }

}

 

就到此位置了,我没有做退款回调,如果要做退款加库存的可以做按照以上操作。

如果看到R.OK不知道是啥这里我就简述一下,自己的框架封装我给附上代码,做这个踩了不少坑以上代码参数不要删除,像微信统一下单 第二次签名那别动,动了一直显示签名不对 我之前是没给appid做签名,扫码的时候一直显示这个。。唉。

package com.sjc.common.utils;

import io.swagger.annotations.ApiModel;

import java.util.HashMap;
import java.util.Map;

@ApiModel("公共的返回类")
public class R extends HashMap<String, Object> {
	private static final long serialVersionUID = 1L;

	public R() {
		put("code", 200);
		put("status", "success");
		put("msg", "操作成功");
	}

	public static R error() {
		return error(1, "操作失败");
	}

	public static R error(String msg) {
		return error(500, msg);
	}

	public static R error(int code, String msg) {
		R r = new R();
		r.put("code", code);
		r.put("msg", msg);
		return r;
	}

	public static R ok(String msg) {
		R r = new R();
		r.put("code", 200);
		r.put("msg", msg);
		return r;
	}

	public static R ok(int code,String msg,Map map) {
		R r = new R();
		r.put("code", code);
		r.put("msg", msg);
		r.put("data", map);
		return r;
	}
	public static R ok(String msg,Map map) {
		R r = new R();
		r.put("code", 200);
		r.put("msg", msg);
		r.put("status", "success");
		r.put("data", map);
		return r;
	}

	public static R ok(Map<String, Object> map) {
		R r = new R();
		r.putAll(map);
		return r;
	}

	public static R ok() {
		return new R();
	}

	@Override
	public R put(String key, Object value) {
		super.put(key, value);
		return this;
	}
}

觉得我对您有帮助的话就打赏一下哦,

 

 

具体不懂的话 看下这个博客吧 https://blog.csdn.net/hexiaohua95/article/details/85016270

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值