Java 微信支付接口(统一下单,异步回调,订单退款,取消订单)

论坛 期权论坛 脚本     
匿名技术用户   2020-12-22 14:31   19   0

一.准备工作

APP绑定微信商户平台获取商户id(mchID)、证书(商户后台下载)、支付签名密钥(商户后台设置api密钥)、退款签名密钥(商户后台设置api密钥ipv3)等

1.导入微信支付SDK

<!--微信支付SDK-->
<dependency>
 <groupId>com.github.wxpay</groupId>
 <artifactId>wxpay-sdk</artifactId>
 <version>0.0.3</version>
</dependency>

2.配置微信参数类

@Component
@ConfigurationProperties
@PropertySource("classpath:config/wxconfig.properties") --需自己创建相应的配置文件
@Data
public class WxConfig implements WXPayConfig {

    /**
     * appid
     */
    public String appID;

    /**
     * 商户id
     */
    public String mchID;

    /**
     * 密钥
     */
    public String key;

    /**
     * 退款密钥
     */
    public String refundKey;

    /**
     * 连接超时时间
     */
    public int httpConnectTimeoutMs;

    /**
     * 读取超时时间
     */
    public int httpReadTimeoutMs;

    /**
     * 证书
     */
    public InputStream certStream;

    /**
     * 证书路径
     */
    public String certPath;

    /**
     * 微信异步通知地址
     */
    public String wxNotifyUrl;


    public InputStream getCertStream() {
        byte [] certData=null;
        InputStream certStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(this.getCertPath());
        try {
            certData = IOUtils.toByteArray(certStream);
            certStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return new ByteArrayInputStream(certData);
    }

}

二.统一下单

需注入第一步种的配置类

/*微信统一下单接口*/
    public Map<String, String> wxPayOrder(HashMap param) throws Exception{
        Double price = Double.valueOf(param.get("price").toString());
        String paymentDetailNo = param.get("paymentDetailNo").toString();

        /*转换金额为分*/
        Map<String, String> respData = null;
        BigDecimal totalPrice  = new BigDecimal(price.toString()); //单位是元
        String totalFee = totalPrice.multiply(new BigDecimal(100)).toBigInteger().toString();

        WXPay wxPay = new WXPay(wxconfig, WXPayConstants.SignType.MD5, false);

        /*调起支付一次签名 得到第一个sign进行下单*/
        Map<String,String> data = new TreeMap<>();
        data.put("appid",wxconfig.getAppID());
        data.put("mch_id",wxconfig.getMchID());    //商户号
        data.put("trade_type","APP");                         //支付场景 APP 微信app支付 JSAPI 公众号支付  NATIVE 扫码支付
        data.put("notify_url",wxconfig.getWxNotifyUrl());     //回调地址
        data.put("spbill_create_ip","127.0.0.1");            //终端ip
        data.put("total_fee",totalFee);            //订单总金额
        data.put("fee_type","CNY");                           //默认人民币
        data.put("out_trade_no",paymentDetailNo);        //交易号
        data.put("body","**APP-购买业务");   //购买显示信息
        data.put("nonce_str",WXPayUtil.generateNonceStr());    // 随机字符串小于32位
        String s=WXPayUtil.generateSignature(data, wxconfig.getKey());
        data.put("sign",s);

        /** wxPay.unifiedOrder 这个方法中调用微信统一下单接口 */
        respData = wxPay.unifiedOrder(data);
        if (respData.get("result_code").equals("SUCCESS")){
            //返回给APP端的参数并进行签名得到第二个sign,APP端再调起支付接口
            Map<String,String> repData = new TreeMap<>();
            repData.put("appid",wxconfig.getAppID());
            repData.put("partnerid",wxconfig.getMchID());
            repData.put("prepayid",respData.get("prepay_id"));
            repData.put("package","Sign=WXPay");
            repData.put("noncestr",respData.get("nonce_str"));
            repData.put("timestamp",String.valueOf(System.currentTimeMillis()/1000));
            String sign = WXPayUtil.generateSignature(repData,wxconfig.getKey()); //调用sdk方法签名

            respData.put("sign",sign);
            respData.put("timestamp",repData.get("timestamp"));
            respData.put("package","Sign=WXPay");

     

        }

        return respData;
    }

三.异步回调接口

