微信APP支付(基於Java實現微信APP支付)


步驟:

  • 導入maven依賴
    <!--微信支付-->
    <dependency>
      <groupId>com.github.wxpay</groupId>
      <artifactId>wxpay-sdk</artifactId>
      <version>0.0.3</version>
    </dependency>
  • 微信支付參數配置
import com.github.wxpay.sdk.WXPayConfig;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

/**
 * 微信支付配置(單例)
 */
public class WXConfigUtil implements WXPayConfig {

    private byte[] certData;
    private static WXConfigUtil INSTANCE;


    public static final String APP_ID = "*****";//應用AppID
    public static final String KEY = "******";//商戶密鑰
    public static final String MCH_ID = "******";//商戶號

    public WXConfigUtil() throws Exception {
        String certPath = WXConfigUtil.class.getClassLoader().getResource("").getPath();//從微信商戶平台下載的安全證書存放的路徑
        File file = new File(certPath+ "apiclient_cert.p12");
        InputStream certStream = new FileInputStream(file);
        this.certData = new byte[(int) file.length()];
        certStream.read(this.certData);
        certStream.close();
    }

    //雙重檢查加鎖
    public static WXConfigUtil getInstance() throws Exception {
        if (INSTANCE == null) {
            synchronized (WXConfigUtil.class) {
                if (INSTANCE == null) {
                    INSTANCE = new WXConfigUtil();
                }
            }
        }
        return INSTANCE;
    }


    @Override
    public String getAppID() {
        return APP_ID;
    }

    //parnerid,商戶號
    @Override
    public String getMchID() {
        return MCH_ID;
    }

    @Override
    public String getKey() {
        return KEY;
    }

    @Override
    public InputStream getCertStream() {
        ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);
        return certBis;
    }

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

    @Override
    public int getHttpReadTimeoutMs() {
        return 10000;
    }
}
  • 業務層統一下單以及異步通知后的XML數據處理
import com.aone.app.common.wx.WXConfigUtil;
import com.aone.app.common.wx.WxCfg;
import com.aone.app.service.PayService;
import com.aone.app.service.WXAppPayService;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

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

@Service
public class WXAppPayServiceImpl implements WXAppPayService {

    private static final Logger logger = LoggerFactory.getLogger("WXAppPayServiceImpl");
    @Autowired
    private WxCfg wxCfg;
    /**
     * 調用官方SDK 獲取預支付訂單等參數
     * @param type
     * @param out_trade_no
     * @param money
     * @return
     * @throws Exception
     */
    @Override
    public Map<String, String> dounifiedOrder(String type,String out_trade_no,String money) throws Exception {
        Map<String, String> returnMap = new HashMap<>();

        //支付參數
        WXConfigUtil config = new WXConfigUtil();
        WXPay wxpay = new WXPay(config);
        //請求參數封裝
        Map<String, String> data = new HashMap<>();
        data.put("appid", config.getAppID());
        data.put("mch_id", config.getMchID());
        data.put("nonce_str", WXPayUtil.generateNonceStr());
        data.put("body", "訂單支付");
        data.put("out_trade_no", out_trade_no);
        data.put("total_fee", "1");
        data.put("spbill_create_ip", wxCfg.getIp()); //自己的服務器IP地址
        data.put("notify_url", wxCfg.getAppNotifyUrl());//異步通知地址(請注意必須是外網)
        data.put("trade_type", wxCfg.getAppType());//交易類型
        data.put("attach", type);//附加數據,在查詢API和支付通知中原樣返回,該字段主要用於商戶攜帶訂單的自定義數據
        String s = WXPayUtil.generateSignature(data, config.getKey());  //簽名
        data.put("sign", s);//簽名

        try {
            //使用官方API請求預付訂單
            Map<String, String> response = wxpay.unifiedOrder(data);
            System.out.println(response);
            String returnCode = response.get("return_code");    //獲取返回碼
            //若返回碼為SUCCESS,則會返回一個result_code,再對該result_code進行判斷
            if (returnCode.equals("SUCCESS")) {
                //主要返回以下5個參數(必須按照順序,否則APP報錯:-1)
                String resultCode = response.get("result_code");
                returnMap.put("appid", response.get("appid"));
                returnMap.put("noncestr", response.get("nonce_str"));
                if ("SUCCESS".equals(resultCode)) {//resultCode 為SUCCESS,才會返回prepay_id和trade_type
                    returnMap.put("package","Sign=WXPay");
                    returnMap.put("partnerid", response.get("mch_id"));
                    returnMap.put("prepayid", response.get("prepay_id"));
                    returnMap.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));//單位為秒

                    String sign = WXPayUtil.generateSignature(returnMap, config.getKey());// 二次簽名
                    returnMap.put("sign",sign); //簽名
                    returnMap.put("trade_type", response.get("trade_type"));//獲取預支付交易回話標志
                    return returnMap;
                } else {
                    //此時返回沒有預付訂單的數據
                    return returnMap;
                }
            } else {
                return returnMap;
            }
        } catch (Exception e) {
            System.out.println(e);
            //系統等其他錯誤的時候
        }
        return returnMap;
    }

    /**
     *
     * @param notifyData 異步通知后的XML數據
     * @return
     */
    @Override
    public String payBack(String notifyData) {
        WXConfigUtil config = null;
        try {
            config = new WXConfigUtil();
        } catch (Exception e) {
            e.printStackTrace();
        }
        WXPay wxpay = new WXPay(config);
        String xmlBack = "";
        Map<String, String> notifyMap = null;
        try {
            notifyMap = WXPayUtil.xmlToMap(notifyData);         // 調用官方SDK轉換成map類型數據
            if (wxpay.isPayResultNotifySignatureValid(notifyMap)) {//驗證簽名是否有效,有效則進一步處理

                String return_code = notifyMap.get("return_code");//狀態
                String out_trade_no = notifyMap.get("out_trade_no");//商戶訂單號
                if (return_code.equals("SUCCESS")) {
                    if (out_trade_no != null) {
                        // 注意特殊情況:訂單已經退款,但收到了支付結果成功的通知,不應把商戶的訂單狀態從退款改成支付成功
                        // 注意特殊情況:微信服務端同樣的通知可能會多次發送給商戶系統,所以數據持久化之前需要檢查是否已經處理過了,處理了直接返回成功標志
                        //業務數據持久化
                        System.err.println("支付成功"+"\n");
                        String attach = notifyMap.get("attach");//附加數據,用於區分是那張表訂單
                        System.out.print("附加數據類型為:{}"+attach+"\n");
                        if(StringUtils.isEmpty(attach)){
                            logger.info("附加數據類型為:{}", attach);
                        }else{
                            //@TODO 預支付下單后回調的邏輯
                            
                        }
                        logger.info("微信手機支付回調成功訂單號:{}", out_trade_no);
                        xmlBack = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
                    } else {
                        logger.info("微信手機支付回調失敗訂單號:{}", out_trade_no);
                        xmlBack = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[報文為空]]></return_msg>" + "</xml> ";
                    }
                }
                return xmlBack;
            } else {
                // 簽名錯誤,如果數據里沒有sign字段,也認為是簽名錯誤
                //失敗的數據要不要存儲?
                logger.error("手機支付回調通知簽名錯誤");
                xmlBack = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[報文為空]]></return_msg>" + "</xml> ";
                return xmlBack;
            }
        } catch (Exception e) {
            logger.error("手機支付回調通知失敗", e);
            xmlBack = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[報文為空]]></return_msg>" + "</xml> ";
        }
        return xmlBack;
    }
}
  • WXCfg中配置的是微信支付的回調地址以及交易類型
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * 微信參數配置中心
 */
