微信H5支付(MWEB)、掃碼支付(NATIVE)、APP支付(APP)

1.下單方法

    @ResponseBody
    @ApiOperation("微信瀏支付/(H5)")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "productId", value = "產品ID", required = true),
            @ApiImplicitParam(name = "client", value = "下單客戶端:0=h5、1=pc、2.app", required = true),
            @ApiImplicitParam(name = "passagewayId", value = "支付通道ID", required = true)
    })
    @ApiResponses(value = {
            @ApiResponse(code = 500, message = "系統錯誤"),
            @ApiResponse(code = 200, message = "{code:響應碼,message:描述,data:參考model類{}}", response = BaseResult.class)})
    @GetMapping(value = "wxPayH5")
    public BaseResult wxPayH5(HttpServletRequest request, BeanOrder beanOrder) {
        try {
            Map<String, Object> result = new HashMap();
            result.put("success", false);
            Long userId = null;
            BeanProduct product = productService.findBeanProductId(beanOrder.getProductId());
            beanOrder.setAmount(product.getPrice());
            if (EmptyUtil.isEmpty(beanOrder.getUserId())) {
                userId = accessTokenService.getAppUserId(request);
                beanOrder.setUserId(userId);
            } else {
                userId = beanOrder.getUserId();
            }
            BigDecimal order_price = beanOrder.getAmount().multiply(new BigDecimal(100));

            logger.info("1.發起微信瀏支付=========================================================》》productId=" + beanOrder.getProductId() + "=======》》 userId" + userId);
            if (EmptyUtil.isEmpty(beanOrder) || EmptyUtil.isEmpty(beanOrder.getAmount())
                    || EmptyUtil.isEmpty(beanOrder.getProductId())
                    || EmptyUtil.isEmpty(beanOrder.getUserId())
                    || EmptyUtil.isEmpty(beanOrder.getClient())
                    || EmptyUtil.isEmpty(beanOrder.getPassagewayId())) {
                return new BaseResult(ResultEnum.ZC_120000.getCode(), "參數缺失", null);
            }

            // 價格 注意:價格的單位是分
            User user = userService.requiresNewSelectByPrimaryKey(userId);
            beanOrder.setPassagewayId(beanOrder.getPassagewayId());
            beanOrder.setCount(product.getNumber());
            beanOrder.setMobile(user.getMobile());
            beanOrder.setAmount(order_price);
            beanOrder.setStatus(1);
            beanOrder.setOrderType(1);

            //下單客戶端:0=h5、1=pc、2=app
            String client = "MWEB";
            if (beanOrder.getClient().intValue() == 1) {
                client = "NATIVE";
            } else if (beanOrder.getClient().intValue() == 2) {
                client = "APP";
            }

            logger.info("2.拼接統一下單地址參數===================================================》》\n");
            Map<String, String> packageParams = AuthUtil.getPackageParams(request);
            BaseResult baseResult = beanOrderService.addBeanOrder(beanOrder);
            String orderNumber = AuthUtil.getOrderNumber(beanOrder.getOrderId().toString());
            beanOrder.setOrderNumber(orderNumber);
            BaseResult baseResult1 = beanOrderService.modifyBeanOrder(beanOrder);
            if (!ResultEnum.QX_000000.getCode().equals(baseResult.getCode()) ||
                    !ResultEnum.QX_000000.getCode().equals(baseResult1.getCode()) ) {
                return new BaseResult(ResultEnum.ZC_120000.getCode(), "支付失敗", null);
            }
            packageParams.put("out_trade_no", orderNumber);                                                                 // 商戶系統內部的訂單號,32個字符
            packageParams.put("appid", beanOrder.getClient().intValue() == 2 ? AuthUtil.APP_APPID : AuthUtil.APPID);    // 微信分配的公衆賬號ID(企業號corpid即爲此appId)
            packageParams.put("total_fee", beanOrder.getAmount().intValue()+"");                                            // 支付金額,單位分
            packageParams.put("detail", JSON.toJSONString(beanOrder));        // 商品詳情
            packageParams.put("notify_url", AuthUtil.NOTIFY_URL + "?orderId=" + beanOrder.getOrderId());                // 此路徑是微信服務器調用支付結果通知路徑隨意寫
            packageParams.put("trade_type", client);                          // 支付類型
            String sign = WXPayUtil.generateSignature(packageParams, AuthUtil.PATERNERKEY);
            packageParams.put("sign", sign);                                  // 簽名
            String requestXML = WXPayUtil.mapToXml(packageParams);          // 將所有參數(map)轉xml格式

            logger.info("3.發送post請求統一下單接口返回預支付id:prepay_id  ======================》》\n" + requestXML);
            String resXml = HttpRequest.httpsRequest(AuthUtil.UFDODER_URL, "POST", requestXML);
            Map<String, String> map = WXPayUtil.xmlToMap(resXml);

            logger.info("4.響應參數 =============================================================》》\n" + resXml);
            String urlString = URLEncoder.encode((beanOrder.getClient().intValue() == 2 ? AuthUtil.BACK_URL : AuthUtil.BACK_URL_H5) + packageParams.get("nonce_str"), "GBK");

            Map<String, String> retsult = new HashMap<>();
            if ("SUCCESS".equals((String) map.get("result_code"))) {
                if (beanOrder.getClient().intValue() == 2) {
                    retsult.put("appid", AuthUtil.APP_APPID);
                    retsult.put("partnerid", AuthUtil.MCHID);             //微信商戶賬號
                    retsult.put("prepayid", map.get("prepay_id"));          //預付訂單id
                    retsult.put("package", "Sign=WXPay");              //擴展字段
                    retsult.put("noncestr", map.get("nonce_str"));          //自定義不重複的長度不長於32位
                    retsult.put("timestamp", DateUtils.getCurrentTimeStamp());
                    String signAgain = WXPayUtil.generateSignature(retsult, AuthUtil.PATERNERKEY);
                    retsult.put("sign", signAgain);                         // 簽名
                    logger.info("5.請求結束 =============================================================》》\n" + retsult);
                    return new BaseResult(ResultEnum.QX_000000.getCode(), retsult);
                } else {
                    String url = beanOrder.getClient() == 1 ? map.get("code_url") : map.get("mweb_url");
                    String retsult_url = url + "&redirect_url=" + urlString;
                    retsult.put("url", retsult_url);
                    retsult.put("orderNumber", packageParams.get("out_trade_no"));
                    logger.info("5.請求結束 =============================================================》》\n" + retsult);
                    return new BaseResult(ResultEnum.QX_000000.getCode(), retsult);
                }
            } else {
                return new BaseResult(ResultEnum.ZC_120000.getCode(), "請求異常", null);
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            return new BaseResult(ResultEnum.ZC_120000.getCode());
        }
    }

2.回調方法

    @ResponseBody
    @ApiOperation("支付完成的回調函數")
    @RequestMapping("/payNotify")
    public void payNotify(HttpServletRequest request, HttpServletResponse response) {
        try {
            String xmlMsg = AuthUtil.readData(request);
            Map<String, String> params = WXPayUtil.xmlToMap(xmlMsg);
            logger.info("1.支付回調=========================================================》》" + xmlMsg);
            BeanOrder beanOrder = beanOrderService.findBeanOrderId(Long.valueOf(params.get("orderId")));
            User user = userService.requiresNewSelectByPrimaryKey(beanOrder.getUserId());
            beanOrderService.payNotify(params, response, beanOrder, user);
        } catch (Exception e) {
            logger.error("充值異常", e);
        }
    }


    public void payNotify(Map<String,String> params, HttpServletResponse response,BeanOrder beanOrder,User user) throws BusinessException {
        try {
            Boolean czflg = true;
            logger.info("2.回調參數pay notice====================================================》》 \n"+params);
            String resXml = "";
            logger.info("3.處理業務開始========================================================》》\n");
            if ("SUCCESS".equals((String) params.get("result_code"))) {
                logger.info("4.支付成功========================================================》》\n" + params);
                try {
                    synchronized (czflg) {
                        // 1.訂單信息
                        beanOrder.setLogContent(params.toString());
                        beanOrder.setOrderNumber(params.get("out_trade_no"));
                        BaseResult result = beanOrderService.addBeanOrder(beanOrder, user);
                        logger.info("5.支付結果========================================================》》\n" + result.toString());
                    }
                } catch (Exception e) {
                    logger.error(e.getMessage(),e);
                }
                logger.info("6.通知微信.異步確認成功.必寫.不然會一直通知後臺.八次之後就認爲交易失敗了====》》\n");
                resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
            } else {
                beanOrder.setStatus(5);
                beanOrder.setOrderNumber(params.get("out_trade_no"));
                beanOrderService.modifyBeanOrder(beanOrder);
                resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[充值失敗]]></return_msg>" + "</xml> ";
            }
            BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
            out.write(resXml.getBytes());
            out.flush();
            out.close();
        } catch (Exception e) {
            logger.error("充值異常:" + e.getMessage(),e);
            throw new BusinessException("充值異常");
        }
    }

3.訂單類

package com.fushui.business.kbeans.model;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import net.sf.jsqlparser.statement.select.First;
import org.hibernate.validator.constraints.Length;

import java.util.Date;
import java.math.BigDecimal;

/**
 * K豆訂單(bean_order)
 * 
 * @author Evan
 * @version 1.0.0 2020-02-25
 */
@ApiModel(description = "K豆訂單")
public class BeanOrder implements java.io.Serializable {
    /** 版本號 */
    private static final long serialVersionUID = -5339897937895258326L;

    @Length(max=10,message = "最大長度爲10",groups = {First.class})
    @ApiModelProperty(value = "")
    private Long orderId;

    @ApiModelProperty(value = "產品Id")
    private Long productId;

    @ApiModelProperty(value = "購買人id")
    private Long userId;

    @ApiModelProperty(value = "支付通道ID beans_passageway_set")
    private Integer passagewayId;

    @ApiModelProperty(value = "訂單類型ID order_type")
    private Integer orderType;

    @ApiModelProperty(value = "下單客戶端:0=h5、1=pc")
    private Integer client;

    @ApiModelProperty(value = "訂單類型名稱")
    private String orderTypeName;

    @ApiModelProperty(value = "聯繫方式")
    private String mobile;

    @ApiModelProperty(value = "商家訂單號")
    private String orderNumber;

    @ApiModelProperty(value = "返回日誌")
    private String logContent;

    @ApiModelProperty(value = "支付金額/元")
    private BigDecimal amount;

    @ApiModelProperty(value = "購買數量")
    private Integer count;

    @ApiModelProperty(value = "訂單狀態")
    private Integer status;

    @ApiModelProperty(value = "訂單狀態")
    private String statusName;

    @ApiModelProperty(value = "創建時間")
    private Date createTime;

    @ApiModelProperty(value = "通道名稱")
    private String passagewayName;

    @ApiModelProperty(value = "產品名稱")
    private String title;


}


4.工具類方法

package com.fushui.common.util.wxpay;

import com.alibaba.fastjson.JSON;
import com.github.wxpay.sdk.WXPayUtil;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;

/**
 * @ClassName AuthUtil
 * @Description TODO
 * @Author Evan
 * @Date 2020/3/18 16:18
 * @Version 1.0
 **/

public class AuthUtil {

    public static final String APPID = "******************";//平臺ID
    public static final String APPSECRET = "";              //平臺密鑰
    public static final String MCHID = "**********";        //商家ID
    public static final String PATERNERKEY = "****************";                      // 商家密鑰
    public static String NOTIFY_URL = "https://testm.tanxingk.com/api/wxpay/payNotify";      // 回調地址
    public static String NOTIFY_URL_H5 = "https://testm.tanxingk.com/api/wxpay/payNotify";   // 微信支付h5 回調地址
    public static String BODY = "彈性K線-K線資訊";                                                      // 商品名稱
    public static String UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";                // 請求地址
    public static String ORDERQUERY = "https://api.mch.weixin.qq.com/pay/orderquery";                   // 微信支付V2賬單查詢接口

    /**
     * @Author Evan
     * @Description //TODO 支付基本參數
     * @Date 14:43 2020/5/8
     * @Param [request]
     * @return java.util.Map<java.lang.String,java.lang.String>
     **/
    public static Map<String, String> getPackageParams(HttpServletRequest request){
        Map<String, String> packageParams = new TreeMap();
        String ip =  AuthUtil.getIp(request);
        packageParams.put("appid", AuthUtil.APPID);                     //  微信分配的公衆賬號ID(企業號corpid即爲此appId)
        packageParams.put("mch_id", AuthUtil.MCHID);                    // 微信支付分配的商戶號
        packageParams.put("body", AuthUtil.BODY);                       // 商品描述
        packageParams.put("nonce_str", WXPayUtil.generateNonceStr());   // 生成簽名的時候需要你自己設置隨機字符串
        packageParams.put("out_trade_no", UUID.randomUUID().toString().replaceAll("-", "")); // 商戶系統內部的訂單號,32個字符
        packageParams.put("spbill_create_ip",ip);                        // 必須傳正確的用戶端IP
        packageParams.put("scene_info", "{\"h5_info\": {\"type\":\"Wap\",\"wap_url\": \"https://testm.tanxingk.com/kcoin/buy\",\"wap_name\": \"彈性K線\"}}");
        return packageParams;
    }

    public static String readData(HttpServletRequest request) {
        BufferedReader br = null;
        try {
            StringBuilder result = new StringBuilder();
            br = request.getReader();
            for (String line; (line = br.readLine()) != null; ) {
                if (result.length() > 0) {
                    result.append("\n");
                }
                result.append(line);
            }
            return result.toString();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (br != null)
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }

    /**
     * 獲取用戶真實IP地址,不使用request.getRemoteAddr();的原因是有可能用戶使用了代理軟件方式避免真實IP地址。
     * 可是,如果通過了多級反向代理的話,X-Forwarded-For的值並不止一個,而是一串IP值,究竟哪個纔是真正的用戶端的真實IP呢?
     * 答案是取X-Forwarded-For中第一個非unknown的有效IP字符串
     * @param request
     * @return
     */
    public static String getIp(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
            if("127.0.0.1".equals(ip)||"0:0:0:0:0:0:0:1".equals(ip)){
                //根據網卡取本機配置的IP
                InetAddress inet=null;
                try {
                    inet = InetAddress.getLocalHost();
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }
                ip= inet.getHostAddress();
            }
        }
        return ip;
    }

    public static String getInterfaceIp(HttpServletRequest request) {
        return getIp(request) + ":" + request.getServerPort();
    }
}

5.前端代碼

async clickSurePay() {
            if (this.choosePayId === '') {
                this.$showToast('請選擇支付方式');
            }
            console.log(this.choosePayId);
            console.log(this.chooseProductId);
            // this.$showToast('功能開發中,敬請期待!');
            const [_, resp] = await getAsync('/wxpay/wxPayH5', {
                productId: this.chooseProductId,
                passagewayId: this.choosePayId
            });
            console.log(resp);
            if (resp?.code === '000000') {
                const data = resp.data || {};
                // 調起微信支付
                console.log(data);
                location.href = data;
            } else {
            }
        };

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章