網銀在線支付接口和應用

網銀的接口不難,可是開通網銀接口須要不小的費用。
相關資源彙編下載:html

 

 

http://dotnet.5d6d.com/thread-475-1-1.html

 
最近關注項目中在線支付,因此看一下文檔,在線支付應用開發:java

 

  基本全部的在線支付均採用如下方式:git

   客戶點擊結賬時將關於訂單的信息和貨幣信息,相應的信息URL,通過md5或其餘方式發送(可能Socket和Http或Https)支付平臺(塊錢,paypal或支付寶等),支付平臺處理完畢時根據相應URL,返回相關的信息(付款信息,訂單信息,驗證信息).算法

  在實際操做Money的問題人們一貫關注他的安全性等問題,同時本人習慣在經過http方式訪問非外網時採用Commons-httpclient的post發送實現,簡單方便,因此採用此種實現:具體看如下API和原代碼:apache

 

網銀在線支付API接口:編程

商戶>>>>>>網銀在線支付:數組

<form method=post action="https://pay.chinaebank.cn/select_bank">安全

<input type=hidden name=v_mid value="1001">                        商戶編號app

<input type=hidden name=v_oid value="19990720-1001-000001234">     訂單編號dom

<input type=hidden name=v_amount value="13.45">                   訂單總金額

<input type=hidden name=v_moneytype value="0">                         幣種

<input type=hidden name=v_url value="http://domain/program">

支付動做完成後返回到該url,支付結果以POST方式發送

<input type=hidden name=v_md5info value="1630DC083D70A1E8AF60F49C143A7B95">                 訂單MD5校驗碼

<input type="hidden" name="remark1 " value="">備註字段1

<input type="hidden" name="remark2" value="">備註字段2

<input type=hidden name=v_rcvname value="張三">                    收貨人姓名

<input type=hidden name=v_rcvaddr value="北京海淀">               收貨人地址

<input type=hidden name=v_rcvtel value="68475566">                  收貨人電話

<input type=hidden name=v_rcvpost value="100036">                      收貨人郵編

<input type=hidden name=v_orderstatus value="0">                     商品信息

<input type=hidden name=v_ordername value="李四">                定貨人姓名

<input type=hidden name= v_orderemail value="test@test.com">       定貨人郵件

<input type=submit value="網銀在線支付">

</form>

MD5校驗串生成方法:當消費者在商戶端生成最終訂單的時候,將訂單中的v_amount v_moneytype v_oid v_mid v_url key六個參數的value值拼成一個無間隔的字符串(順序不要改變)。參數key是商戶的MD5密鑰(該密匙可在登錄商戶管理界面後自行更改。)

 網銀在線支付>>>商戶

支付完成後頁面轉到商戶,從網銀在線支付返回的消息格式爲:

 

<form method=get action="v_url" target=_self>

 <input type="hidden" name="v_oid" value="">         

  <input type="hidden" name="v_pstatus" value="">

<input type="hidden" name="v_pstring" value="">

<input type="hidden" name="v_pmode" value="">

<input type="hidden" name="v_md5str" value="">

<input type="hidden" name="v_amount" value="">

<input type="hidden" name="v_moneytype" value="">

<input type="hidden" name="remark1 " value="">

<input type="hidden" name="remark2" value="">

</form>

 

該消息格式詳細解釋以下:v_url是該筆訂單提交時參數v_url 的值,即網銀返回到商戶的接口地址。

 

變量名稱

變量命名

返回值說明

 

訂單編號

v_oid

商戶發送的v_oid定單編號。

支付狀態

v_pstatus

20(表示支付成功)

30(表示支付失敗)

支付結果信息

v_pstring

支付完成

支付完成

支付方式

v_pmode

支付銀行,例如工商銀行

訂單MD5校驗碼

v_md5str

該參數的MD5字符串的順序爲:v_oid,v_pstatus,v_amount,v_moneytype,key

 MD5字符串示例

20050320-1001-0000012342012.340key

用MD5函數加密上述字符串後獲得的值若是和v_md5str值相等即代表返回的信息沒有被纂改

訂單總金額

v_amount

訂單實際支付金額

幣種

v_moneytype

訂單實際支付幣種

備註字段1

remark1

 

備註字段2

remark2

 

表3

package cn.com.vnvtrip.china.pay.proxy;

import static cn.com.vnvtrip.china.pay.commons.ChinaPayConstants.CHINABANK_NOTIFY_URL_HTTP;
import static cn.com.vnvtrip.china.pay.commons.ChinaPayConstants.CHINABANK_PAY_HTTPS;
import static cn.com.vnvtrip.china.pay.commons.ChinaPayConstants.CHINABANK_PAY_MD5_KEY;

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

import org.apache.commons.codec.digest.DigestUtils;

import cn.com.vnvtrip.china.pay.commons.Env;
import cn.com.vnvtrip.china.pay.commons.HTTPClient;

/**
 * 
 * 網銀接口服務的代理
 * 
 * @author longgangbai
 * 
 */
public class ChinaPayProxy {
 /**
  * 在下訂單時採用的的Md5加密的信息: MD5校驗串生成方法:當消費者在商戶端生成最終訂單的時候, 將訂單中的v_amount
  * v_moneytype v_oid v_mid v_url key六個參數的value值拼成一個無間隔的字符串(順序不要改變)。
  * 參數key是商戶的MD5密鑰(該密匙可在登錄商戶管理界面後自行更改。)
  * 
  * @param v_amount
  * @param v_moneytype
  * @param v_oid
  * @param v_mid
  * @param v_url
  * @param key
  * @return
  */
 private static String getMd5Sign(String v_amount, String v_moneytype,
   String v_oid, String v_mid, String v_url, String key) {
  StringBuffer sb = new StringBuffer();
  sb.append(v_amount);
  sb.append(v_moneytype);
  sb.append(v_oid);
  sb.append(v_mid);
  sb.append(v_url);
  sb.append(key);
  byte[] bytes = DigestUtils.md5(sb.toString());
  String md5info = new String(bytes).toUpperCase();
  return md5info;
 }

 /**
  * 調用支付網關接口網址 銀行結賬的接口代理 (本人習慣採用Commons-httpclient實現)
  * 用途:用來接受商戶發給網銀在線服務支付的訂單信息
  * 
  * @param v_mid
  *            商戶編號(非空)
  * @param v_oid
  *            訂單編號(非空)(格式:訂單生成日期(yyyymmdd)-商戶編號-商戶流水號)字段不可超過64位
  * @param v_amount
  *            訂單總金額 (非空)
  * @param v_moneytype
  *            貨幣類型 (非空) 0:RMB 1美圓
  * @param v_url
  *            (非空) 支付的動做完成時返回的該url,支付結果以post方式發送
  * @param v_md5info
  *            訂單md5校驗碼
  * @param remark1
  *            備註字段1(可選字段)
  * @param remark2
  *            備註字段2 (可選字段)
  * @param v_vmd
  *            yyyymmdd 備註字段2 (不可爲空字段)
  * @param v_rcvname
  *            收貨人姓名 (自定義非網銀必須字段)
  * @param v_rcvaddr
  *            收貨人地址(自定義非網銀必須字段)
  * @param v_rcvtel
  *            收貨人電話(自定義非網銀必須字段)
  * @param v_rcpost
  *            收貨人郵編(自定義非網銀必須字段)
  * @param v_orderstatus
  *            商品信息(自定義非網銀必須字段)
  * @param v_ordername
  *            定貨人姓名(自定義非網銀必須字段)
  * @param v_orderemail
  *            定貨人郵件(自定義非網銀必須字段)
  * @return
  */
 public static boolean chinaBankPayCheck(String v_mid, String v_oid,
   String v_amount, String v_moneytype, String v_url, String remark1,
   String remark2, String v_rcvname, String v_rcvaddr,
   String v_rcvtel, String v_rcpost, String v_orderstatus,
   String v_ordername, String v_orderemail) {
  Properties p = Env.getEnv().getProperties();
  String md5key = p.getProperty(CHINABANK_PAY_MD5_KEY);
  String v_md5info = getMd5Sign(v_amount, v_moneytype, v_oid, v_mid,
    v_url, md5key);
  Map<String, String> paramMaps = new HashMap<String, String>();
  paramMaps.put("v_mid", v_mid);
  paramMaps.put("v_oid", v_oid);
  paramMaps.put("v_amount", v_amount);
  paramMaps.put("v_moneytype", v_moneytype);
  paramMaps.put("v_url", p.getProperty(CHINABANK_NOTIFY_URL_HTTP));
  paramMaps.put("v_md5info", v_md5info);
  paramMaps.put("remark1", remark1);
  paramMaps.put("remark2", remark2);
  paramMaps.put("v_rcvname", v_rcvname);
  paramMaps.put("v_rcvaddr", v_rcvaddr);
  paramMaps.put("v_rcvtel", v_rcvtel);
  paramMaps.put("v_rcpost", v_rcpost);
  paramMaps.put("v_orderstatus", v_orderstatus);
  paramMaps.put("v_ordername", v_ordername);
  paramMaps.put("v_orderemail", v_orderemail);
  return HTTPClient.executeHttp(CHINABANK_PAY_HTTPS, paramMaps, null);
 }

 /**
  * result爲支付完畢接受的結果的map 校驗檢測在網銀支付數據是否被攔截的
  * 
  * @param v_oid
 *            獲取結果中的訂單編號
  * @param v_pstatus
 *            獲取訂單的支付狀態
  * @param v_pstring
 *            支付的結果
  * @param v_amount
 *            實際支付的金額
  * @param v_moneytype
 *            實際支付的幣種
  * @param v_md5str
 *            獲取訂單校驗的MD5驗證
  * @return
  */
 public static boolean checkPayOff(String v_oid, String v_pstatus,
   String v_pstring, String v_amount, String v_moneytype,
   String v_md5str) {
  Properties p = Env.getEnv().getProperties();
  String md5key = p.getProperty(CHINABANK_PAY_MD5_KEY);
  String checkmd5 = getCheckMd5(v_oid, v_pstatus, v_amount, v_moneytype,
    md5key);
  if (checkmd5.equals(v_md5str)) {
   return true;
  }
  return false;
 }

 /**
  * 獲得網銀訂單付款後Md5加密檢查
  * 
  * @param v_oid
  * @param v_pstatus
  * @param v_amount
  * @param v_moneytype
  * @param key
  * @return
  */
 private static String getCheckMd5(String v_oid, String v_pstatus,
   String v_amount, String v_moneytype, String key) {
  StringBuffer sb = new StringBuffer();
  sb.append(v_oid);
  sb.append(v_pstatus);
  sb.append(v_amount);
  sb.append(v_moneytype);
  sb.append(key);
  byte[] bytes = DigestUtils.md5(sb.toString());
  String md5info = new String(bytes).toUpperCase();
  return md5info;
 }
}

來自http://topmanopensource.javaeye.com/blog/497872

 

 

 

2010-02-09

建設銀行對接(一)

文章分類:Java編程

 

         這幾天 項目須要對接建設銀行的支付和查詢功能,在支付和查詢的時候將系統連接到建行指定的頁面上,因爲這些頁面是基於互聯網的,開放的,因此須要對數據加密和數字簽名。 我來實現這個數據加密解密模塊,功能已經完成了,唉,不過讓我暈死的是,建行其實一併提供了 jar 包,已經實現了數據加密解密,校驗數字簽名的功能,只不過同事沒注意到,只發接口文檔給我,沒發 jar 包給我,害我白着急了幾天,不過工做也沒算浪費,本身實現的仍是比較放心些吧。這些頁面的跳轉沒什麼技術,主要在於數據加密和數字簽名,在連接到建行頁面以前,先將參數加密,在收到建行跳轉過來的連接參數後,取出參數裏的簽名,將簽名和原始參數進行校驗,以確認目前跳轉過來的的確是建行。頁面跳轉沒什麼好說的,我所感興趣的在於加密這些地方,以及對建行文檔的理解。

按照建行的規定,咱們發送的數據須要進行 MD5 加密,建行對返回的數據進行了數字簽名,咱們須要校驗簽名的有效性。如下是建行的兩項約定:

 

建行附錄 1 : MAC 算法說明

Html代碼 複製代碼 收藏代碼http://jameswxx.javaeye.com/images/spinner.gif

  1.       建設銀行家居銀行項目組決定對商戶向網上銀行系統提交的交易內容進行MAC校驗,校驗算法採用標準MD5算法,不帶密鑰。該算法的詳細說明請參見RFC 1321文檔。   
  2.   
  3.     商戶實行標準的MD5算法對向網上銀行系統提交的交易內容進行MAC校驗,產生128位(bit)的MAC結果。輸入爲字符串,輸出爲16進制字符表示的字符串。   
  4.   
  5. 下面是對MAC結果的顯示方式的描述:   
  6.   
  7. 對128位的交易結果按4位爲一個單位進行劃分,共得到32段   
  8.   
  9. 將每段當作一個16進制數,如0011爲0X3,1101爲0Xd。   
  10.   
  11. 將這個數映射到ASCII碼錶,造成相應的字符,如0X2爲「2」,0Xd爲「d」。   
  12.   
  13. 將這些字符連成一個字符串,長度爲32。   
  14.   
  15.     
  16.   
  17.     下面是一些字符串進行MAC並按上述方法進行轉換後得到的結果:   
  18.   
  19. MD5 ("") = d41d8cd98f00b204e9800998ecf8427e   
  20.   
  21. MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661   
  22.   
  23. MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72  

校驗,校驗算法採用標準MD5算法,不帶密鑰。該算法的詳細說明請參見RFC 1321文檔。

 

算法對向網上銀行系統提交的交易內容進行MAC校驗,產生128位(bit)的MAC結果。輸入爲字符串,輸出爲16進制字符表示的字符串。

 

結果的顯示方式的描述:

 

位的交易結果按4位爲一個單位進行劃分,共得到32段

 

進制數,如0011爲0X3,1101爲0Xd。

 

碼錶,造成相應的字符,如0X2爲「2」,0Xd爲「d」。

 

 

 

 

並按上述方法進行轉換後得到的結果:

 

MD5 ("") = d41d8cd98f00b204e9800998ecf8427e

 

MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661

 

MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72

 

 

建行附錄 2 :數字簽名算法說明

     

Html代碼 

  1.  銀行將客戶支付信息實時通知給商戶時,使用的數字簽名算法是MD5withRSA算法。商戶驗證簽名的公鑰在商戶在網銀系統開戶,獲取數字證書後,登陸到網銀系統中,經過下載公鑰交易獲取。(下載後需妥善管理並及時更新商戶公鑰,以防公私鑰不匹配形成驗籤不經過)。(目前家居銀行項目組採用靜態密鑰對,上線前生成一對,發給合做商戶。)   
  2.   
  3.       商戶獲取的公鑰用X.509格式表示,而且將其按照每4位(bit)轉換爲一個16進制數的方式表示,產生16進制的字符串。家居銀行使用標準MD5withRSA算法對給商戶的響應進行簽名,產生1024位(bit)的簽名結果,而且將其按照每4位(bit)轉換爲一個16進制數的方式表示,造成16進制的字符串,長度爲256。   
  4.   
  5.     
  6.   
  7. 下面是對簽名結果的表示方式的描述:   
  8.   
  9. 對1024位的交易結果按4位爲一個單位進行劃分,共得到256段   
  10.   
  11. 將每段當作一個16進制數,如0011爲0X3,1101爲0Xd。   
  12.   
  13. 將這個數映射到ASCII碼錶,造成相應的字符,如0X2爲「2」,0Xd爲「d」。   
  14.   
  15. 將這些字符連成一個字符串,長度爲256。   
  16.   
  17.     
  18.   
  19. 例如:   
  20.   
  21. 待簽名的字符串爲:   
  22.   
  23. POSID=000000000&BRANCHID=110000000&ORDERID=19991101234&PAYMENT=500.00&CURCODE=01&REMARK1=19991101&REMARK2=merchantname&SUCCESS=Y  
  24.   
  25. 簽名結果爲:   
  26.   
  27. 4b3ef029516193b7d969ac1840083635a3e0901b8cd526caa44c1a072f496d7f0d4bca3942c0d9030bede37c7809b835cec787eb39e18b7596a724fba9805b24714dfbb0f4a3fb430b32e075254a114d4c38a0ac52ef46a0ad33dec3fbfc15417402a1399e65e46996c0cf49fc7ffca9222f8cd693c8376b6f928828967bec42   
  28.   
  29.     
  30.   
  31.       當商戶收到銀行傳來的CGI串後,從中獲取簽名(格式如上)和需簽名的原文。商戶端程序(商戶自行開發MD5withRSA簽名校驗程序)將簽名和商戶端的公鑰轉換成二進制格式,與簽名的原文一塊兒對簽名的正確性進行校驗,校驗步驟以下:   
  32.   
  33. 使用公鑰進行簽名的逆運算   
  34.   
  35. 使用標準MD5算法運算原文   
  36.   
  37. 比較1)、2)結果。  

算法。商戶驗證簽名的公鑰在商戶在網銀系統開戶,獲取數字證書後,登陸到網銀系統中,經過下載公鑰交易獲取。(下載後需妥善管理並及時更新商戶公鑰,以防公私鑰不匹配形成驗籤不經過)。(目前家居銀行項目組採用靜態密鑰對,上線前生成一對,發給合做商戶。)

 

格式表示,而且將其按照每4位(bit)轉換爲一個16進制數的方式表示,產生16進制的字符串。家居銀行使用標準MD5withRSA算法對給商戶的響應進行簽名,產生1024位(bit)的簽名結果,而且將其按照每4位(bit)轉換爲一個16進制數的方式表示,造成16進制的字符串,長度爲256。

 

 

 

 

 

位的交易結果按4位爲一個單位進行劃分,共得到256段

 

進制數,如0011爲0X3,1101爲0Xd。

 

碼錶,造成相應的字符,如0X2爲「2」,0Xd爲「d」。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

串後,從中獲取簽名(格式如上)和需簽名的原文。商戶端程序(商戶自行開發MD5withRSA簽名校驗程序)將簽名和商戶端的公鑰轉換成二進制格式,與簽名的原文一塊兒對簽名的正確性進行校驗,校驗步驟以下:

 

 

 

算法運算原文

 

、2)結果。

 

 

 

 

        仔細看上面兩項約定,不管是 MD5 加密仍是 RSA 加密,都有一個基礎工做,就是將二進制數據分割,換算成 16 進制字符,還須要進行逆運算。將結果按 4 位爲一個單位進行劃分,共得到 32段, 將每段當作一個 16 進制數,如 0011 爲 0X3 , 1101 爲 0Xd 。 將這個數映射到 ASCII 碼錶,造成相應的字符,如 0X2 爲「 2 」, 0Xd 爲「 d 」。 將這些字符連成一個字符串,長度爲32 。 我先實現這個功能,代碼以下:

ByteUtil.java

Java代碼 

    1. package cn.ipanel.payment.business.bank.ccb.encryption;   
    2.   
    3.  /**  
    4.  * 字節運算工具,其做用和背景請見建行接口文檔的"附錄1:MAC算法說明"  
    5.  *  
    6.  * @author wangxiaoxue  
    7.  *  
    8.  */  
    9. public class ByteUtil {   
    10.      // 用來將字節轉換成 16 進製表示的字符   
    11.     private static char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7',   
    12.   
    13.            '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };   
    14.   
    15.      /**  
    16.      * 找到字符在數組中的位置 
    17.      *  
    18.      * @param c  
    19.      * @return  
    20.      */  
    21.     private static int getIndex(char c) {   
    22.        int p = -1;   
    23.        for (int i = 0; i < hexDigits.length; i++) {   
    24.            if (hexDigits[i] == c) {   
    25.               p = i;   
    26.               break;   
    27.            }   
    28.        }   
    29.        return p;   
    30.     }   
    31.   
    32.      /**  
    33.      * 將字節轉化成字符串,轉換算法以下:<br>  
    34.      * 1:每一個字節長度爲8位,分割爲兩個4位,高四位和低四位<br>  
    35.      * 2:將每一個四位換算成16進制,而且對應ascii碼,如0x01對應1,0x0d對應d,具體對應關係請見數組hexDigits[]<br>  
    36.      * 3:將獲得的字符拼成字符串 
    37.      *  
    38.      * @param bytes  
    39.      * @return  
    40.      */  
    41.     public static String byteToChar(byte[] bytes) {   
    42.        // 每一個字節用 16 進製表示的話,使用兩個字符,因此字符數組長度是字節數字長度的2倍   
    43.        char str[] = new char[bytes.length * 2];    
    44.        // 表示轉換結果中對應的字符位置   
    45.        int  = 0;   
    46.         // 每個字節轉換成 16 進制字符   
    47.        for (int i = 0; i < bytes.length; i++) {   
    48.            byte byte0 = bytes[i]; // 取第 i 個字節   
    49.            // 取字節中高 4 位(左邊四位)的數字轉換,>>>爲邏輯右移,右移後,高四位變成低四位,須要對低四位以外的值進行消零運算   
    50.            str[k++] = hexDigits[byte0 >>> 4 & 0xf];   
    51.             // 取字節中低 4 位(右邊四位)的數字轉換,而且和0xf進行"邏輯與"運算,以消除高位的值,獲得純淨的低四位值   
    52.            str[k++] = hexDigits[byte0 & 0xf];   
    53.        }   
    54.        return new String(str);   
    55.     }   
    56.     
    57.     /**  
    58.      * 將字節轉換成二進制數組,是byteToChar方法的逆運算,轉換算法以下:<br>  
    59.      * 1:將字符按順序每兩個分爲一組,分別找出每一個字符在映射表hexDigits[]中的索引值,請見getIndex(char c)方法<br>  
    60.      * 2:每兩個字符一組進行運算,將第一個字符的索引值邏輯左移四位,並和"0xf"進行"邏輯或"運算,目的是將低四位都設置爲1,由於邏輯左移後,低四位都變成0了<br>  
    61.      * 3:將第二個字符的索引值和 
    62. "0xf0"進行"邏輯或"運算,目的的是將高位設置爲1<br>  
    63.      * 4:將兩個運算完的索引值進行"邏輯與"運算,獲得了兩個字符所表明的一個字節值<br>  
    64.      * 5:依次運算,最後獲得字節數組,返回 
    65.      *  
    66.      * @param str  
    67.      * @return  
    68.      */  
    69.     public static byte[] charToByte(String str) {   
    70.        char[] chars = str.toCharArray();   
    71.        byte[] bytes = new byte[chars.length / 2];   
    72.        int k = 0;   
    73.        for (int i = 0; i < chars.length; i = i + 2) {   
    74.            // 獲得索引值   
    75.            byte high = (byte) getIndex(chars[i]);   
    76.            byte low = (byte) getIndex(chars[i + 1]);   
    77.            // 第一個字符索引邏輯左移四位,並進行或運算,將低四位設置爲1   
    78.            high = (byte) ((high << 4) | 0xf);   
    79.             // 第二個字符索引進行或運算,將高四位設置爲1   
    80.            low = (byte) (low | 0xf0);   
    81.             // 兩個字節進行與運算   
    82.            bytes[k++] = (byte) (high & low);   
    83.        }   
    84.        return bytes;   
    85.     }   
    86.   
    87.      public static void main(String[] args) {    
    88.        String str = "abgcd1234";   
    89.        System.out.println("原始字符串:" + str);   
    90.        String result = ByteUtil.byteToChar(str.getBytes());   
    91.        System.out.println("運算結果:" + result);   
    92.        byte[] resultbytes = ByteUtil.charToByte(result);   
    93.        System.out.println("逆運算結果:" + new String(resultbytes));    
    94.     }   
    95. }  
相關文章
相關標籤/搜索