@Component
public class WxCfg {

    @Value("${wx.appType}")
    private String appType;//App支付交易類型
    @Value("${wx.appNotifyUrl}")
    private String appNotifyUrl;//App回調地址
    @Value("${wx.h5Type}")
    private String h5Type;//H5支付交易類型
    @Value("${wx.h5NotifyUrl}")
    private String h5NotifyUrl;//H5回調地址
    @Value("${wx.ip}")
    private String ip;//服務器ip
    @Value("${wx.redirect_url}")
    private String redirect_url;//跳轉地址


    public String getAppNotifyUrl() {
        return appNotifyUrl;
    }

    public void setAppNotifyUrl(String appNotifyUrl) {
        this.appNotifyUrl = appNotifyUrl;
    }

    public String getH5NotifyUrl() {
        return h5NotifyUrl;
    }

    public void setH5NotifyUrl(String h5NotifyUrl) {
        this.h5NotifyUrl = h5NotifyUrl;
    }

    public String getAppType() {
        return appType;
    }

    public void setAppType(String appType) {
        this.appType = appType;
    }

    public String getH5Type() {
        return h5Type;
    }

    public void setH5Type(String h5Type) {
        this.h5Type = h5Type;
    }

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public String getRedirect_url() {
        return redirect_url;
    }

    public void setRedirect_url(String redirect_url) {
        this.redirect_url = redirect_url;
    }
}
  • 控制層下單接口以及回調接口
import com.aone.app.service.WXAppPayService;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

@RestController
@RequestMapping("pay")
@Api("App支付")
public class PayAppController {


    @Autowired
    private WXAppPayService wxAppPayService;


    /**
     *
     * App支付統一下單
     * @param out_trade_no  訂單號
     * @param total_fee      支付金額
     * @param type          0:預約訂單1:專家保證金2:即時咨詢訂單
     * @return
     * @throws Exception
     */
    @PostMapping("order")
    public Map<String, String> order(@RequestParam(value = "type") String  type,@RequestParam(value = "out_trade_no") String out_trade_no,@RequestParam(value = "total_fee") String total_fee)throws Exception{
        return wxAppPayService.dounifiedOrder(type,out_trade_no,total_fee);
    }


    /**
     *   微信支付異步結果通知
     */
    @RequestMapping(value = "wxPayNotify", method = {RequestMethod.GET, RequestMethod.POST})
    public String wxPayNotify(HttpServletRequest request, HttpServletResponse response) {
        System.out.print("微信回調開始"+"\n");
        String resXml = "";
        try {
            InputStream inputStream = request.getInputStream();
            //將InputStream轉換成xmlString
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            StringBuilder sb = new StringBuilder();
            String line = null;
            try {
                while ((line = reader.readLine()) != null) {
                    sb.append(line + "\n");
                }
            } catch (IOException e) {
                System.out.println(e.getMessage());
            } finally {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            resXml = sb.toString();
            String result = wxAppPayService.payBack(resXml);
            System.out.print("微信回調結束"+"\n");
            return result;
        } catch (Exception e) {
            System.out.println("微信手機支付失敗:" + e.getMessage());
            String result = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[報文為空]]></return_msg>" + "</xml> ";
            return result;
        }
    }

}
  • 下單封裝的Map中封裝參數服務器IP寫死可能會報錯,提供獲取服務器IP工具類
import javax.servlet.http.HttpServletRequest;

/**
 * 獲取服務器IP工具類
 */
public class IpAddr {

    public static String getIpAddr(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.getRemoteAddr();
        }
        return ip;
    }

}

主要用於安卓APP微信支付,IOS微信支付可能是需要交錢,太貴了,所以IOS的微信支付提供外鏈接使用H5進行支付。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM