java工廠-積木系列

来源:http://www.cnblogs.com/killbug/archive/2016/06/08/5569056.html
-Advertisement-
Play Games

這裡記錄一個例子,工廠模式的理論就不扯淡了。 遇到的問題:支付方式有很多種,比如微信支付 支付寶支付 銀聯支付 等等。我們在在實現的時候發現他麽的流程上是相似的,以及每個方式都有大量的個性配置,在實例化時需要載入他們,以及為了清晰的講調用方和實現方進行分離,就有來下麵的小設計。 以銀聯為例,(通過測 ...


這裡記錄一個例子,工廠模式的理論就不扯淡了。

遇到的問題:支付方式有很多種,比如微信支付 支付寶支付 銀聯支付 等等。我們在在實現的時候發現他麽的流程上是相似的,以及每個方式都有大量的個性配置,在實例化時需要載入他們,以及為了清晰的講調用方和實現方進行分離,就有來下麵的小設計。

以銀聯為例,(通過測試)。

共用介面:

public interface CommonPay {

    /**
     * <pre>
     * 功能描述:
     * 支付預處理:生成支付URL或提供給支付平臺的數據
     *
     * @param param 預支付參數
     * @return 支付URL或提供給支付平臺的數據
     * @throws Exception
     */
    <T> PrepayDto<T> prePay(PrepayParam param) throws Exception;
}

 

工廠類:

public class CommonPayFactory implements InitializingBean {

    /** 銀聯支付 */
    private CommonPay unionPay;

    @Override public void afterPropertiesSet() throws Exception {
        if (unionPay == null) {
            throw new RuntimeException("未配置任何支付實例");
        }
    }

    public CommonPay getUnionPay() {
        return unionPay;
    }

    public void setUnionPay(CommonPay unionPay) {
        this.unionPay = unionPay;
    }
}

 

我們利用spring,將bean初始化信息配置好:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="unionPayConfig" class="com.xiaoka.cashier.web.union.UnionPayConfig">
        <property name="payUrl" value="${pay.unionpay.payUrl}" />
        <property name="queryUrl" value="${pay.unionpay.queryUrl}" />
        <property name="merId" value="${pay.unionpay.merId}" />
        <property name="signPfxFile" value="${pay.unionpay.signPfxFile}" />
        <property name="signPfxPwd" value="${pay.unionpay.signPfxPwd}" />
        <property name="verifySignCertFile" value="${pay.unionpay.verifySignCertFile}" />
        <property name="payNotifyFrontUrl" value="${pay.unionpay.payNotifyFrontUrl}" />
        <property name="payNotifyUrl" value="${pay.unionpay.payNotifyUrl}" />
    </bean>


    <!-- 通用支付工廠實例 -->
    <bean id="commonPayFactory" class="com.xiaoka.cashier.service.pay.CommonPayFactory">
        <property name="unionPay">
            <bean class="com.xiaoka.cashier.service.pay.UnionPay">
                <property name="UnionPayConfig" ref="unionPayConfig" />
            </bean>
        </property>
    </bean>
</beans>

 

如此在項目啟動時,銀聯實現會根據配置信息實例化,工廠類會自動載入入銀聯實例以備獲取。

以下是銀聯實現:

public class UnionPay implements CommonPay {

    /** Logger */
    private static final Logger LOGGER = LoggerFactory.getLogger(UnionPay.class);

    /** 日期時間格式 */
    private static final String DATE_FORMAT_YYYYMMDDHHMMSS = "yyyyMMddHHmmss";

    /** 銀聯支付配置 */
    private UnionPayConfig unionPayConfig;

    @Override public <T> PrepayDto<T> prePay(PrepayParam param) throws Exception {

        PrepayDto<String> prepayDto = new PrepayDto<>();

        // 構造預下單簽名參數
        Map<String, String> params = new TreeMap<>();

        /** 銀聯全渠道系統,產品參數,除了encoding自行選擇外其他不需修改 */
        // 版本號,全渠道預設值
        params.put("version", UnionPayConfig.VERSION);
        // 字元集編碼,可以使用UTF-8,GBK兩種方式
        params.put("encoding", UnionPayConfig.CHARSET);
        // 簽名方法,只支持 01:RSA方式證書加密
        params.put("signMethod", "01");
        // 交易類型 ,01:消費
        params.put("txnType", "01");
        // 交易子類型, 01:自助消費
        params.put("txnSubType", "01");
        // 業務類型,B2C網關支付,手機wap支付
        params.put("bizType", "000201");
        // 渠道類型,這個欄位區分B2C網關支付和手機wap支付;07:PC,平板  08:手機
        params.put("channelType", "07");

        /** 商戶接入參數 */
        // 商戶號碼,請改成自己申請的正式商戶號或者open上註冊得來的777測試商戶號
        params.put("merId", unionPayConfig.getMerId());
        // 接入類型,0:直連商戶
        params.put("accessType", "0");
        // 商戶訂單號,8-40位數字字母,不能含“-”或“_”,可以自行定製規則
        params.put("orderId", param.getOutTradeNo());
        // 訂單發送時間,取系統時間,格式為YYYYMMDDhhmmss,必須取當前時間,否則會報txnTime無效
        SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT_YYYYMMDDHHMMSS);
        Date orderAddTime = new Date();
        String txtTime = sdf.format(orderAddTime);
        params.put("txnTime", txtTime);
        // 賬號
        // 1、 後臺類消費交易時上送全卡號或卡號後4位
        // 2、 跨行收單且收單機構收集銀行卡信息時上送、
        // 3、前臺類交易可通過配置後返回,卡號可選上送
        params.put("accNo", param.getCardNumber());
        // 交易幣種(境內商戶一般是156 人民幣)
        params.put("currencyCode", "156");
        // 交易金額,單位分,不要帶小數點
        params.put("txnAmt", param.getTotalFee());
        // 請求方保留域,如需使用請啟用即可;透傳欄位(可以實現商戶自定義參數的追蹤)本交易的後臺通知,
        // 對本交易的交易狀態查詢交易、對賬文件中均會原樣返回,商戶可以按需上傳,長度為1-1024個位元組
        params.put("reserved", "{cardNumberLock=1}"); // TODO
        // 前臺通知地址 (需設置為外網能訪問 http https均可),支付成功後的頁面 點擊“返回商戶”按鈕的時候將非同步通知報文post到該地址
        // 如果想要實現過幾秒中自動跳轉回商戶頁面許可權,需聯繫銀聯業務申請開通自動返回商戶許可權
        params.put("frontUrl", unionPayConfig.getPayNotifyFrontUrl());
        // 後臺通知地址(需設置為【外網】能訪問 http https均可),支付成功後銀聯會自動將非同步通知報文post到商戶上送的該地址,失敗的交易銀聯不會發送後臺通知
        // 註意:1.需設置為外網能訪問,否則收不到通知    2.http https均可
        // 3.收單後臺通知後需要10秒內返回http200或302狀態碼
        // 4.如果銀聯通知伺服器發送通知後10秒內未收到返回狀態碼或者應答碼非http200,那麼銀聯會間隔一段時間再次發送。總共發送5次,每次的間隔時間為0,1,2,4分鐘。
        // 5.後臺通知地址如果上送了帶有?的參數,例如:http://abc/web?a=b&c=d 在後臺通知處理程式驗證簽名之前需要編寫邏輯將這些欄位去掉再驗簽,否則將會驗簽失敗
        params.put("backUrl", unionPayConfig.getPayNotifyUrl());

        // 對請求參數拼接後進行消息摘要(SHA-1),然後用商戶私鑰進行簽名
        sign(params);

        // 生成表單HTML文檔
        String html = createAutoFormHtml(unionPayConfig.getPayUrl(), params, UnionPayConfig.CHARSET);

        prepayDto.setOrderId(param.getOrderId());
        prepayDto.setOrderPaymentMethod(1);//TODO
        prepayDto.setOutTradeNo(param.getOutTradeNo());
        prepayDto.setResp(html);
        return (PrepayDto<T>) prepayDto;
    }


    private void sign(Map<String, String> params) {
        // 證書ID 填寫簽名私鑰證書的Serial Number,該值可通過銀聯提供的SDK獲取
        params.put("certId", unionPayConfig.getCertId());

        // 對請求參數進行簽名
        String reqStr = PayUtil.mapToQueryStr(params, UnionPayConfig.CHARSET, false, true); // 將Map信息轉換成key1=value1&key2=value2的形式
        byte[] signDigest = SHA1Util.sha1X16(reqStr, UnionPayConfig.CHARSET); // SHA-1消息摘要
        byte[] byteSign = RSA.signBySoft(unionPayConfig.getPrivateKey(), signDigest); // 用商戶私鑰簽名
        String sign = com.xiaoka.freework.utils.encrypt.Base64.encode(byteSign); // Base64編碼
        // 報文摘要的簽名
        params.put("signature", sign);
    }

    private static String createAutoFormHtml(String reqUrl, Map<String, String> hiddens, String encoding) {
        StringBuilder sb = new StringBuilder();
        sb.append("<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=").append(encoding).append("\"/></head><body>");
        sb.append("<form id = \"pay_form\" action=\"").append(reqUrl).append("\" method=\"post\">");
        if (null != hiddens && 0 != hiddens.size()) {
            Set<Map.Entry<String, String>> set = hiddens.entrySet();
            for (Map.Entry<String, String> ey : set) {
                String key = ey.getKey();
                String value = ey.getValue();
                sb.append("<input type=\"hidden\" name=\"").append(key).append("\" id=\"")
                  .append(key).append("\" value=\"").append(value).append("\"/>");
            }
        }
        sb.append("</form>");
        sb.append("</body>");
        sb.append("<script type=\"text/javascript\">");
        sb.append("document.all.pay_form.submit();");
        sb.append("</script>");
        sb.append("</html>");
        return sb.toString();
    }


    public UnionPayConfig getUnionPayConfig() {
        return unionPayConfig;
    }

    public void setUnionPayConfig(UnionPayConfig unionPayConfig) {
        this.unionPayConfig = unionPayConfig;
    }
}

 

調用方只需獲取銀聯實例調用方法即可:

        CommonPay unionPay = commonPayFactory.getUnionPay();

        PrepayParam prepayParam = new PrepayParam();
        prepayParam.setOrderId(orderId);
        prepayParam.setOutTradeNo(tradeNo);
        prepayParam.setCardNumber(cardNo);
        prepayParam.setTotalFee(amount.toString());
        PrepayDto prepayDto = null;
        try {
            prepayDto = unionPay.prePay(prepayParam);
        } catch (Exception e) {
            logger.error("unionPay.prePay has error", e);
        }

 

耐心點~

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • rmdir命令可以刪除空目錄,註意必須要是空的目錄才能使用該命令刪除,該命令使用頻率並不高。 ...
  • ![CentOS Logo][0] 介紹 Nux Dextop是類似CentOS、RHEL、ScientificLinux的第三方RPM倉庫(比如:Ardour,Shutter等等)。目前,Nux Dextop對CentOS/RHEL 6|7可用。 安裝 Nux Dextop庫依賴於EPEL庫,所有 ...
  • 在編寫shell腳本中,經常要處理一些輸入參數,在使用過程中發現getopts更加方便,能夠很好的處理用戶輸入的參數和參數值。 getopts用於處理用戶輸入參數,舉例說明使用方法: while getopts :a:b:cdefg opt; do case $opts in a) do sth; ...
  • shell簡介 Shell是系統的用戶界面,提供了用戶與內核進行交互操作的一種介面。它接收用戶輸入的命令並把它送入內核去執行。 運行背景 a 一個基本的linux系統結構 b.由上圖可以看出,shell 在系統中是處於中間層次的。他為用戶和kernel的交互提供了一種方式,方案。 c.關於分層結構的 ...
  • 使用mkdir命令可以新建目錄,這裡只通過幾個示例簡單介紹了常見使用方式。 ...
  • 當我們使用linux的最小安裝時,很多系統程式都沒有被安裝,這時,我們可以通過yum命令安裝指定的包包,當然前提是你的linux處於聯網狀態的,下麵說一下yum的用法 1 顯示程式列表(聯網的) yum list | grep net-tools //顯示網上所有的net-tools包包列表,其中g ...
  • 1、訂單流程重新設計流程,資料庫表設計在原有基礎上進行相應的改動 2、企業類型增加車隊概念 3、重新修改sso單點登錄,原型支持一個用戶可登錄多個客戶端,目前只能同時登錄一個客戶端; 修改相應的攔截器 4、圖片/文件上傳通用組建,支持web端非同步上傳,以及手機端上傳圖片 5、資料庫模型推薦 (1)、 ...
  • array_slice和array_splice函數是用在取出數組的一段切片,array_splice還有用新的切片替換原刪除切片位置的功能。類似javascript中的Array.prototype.splice和Array.prototype.slice方法。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...