1. 程式人生 > >針對APP的後臺支付程式碼(微信和支付寶)

針對APP的後臺支付程式碼(微信和支付寶)

APP支付: 1.微信支付: 這是app支付時,一個完整的流程 1.1首先要去微信開放平臺註冊,並建立APP 1.2取得微信支付的許可權 1.3 商戶平臺有公眾號平臺和APP平臺兩種,一定要是APP平臺,可以在下面這個地方檢視 1.4 我們需要在商戶平臺和開放平臺上獲取到以下資料: 開放平臺: APPID、APP_SECRET:見1圖。 商戶平臺: 祕鑰(APP_KEY):微信商戶平臺(pay.weixin.qq.com)-->賬戶設定-->API安全-->金鑰設定 商戶號(MCH_ID):微信商戶平臺(pay.weixin.qq.com)-->賬戶設定-->商戶資訊-->微信支付商戶號
數字證書(微信退款需要使用):微信商戶平臺(pay.weixin.qq.com)-->賬戶設定-->API安全-->證書下載 2.支付寶的操作流程簡單,文件清楚(略) 3準備工作結束,我的產品是使用Hbuilder開發的HTML5的APP,後端是依託於Java Web程式。工作流程參照時序圖。主要程式碼如下,詳情見檔案   其中用到一些工具方法  或者客戶端建立程式碼都在連結裡面有,我放在了碼雲上面 https://gitee.com/muziTM/payDemo   3.1  入口   
/**
 * 支付類   包含支付寶和微信
 * 
@author Tianming_Li * */ @Controller @RequestMapping("/payController") public class PayController { //訂單 @Autowired OrdersService orderService; //付費 @Autowired PayService payService; //退費 @Autowired RefundService refundService; //支付成功回撥 @Autowired CallBackService callBackService;
/** * 下單 * @param outTradeNo * @param payType * @param request * @param response * @param modelMap * @return */ @RequestMapping("/pay") public String pay(@RequestParam(name="outTradeNo") String outTradeNo,@RequestParam(name="payType") String payType, HttpServletRequest request,HttpServletResponse response, ModelMap modelMap){ //根據訂單號獲取到訂單資訊 Orders order = orderService.selectOrdersInfoByOutTradeNo(outTradeNo); if("wxpay".equals(payType)){ return payService.wxPay(order, request, response, modelMap); }else{ return payService.aliPay(order); } } /** * 退款 * @param outTradeNo * @param payType * @param request * @param response * @return */ @RequestMapping("/refunds") public String refunds(@RequestParam(name="outTradeNo") String outTradeNo,@RequestParam(name="payType") String payType, HttpServletRequest request,HttpServletResponse response){ //根據訂單號獲取到訂單資訊 Orders order = orderService.selectOrdersInfoByOutTradeNo(outTradeNo); if("wxpay".equals(payType)){ return refundService.wxRefund(order, request, response); }else{ return refundService.aliRefund(order); } } /** * 接收微信支付成功通知 * * @param request * @param response * @throws IOException */ @RequestMapping(value = "/getWxPayNotify") public void getWxPayNotify(HttpServletRequest request, HttpServletResponse response) throws IOException { callBackService.wxCallBack(request, response); } /** * 接收支付寶支付成功回撥 * @param request * @param response * @throws IOException */ @RequestMapping("/getAliPayNotify") public void getAliPayNotify(HttpServletRequest request, HttpServletResponse response) throws IOException { callBackService.aliCallBack(request, response); } }

  3.2 支付程式碼

 

/**
 * 支付處理
 * @author Tianming_Li
 *
 */
@Service("payService")
public class PayServiceImpl implements PayService {
    
    protected static final Log logger = LogFactory.getLog(PayServiceImpl.class);
    
    @Override
    public String wxPay(Orders order, HttpServletRequest request, HttpServletResponse response, ModelMap modelMap) {
        BaseDao dao = DaoUtils.getDao("sys");
        StringBuilder sb = new StringBuilder();
        sb.append("SELECT * FROM WX_PAY_CONFIG WHERE YY_ID = '");
        sb.append(order.getYyId());
        sb.append("'");
        //獲取到微信配置引數
        WxPayConfig appPayConfig = dao.getObjectBySql(sb.toString(), WxPayConfig.class);
        Map<String, Object> map = new HashMap<String, Object>();
        //初始化一個請求物件
        WxRequestHandler wxRequestHandler = new WxRequestHandler(request, response);
        String payFee = order.getPayMoney();
        int intPayFee = (int) (Float.valueOf(payFee) * 100);
        String nonceStr = WxUtils.getNonceStr();
        String outTradeNo = order.getOutTradeNo();
        String timestamp = WxUtils.getTimeStamp();
        wxRequestHandler.setParameter("appid", appPayConfig.getAppId());//appId
        wxRequestHandler.setParameter("mch_id", appPayConfig.getMchId());//商戶號
        wxRequestHandler.setParameter("nonce_str", nonceStr);//隨機字串
        wxRequestHandler.setParameter("body", appPayConfig.getBody());//商品描述
        wxRequestHandler.setParameter("notify_url", appPayConfig.getNotifyUrl());//通知地址
        wxRequestHandler.setParameter("out_trade_no", outTradeNo);
        wxRequestHandler.setParameter("spbill_create_ip", request.getRemoteAddr());
        wxRequestHandler.setParameter("total_fee", String.valueOf(intPayFee));
        wxRequestHandler.setParameter("trade_type", "APP");
         //注意簽名生成方式,具體見官方文件
        wxRequestHandler.setParameter("sign", wxRequestHandler.createMD5Sign(appPayConfig.getAppKey()));
        String prepayid;
        try {
            prepayid = wxRequestHandler.sendPrepay();
            if (prepayid != null && !prepayid.equals("")) {
                String signs ="appid=" + appPayConfig.getAppId()
                            + "&noncestr=" + nonceStr
                            + "&package=Sign=WXPay"
                            + "&partnerid="+ appPayConfig.getPartnerId() //商戶id
                            + "&prepayid=" + prepayid
                            + "&timestamp=" + timestamp 
                            + "&key=" + appPayConfig.getAppKey();//商戶平臺---api安全---金鑰
                map.put("code", 200);
                map.put("info", "success");
                map.put("prepayid", prepayid);
                map.put("sign", WxUtils.getMD5Encode(signs, "utf8").toUpperCase());
                map.put("appid", appPayConfig.getAppId());
                map.put("timestamp", timestamp); // 等於請求prepayId時的time_start
                map.put("noncestr", nonceStr); // 與請求prepayId時值一致
                map.put("package", "Sign=WXPay"); // 固定常量
                map.put("partnerid", appPayConfig.getPartnerId());//商戶id
            } else {
                map.put("code", 400);
                map.put("info", "獲取prepayid失敗");
            }
        } catch (Exception e) {
            map.put("code", 405);
            map.put("info", "系統異常");
        }

        return JSONUtils.toJSON(map);
    }

    @Override
    public String aliPay(Orders order) {
        //獲取配置引數
        BaseDao dao = DaoUtils.getDao("sys");
        StringBuilder sb = new StringBuilder();
        sb.append("SELECT * FROM ALI_PAY_CONFIG WHERE YY_ID = '");
        sb.append(order.getYyId());
        sb.append("'");
        AliPayConfig appPayConfig = dao.getObjectBySql(sb.toString(), AliPayConfig.class);
        
        // 例項化客戶端(引數:閘道器地址、商戶appid、商戶私鑰、格式、編碼、支付寶公鑰、加密型別),為了取得預付訂單資訊
        AlipayClient alipayClient = new DefaultAlipayClient(appPayConfig.getUrl(), appPayConfig.getAppId(),
                appPayConfig.getRsaPrivateKey(), appPayConfig.getFormat(), appPayConfig.getCharset(),
                appPayConfig.getPublicKey(), appPayConfig.getSigntype());
        // 例項化具體API對應的request類,類名稱和介面名稱對應,當前呼叫介面名稱:alipay.trade.app.pay
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
        // SDK已經封裝掉了公共引數,這裡只需要傳入業務引數。以下方法為sdk的model入參方式(model和biz_content同時存在的情況下取biz_content)。
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        //銷售產品碼,商家和支付寶簽約的產品碼,為固定值QUICK_MSECURITY_PAY
        BeanUtils.copy(order, model);//將訂單資訊複製到model類中
        request.setBizModel(model);
        // 回撥地址  指向回撥函式  example: https://ip:port/payController/getAliPayNotify.do
        request.setNotifyUrl(appPayConfig.getNotifyUrl()); 
        String orderStr = "";
        try {
            // 這裡和普通的介面呼叫不同,使用的是sdkExecute
            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
            orderStr = response.getBody();
            logger.info("訂單str:" + orderStr);
        } catch (AlipayApiException e) {
            logger.info(e.getMessage());
        }
        return orderStr;
    }

}

 3.2回撥程式碼

@Service("callBackService")
public class CallBackServiceImpl implements CallBackService {
    
    private static final Log LOGGER = LogFactory.getLog(CallBackServiceImpl.class);

    public static final String SUCCESS = "SUCCESS";

    @SuppressWarnings("unchecked")
    @Override
    public void wxCallBack(HttpServletRequest request, HttpServletResponse response) throws IOException {
        PrintWriter writer = response.getWriter();
        InputStream inputstream = request.getInputStream();
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] b = new byte[1024];
        int len = 0;
        while((len = inputstream.read(b))!= -1){
            outputStream.write(b, 0, len);
        }
        String result = new String(outputStream.toByteArray(), "utf-8");
        inputstream.close();
        outputStream.close();
        Map<String, String> map = null;
        //解析微信通知返回的資訊
        try {
            map = WxUtils.doXMLParse(result);
        } catch (JDOMException e) {
            LOGGER.info("微信回撥失敗:"+e.getMessage());
        }    
        // 若支付成功,則告知微信伺服器收到通知
        if (SUCCESS.equals(map.get("return_code")) && SUCCESS.equals(map.get("result_code"))) {
                
            /**
             * ...
             * 回撥業務
             */
            
            //微信會一直呼叫介面,直到我們返回SUCCESS
            String notifyStr = WxUtils.setXML(SUCCESS, "");
            writer.write(notifyStr);
            writer.flush();
        }
    }

    @Override
    public void aliCallBack(HttpServletRequest request, HttpServletResponse response) throws IOException {
        LOGGER.info("進入支付寶回撥");
        // 獲取支付寶GET過來反饋資訊
        String reqWay = "";
        if ("GET".equals(request.getMethod())) {
            reqWay = "GET";
        }
        Map<String, String> params = new HashMap<String, String>();
        Map<?, ?> requestParams = request.getParameterMap();
        for (Iterator<?> iter = requestParams.keySet().iterator(); iter.hasNext();) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
            }
            // 亂碼解決,這段程式碼在出現亂碼時使用。如果mysign和sign不相等也可以使用這段程式碼轉化
            if ("GET".equals(reqWay)) {
                try {
                    valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
                } catch (UnsupportedEncodingException e) {
                    LOGGER.info("不支援的編碼:" + e.getMessage());
                }
            }
            params.put(name, valueStr);
        }
        String tradeNo = request.getParameter("trade_no"); // 支付寶交易號
        String tradeStatus = request.getParameter("trade_status"); // 支付狀態
        String outTradeNo = request.getParameter("out_trade_no"); // 系統訂單號
        String sellerId = request.getParameter("seller_id"); // 商戶號

        LOGGER.info("支付寶交易號:" + tradeNo + ", 返回狀態:" + tradeStatus + ",訂單號  :" + outTradeNo);
        Map<String, Object> map = new HashMap<String, Object>();
        String result = "";
        try {
            if ("TRADE_SUCCESS".equals(tradeStatus)) {
                /**
                 * ...
                 * 回撥業務
                 */
            } else {
                result = "fail";// 為了保證不重複回撥
            }
        } catch (Exception e) {
            result = "fail";
        }
        map.put(result, result);
        AliUtils.renderText(response, result);

    }

}

3.4 微信退費的時候需要證書,密碼預設是商戶號,由於專案中使用了多個微信商戶號,因此有多個證書,路徑和密碼放在了資料庫中

public class WxRequestHandler{
/**
     * 退費
     * @return
     */
    public Map<String,String> sendWxChanel(String filePath,String pwd) throws Exception{
        Map<String,String> map =null;
        Set es=this.getAllParameters().entrySet();
        Iterator it=es.iterator();
        StringBuilder sb = new StringBuilder("<xml>");
        while(it.hasNext()){
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            sb.append("<"+k+">"+v+"</"+k+">");
        }
        sb.append("</xml>");
        String params=sb.substring(0);
        String requestUrl = this.getGateUrl();
        TenpayHttpClient httpClient = new TenpayHttpClient();
        httpClient.setReqContent(requestUrl);
        String resContent = "";
        //filepath 檔案路徑 pwd 密碼
        if (httpClient.callHttpPost(requestUrl, params,true,filePath,pwd)) {
            resContent = httpClient.getResContent();
            map=WxUtils.doXMLParse(resContent);
        }
        return map;
    }

}