 /*微信支付异步回调 支付异常的情况回滚并调用退款接口进行退款*/
 @Override
 @Transactional(readOnly = false,  propagation = Propagation.REQUIRES_NEW, rollbackFor=Exception.class)
 public Boolean wxPayNotifyUrl(Map<String, String> map) throws Exception {
  log.info(">>>>微信支付回调...<<<<");

  String out_trade_no = map.get("out_trade_no");
  String return_code = map.get("return_code");

  if(return_code.equals("SUCCESS")){
   log.info(">>>>微信调起成功...<<<<");
   PaymentDetailVo entity = paymentDao.queryPaymentDetail(out_trade_no);

   /*微信重复调起-->校验如果已经支付则直接返回*/
   if(entity.getDetailStatus() == 1){
    return true;
   }

   /*判断sign签名是否一致*/
   if(WXPayUtil.isSignatureValid(map, wxconfig.getKey())){
    log.info(">>>>微信签名通过...<<<<");

    /*校验api返回的金额与流水单的金额是否一致*/
    if(map.get("total_fee") == null){
     log.error("API返回无金额!");
                    /*调用退款接口 */
     wxRefund(out_trade_no,entity.getUserId(), entity.getPaymentStatus());
     throw new Exception("API返回无金额!");
    }
                /*转换金额为元*/
    BigDecimal totalPrice  = new BigDecimal(entity.getPaymentPrice().toString()); //单位是元
    String paymentPrive = totalPrice.multiply(new BigDecimal(100)).toBigInteger().toString();
    String total_fee = map.get("total_fee").toString();
    if(!total_fee.equals(paymentPrive)){
     log.error("API返回的交易金额与流水单的金额不一致,存在假通知的危险!");
     wxRefund(out_trade_no,entity.getUserId(), entity.getPaymentStatus());
     throw new Exception("API返回的交易金额与流水单的金额不一致,存在假通知的危险!");
    }

    HashMap param = new HashMap();
    param.put("no", entity.getPaymentNo());

    /*异步回调成功,修改订单状态*/
    ...


    log.info(">>>>微信异步回调完毕...<<<<");
   }else{
    log.error("API返回的数据签名验证不通过,有可能被第三方篡改!!!");
    wxRefund(out_trade_no,entity.getUserId(), Integer.valueOf(map.get("type").toString()));
    throw new Exception("API返回的数据签名验证不通过,有可能被第三方篡改!!!");
   }
  }else {
   return false;
  }
  return true;
 }

四.退款接口

/*微信退款*/
private Boolean wxDoingRefund(HashMap param) {
  Double price = Double.valueOf(param.get("price").toString());
  String paymentDetailNo = param.get("paymentDetailNo").toString();
  String out_trade_no = param.get("out_trade_no").toString();

  Map<String, String> respData = null;
  BigDecimal totalPrice  = new BigDecimal(price); //此时的单位是元
  String totalFee = totalPrice.multiply(new BigDecimal(100)).toBigInteger().toString();
  try {
   WXPay wxPay = new WXPay(wxconfig, WXPayConstants.SignType.MD5, false);

   Map<String,String> data = new TreeMap<>();
   data.put("appid",wxconfig.getAppID());
   data.put("mch_id",wxconfig.getMchID());    //商户号
   data.put("total_fee",totalFee);            //订单总金额
   data.put("refund_fee",totalFee);           //退款总金额
   data.put("out_trade_no",out_trade_no);     //商户订单号
   data.put("out_refund_no",paymentDetailNo);    //商户退款单号
   data.put("refund_desc","支付异常");
   data.put("nonce_str",WXPayUtil.generateNonceStr()); // 随机字符串小于32位
   String s=WXPayUtil.generateSignature(data, wxconfig.getRefundKey());
   data.put("sign",s);
   /** wxPay.refund 这个方法中调用微信退款接口 */
   respData = wxPay.refund(data);
   if (respData.get("return_code").equals("SUCCESS")){
    log.info(">>>>微信退款成功...>>>>");
    
    return true;

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

五.关闭订单

关闭订单——以下情况需要调用关单接口:
* 商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付;
* 系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口。
* 订单生成后不能马上调用关单接口,最短调用时间间隔为5分钟
/*微信关闭订单*/
public Boolean cloaseOrder(String out_trade_no) throws Exception{
  WXPay wxPay = new WXPay(wxconfig, WXPayConstants.SignType.MD5, false);
  Map<String, String> respData = null;
  Map<String,String> data = new TreeMap<>();
  data.put("appid",wxconfig.getAppID());
  data.put("mch_id",wxconfig.getMchID());        //商户号
  data.put("out_trade_no",out_trade_no);         //商户订单号
  data.put("nonce_str",WXPayUtil.generateNonceStr());        // 随机字符串小于32位
  String s=WXPayUtil.generateSignature(data, wxconfig.getRefundKey());
  data.put("sign",s);
  /** wxPay.cloaseOrder 这个方法中调用微信退款接口 */
  respData = wxPay.closeOrder(data);
  if (respData.get("return_code").equals("SUCCESS")){
   log.info(">>>>微信关闭订单成功...>>>>");
   return true;
  }
  return false;
 }

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:7942463
帖子:1588486
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP