消息通道

使用方式

提供机构号以及接收通知的服务器地址给对接人员即可完成配置。开发者需要按照本文档的返回参数返回json

通讯协议

传输方式 通讯采用HTTP方式
提交方式 采用POST请求
字符编码 请求交易报文中的编码方式默认为GBK
报文格式 以json做为数据传输格式 注:“Content-Type","application/json”

安全规范

测试用证书:找开发获取

测试用MD5KEY: 找开发获取 (例:e10adc3949ba59abbe56e057f20f883e)

例如推送接口的请求原数据为:

{"logNo":"201804270298407869","UserId":"","BalDate":"20180427","AgentId":"172","TxnLogId":"9115248173556540156342038","BusinessId":"800461000001090","SDTermNo":"95126798","TxnCode":"N001","PayChannel":"2","TxnDate":"20180427","TxnTime":"162238","TxnAmt":"43.9","TxnStatus":"1","BankType":"","OfficeId":"91524817356294700943","ChannelId":"9115248173556540156342038","attach":"","CrdFlg":""}

将请求数据通过req_rsa_public_key.pem加密后,生成报文中pubDate的值。 pubDate为: 69B6F0FC21D74E8521C0FD2D4E5FF2F1597BD1FA4E4E96D6F8EA0158F23E744751CA10B4FE001AFAFE407A4188F50177003A275EBEF1785413D653DA28A9F2FEE9A8FCA67146980D270BB7C895DBEF328FEC5212451FDF23B142654BD5313B03726A0494504DB005F1BBC77E83654B5C0FEDBC25C36B2FCF83FD0AA1082B0244BBBFFA62E6B46B6AE0A6EB607B2E464204E106B5CD38257D7237AE8ADA4C0A5F8ED49DE39826F4B7C3B7FD62B1AECB8E8086536BBCC19121EFA676D6FBA4BC4E43BC2E9EB59173C4CBEA92EB2FAD08B0F6C91E45DC442DD8BB772D4D207B63A15531387EC5B9CAE7A334A8AFC8E352EA638D5E97B3F2BF09DDFFDC79845E04C785AD52991743240DB7A8180DC8CC586C845098EE1644324FF93DC3A0D6853F6838CFDA431F4035F998426B94E71C4C8BA602CEFF752B8573D502BE73AE733B1A44B3C61D238AA8BB08AA31FC2EC3B896B2CB83B74F176E8EE7C7B97FF2578F230A4FADDAE9934E928F8939372D4F094B804F5C9C2F6C4B58D6A900384A278B41CA16589DE05A30DEDA7C19D0209FBE445BF513622A69D0AA963BC10C760C6E0FADAA9A5C7C74E4ABBF69B42534AA9FF4988434B9C87DF155D96965A35EC9472FAD146AC80F0D470428072A255076B845D1B8F88264863EB070859834E28C66C58FB67BB759B39C06AB774B2E57D0BF4AC361B8B6071AB186EC44A715D11F4F6D

txnData拼接MD5KEY后,计算md5值做为signValue。

值:计算md5的值为:C6722327ADBEA235FD8808BB26022715

选择加密加签之后 接口的最终请求数据为: {"Datas":"4222541DF5EA7A1A676A16AE51F77DD2EA89233B26E12A2807679091891B8B935AE1CEBA76968D281895CC3ED53A3DE60E0B199A6DE44727F37CE68756E846E518464EA7AD435C820CD486DDF4D3711AEEE063481431291119451287085E9C854C76EA91F0A18F78AD50F9EF68DD4FE9D3373CD089A20983283E75966E92020CBF6D72ACEB0EE2F38820B04BACBC9E0ED23D46461C9FAF4014F3E53C8F36B8C4F683D31C8D5F7503F5A247E44D721242C5B12872BBC65E2D52FDA23180138197CA676B1EE845635B0AB752DB08276589E164A380EF543F626A35B86634752A99BEB2DF95A94090F998D290E72A1CABCC5F55CF50CFD42D3350B23488F7CD4F7CA9C8D5F0FECEE7DF488750F1A13010057380C5872EC9952CF9141F3C438EC0886128AACB18FC76BC51AA64295F890E3760B6FDE84DAE350D82B8A8ADCA37C869A1492852AFD35747D67073D0A130F4DB844D2AEF910A12B3B41015D9ADEEA4AEBCCF52BF13F2BF77600593ADC529079C4C2D468E24B5ADA87B3F6398C11348613B633A9408B5CD9AFF29C939C7E1D659F9AA062C317B7B45D7FB4FB963D55D56AF1939119BBEF5315A91492A75CD5B5D6D96E9C7673CF5C6ED80E04E8BF0CC05F3261CADB9E9AC61CE9FFA53FC5C7C47C2FF49B4FB781959AA055C345166FC4D5E2E23C44CAE6C56797A6D3AE571663A13BC897E240F06C7A66489BDC1F05DAE","singValue":"A26E178E5810E896066FE1A417506E97"} 合作商在接收到请求时,用私钥解密。 返回时数据不用加密 还是跟之前一样

记账结果通知(数据不加密)

请求参数

序号 中文域名 英文域名 类型 域中值的填写方法
1 结算日期 BalDate Char(8) YYYYMMDD
2 合作商号 AgentId Char(10)
3 交易流水号 TxnLogId Char(6) 对应交易上送的tradeNo字段
4 收单商户号 BusinessId Char(15)
5 收单终端号 SDTermNo Char(32)
6 交易码 TxnCode Char(4) N001:扫手机
N002:扫POS
N007:公众号支付
L005:扫码退款
L020:银行卡消费
L030:银行卡撤销
L009:银行卡冲正
4433040:台牌微信
4433050:台牌支付宝
2010060:预授权完成
2010070:预授权完成撤销
7 支付通道 PayChannel Char(2) 1.支付宝ISV
2.微信支付
3.百度百付宝
4.苏宁易付宝
5.支付宝城市服务商
6 京东钱包
7.QQ钱包
8 翼支付
8 交易日期 TxnDate Char(8) YYYYMMDD
9 交易时间 TxnTime Char(6) HHMMSS
10 交易金额 TxnAmt Char(10) 按元为单位填写
11 交易状态 TxnStatus CHAR(1) 1:成功
12 支付银行 BankType Char(20) 返回的支付银行
13 支付通道订单号 OfficeId Char(64) 微信/支付宝订单号
14 商户单号 ChannelId Char(64) 对应交易上送selOrderNo字段
15 C端用户信息 UserId Char(64) C端支付用户信息
16 平台流水号 logNo Char(20) 注意:银行卡冲正 以9开头 后面17位跟原交易一样
17 附加信息 attach Char(64) 附加信息
18 银行卡借贷记标识 CrdFlg Char(1) 1:借记卡
2:贷记卡

返回参数

序号 中文域名 英文域名 类型 域中值的填写方法
1 响应码 RspCode Char(6) 000000成功,其它失败
2 描述 RspDes Char(64) 不足右补空格

记账结果通知(数据加密)

请求参数

序号 中文域名 英文域名 类型 域中值的填写方法
1 推送数据 Datas Char(10240) 加密加签说明见安全规范
2 签名 signValue Char(128) MD5加签数据

返回参数

序号 中文域名 英文域名 类型 域中值的填写方法
1 响应码 RspCode Char(6) 000000成功,其它失败
2 描述 RspDes Char(64) 不足右补空格

加密加签参考文档

package main;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;  
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.KeyFactory;  
import java.security.KeyPair;  
import java.security.KeyPairGenerator;  
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;  
import java.security.interfaces.RSAPublicKey;  
import java.security.spec.InvalidKeySpecException;  
import java.security.spec.PKCS8EncodedKeySpec;  
import java.security.spec.X509EncodedKeySpec;  
import java.util.HashMap;  

import javax.crypto.Cipher;  

import net.sf.json.JSONObject;
import sun.misc.BASE64Decoder;

import java.math.BigInteger;    
import java.security.interfaces.RSAPrivateKey;    
import java.security.spec.RSAPrivateKeySpec;    
import java.security.spec.RSAPublicKeySpec;    

public class RsaUtils {    

    /**  
     * 生成公钥和私钥  
     * @throws NoSuchAlgorithmException   
     *  
     */    
    public static HashMap<String, Object> getKeys() throws NoSuchAlgorithmException{    
        HashMap<String, Object> map = new HashMap<String, Object>();    
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");    
        keyPairGen.initialize(1024);    
        KeyPair keyPair = keyPairGen.generateKeyPair();    
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();    
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();    
        map.put("public", publicKey);    
        map.put("private", privateKey);    
        return map;    
    }    
    /**  
     * 使用模和指数生成RSA公钥  
     * 注意:【此代码用了默认补位方式,为RSA/None/PKCS1Padding,不同JDK默认的补位方式可能不同,如Android默认是RSA  
     * /None/NoPadding】  
     *   
     * @param modulus  
     *            模  
     * @param exponent  
     *            指数  
     * @return  
     */    
    public static RSAPublicKey getPublicKey(String modulus, String exponent) {    
        try {    
            BigInteger b1 = new BigInteger(modulus);    
            BigInteger b2 = new BigInteger(exponent);    
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");    
            RSAPublicKeySpec keySpec = new RSAPublicKeySpec(b1, b2);    
            return (RSAPublicKey) keyFactory.generatePublic(keySpec);    
        } catch (Exception e) {    
            e.printStackTrace();    
            return null;    
        }    
    }    

    /**  
     * 使用模和指数生成RSA私钥  
     * 注意:【此代码用了默认补位方式,为RSA/None/PKCS1Padding,不同JDK默认的补位方式可能不同,如Android默认是RSA  
     * /None/NoPadding】  
     *   
     * @param modulus  
     *            模  
     * @param exponent  
     *            指数  
     * @return  
     */    
    public static RSAPrivateKey getPrivateKey(String modulus, String exponent) {    
        try {    
            BigInteger b1 = new BigInteger(modulus);    
            BigInteger b2 = new BigInteger(exponent);    
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");    
            RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(b1, b2);    
            return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);    
        } catch (Exception e) {    
            e.printStackTrace();    
            return null;    
        }    
    }    

    /**  
     * 公钥加密  
     *   
     * @param data  
     * @param publicKey  
     * @return  
     * @throws Exception  
     */    
    public static String encryptByPublicKey(String data, RSAPublicKey publicKey)    
            throws Exception {    
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");    
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);    
        // 模长    
        int key_len = publicKey.getModulus().bitLength() / 8;    
        // 加密数据长度 <= 模长-11   
        String iosDatas = new String(data.getBytes("GBK"),"ISO-8859-1");  
        String[] datas = splitString(iosDatas, key_len - 11);    
        String mi = "";    
        //如果明文长度大于模长-11则要分组加密    
        for (String s : datas) {  
            mi += byte2HexStr(cipher.doFinal(s.getBytes("ISO-8859-1")));    
        }       
//        for (String s : datas) {  
//            mi += bcd2Str(cipher.doFinal(s.getBytes("ISO-8859-1")));    
//        }    
        return mi;    
    }    

    /**  
     * 私钥解密  
     *   
     * @param data  
     * @param privateKey  
     * @return  
     * @throws Exception  
     */    
    public static String decryptByPrivateKey(String data, RSAPrivateKey privateKey)    
            throws Exception {    
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");    
        cipher.init(Cipher.DECRYPT_MODE, privateKey);    
        //模长    
        int key_len = privateKey.getModulus().bitLength() / 8;    
        byte[] bytes = data.getBytes("UTF-8");    
        byte[] bcd = ASCII_To_BCD(bytes, bytes.length);     
        //如果密文长度大于模长则要分组解密    
        String ming = "";    
        byte[][] arrays = splitArray(bcd, key_len);    
        for(byte[] arr : arrays){    
            ming += new String(cipher.doFinal(arr),"ISO-8859-1");    
        }    
        return new String(ming.getBytes("ISO-8859-1"));    
    }    
    /**  
     * ASCII码转BCD码  
     *   
     */    
    public static byte[] ASCII_To_BCD(byte[] ascii, int asc_len) {    
        byte[] bcd = new byte[asc_len / 2];    
        int j = 0;    
        for (int i = 0; i < (asc_len + 1) / 2; i++) {    
            bcd[i] = asc_to_bcd(ascii[j++]);    
            bcd[i] = (byte) (((j >= asc_len) ? 0x00 : asc_to_bcd(ascii[j++])) + (bcd[i] << 4));    
        }    
        return bcd;    
    }    
    public static byte asc_to_bcd(byte asc) {    
        byte bcd;    

        if ((asc >= '0') && (asc <= '9'))    
            bcd = (byte) (asc - '0');    
        else if ((asc >= 'A') && (asc <= 'F'))    
            bcd = (byte) (asc - 'A' + 10);    
        else if ((asc >= 'a') && (asc <= 'f'))    
            bcd = (byte) (asc - 'a' + 10);    
        else    
            bcd = (byte) (asc - 48);    
        return bcd;    
    }    
    /**  
     * BCD转字符串  
     */    
    public static String bcd2Str(byte[] bytes) {    
        char temp[] = new char[bytes.length * 2], val;    

        for (int i = 0; i < bytes.length; i++) {    
            val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f);    
            temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0');    

            val = (char) (bytes[i] & 0x0f);    
            temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0');    
        }    
        return new String(temp);    
    }    
    /**  
     * 拆分字符串  
     */    
    public static String[] splitString(String string, int len) {    
        int x = string.length() / len;    
        int y = string.length() % len;    
        int z = 0;    
        if (y != 0) {  
            z = 1;    
        }  
        String[] strings = new String[x + z];    
        String str = "";    
        for (int i=0; i<x+z; i++) {    
            if (i==x+z-1 && y!=0) {    
                str = string.substring(i*len, i*len+y);    
            }else{    
                str = string.substring(i*len, i*len+len);    
            }  
            strings[i] = str;    
        }    
        return strings;    
    }  
    /**  
     *拆分数组   
     */    
    public static byte[][] splitArray(byte[] data,int len){    
        int x = data.length / len;    
        int y = data.length % len;    
        int z = 0;    
        if(y!=0){    
            z = 1;    
        }    
        byte[][] arrays = new byte[x+z][];    
        byte[] arr;    
        for(int i=0; i<x+z; i++){    
            arr = new byte[len];    
            if(i==x+z-1 && y!=0){    
                System.arraycopy(data, i*len, arr, 0, y);    
            }else{    
                System.arraycopy(data, i*len, arr, 0, len);    
            }    
            arrays[i] = arr;    
        }    
        return arrays;    
    }  


    public static RSAPrivateKey loadPrivateKey(InputStream in) throws Exception{
        try {
            BufferedReader br= new BufferedReader(new InputStreamReader(in));
            String readLine= null;
            StringBuilder sb= new StringBuilder();
            while((readLine= br.readLine())!=null){
                if(readLine.charAt(0)=='-'){
                    continue;
                }else{
                    sb.append(readLine);
                    sb.append('\r');
                }
            }
            return loadPrivateKey(sb.toString());
        } catch (IOException e) {
            throw new Exception("私钥数据读取错误");
        } catch (NullPointerException e) {
            throw new Exception("私钥输入流为空");
        }
    }
    public static RSAPrivateKey loadPrivateKey(String privateKeyStr) throws Exception{
        try {
            BASE64Decoder base64Decoder= new BASE64Decoder();
            byte[] buffer= base64Decoder.decodeBuffer(privateKeyStr);
            PKCS8EncodedKeySpec keySpec= new PKCS8EncodedKeySpec(buffer);
            KeyFactory keyFactory= KeyFactory.getInstance("RSA");
            return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此算法");
        } catch (InvalidKeySpecException e) {
            throw new Exception("私钥非法");
        } catch (IOException e) {
            throw new Exception("私钥数据内容读取错误");
        } catch (NullPointerException e) {
            throw new Exception("私钥数据为空");
        }
    }


    public static RSAPublicKey loadPublicKey(InputStream in) throws Exception{
        try {
            BufferedReader br= new BufferedReader(new InputStreamReader(in));
            String readLine= null;
            StringBuilder sb= new StringBuilder();
            while((readLine= br.readLine())!=null){
                if(readLine.charAt(0)=='-'){
                    continue;
                }else{
                    sb.append(readLine);
                    sb.append('\r');
                }
            }
            return loadPublicKey(sb.toString());
        } catch (IOException e) {
            throw new Exception("公钥数据流读取错误");
        } catch (NullPointerException e) {
            throw new Exception("公钥输入流为空");
        }
    }

    /**
     * 从字符串中加载公钥
     * @param publicKeyStr 公钥数据字符串
     * @throws Exception 加载公钥时产生的异常
     */
    public static RSAPublicKey loadPublicKey(String publicKeyStr) throws Exception{
        try {
            BASE64Decoder base64Decoder= new BASE64Decoder();
            byte[] buffer= base64Decoder.decodeBuffer(publicKeyStr);
            KeyFactory keyFactory= KeyFactory.getInstance("RSA");
            X509EncodedKeySpec keySpec= new X509EncodedKeySpec(buffer);
            return (RSAPublicKey) keyFactory.generatePublic(keySpec);
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此算法");
        } catch (InvalidKeySpecException e) {
            throw new Exception("公钥非法");
        } catch (IOException e) {
            throw new Exception("公钥数据内容读取错误");
        } catch (NullPointerException e) {
            throw new Exception("公钥数据为空");
        }
    }
    /*
     * MD5加密
     * */ 
    public static String MD5MsgDigestDemo(String pubDate){
        MessageDigest md5Digest = null;
        try {
            md5Digest = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // 更新要计算的内容
        md5Digest.update(pubDate.getBytes());
        // 完成哈希计算,得到摘要
        byte[] md5Encoded = md5Digest.digest();
        System.out.println("原文: " + pubDate);
        System.out.println("MD5摘要: " + byte2HexStr(md5Encoded));
        return byte2HexStr(md5Encoded);
    }

    /*
     * 实现字节数组向十六进制的转换方法一
     */
    public static String byte2HexStr(byte[] b) {
        String hs = "";
        String stmp = "";
        for (int n = 0; n < b.length; n++) {
            stmp = (Integer.toHexString(b[n] & 0XFF));
            if (stmp.length() == 1)
                hs = hs + "0" + stmp;
            else
                hs = hs + stmp;
        }
        return hs.toUpperCase();
    }
    /*
     *测试方法 
     */
    public static void main(String[] args) throws Exception {    

//        HashMap<String, Object> map = RsaUtils.getKeys();    
//        //生成公钥和私钥    
//        RSAPublicKey publicKey = (RSAPublicKey) map.get("public");    
//        RSAPrivateKey privateKey = (RSAPrivateKey) map.get("private");    
//            
//        //模    
//        String modulus = publicKey.getModulus().toString();    
//        //公钥指数    
//        String public_exponent = publicKey.getPublicExponent().toString();    
//        //私钥指数    
//        String private_exponent = privateKey.getPrivateExponent().toString();    
        //明文    
        JSONObject obj = new JSONObject();
        obj.put("logNo", "201804270298407869");
        obj.put("UserId", "");
        obj.put("BalDate", "20180427");
        obj.put("AgentId", "172");
        obj.put("TxnLogId", "9115248173556540156342038");
        obj.put("BusinessId", "800461000001090");
        obj.put("SDTermNo", "95126798");
        obj.put("TxnCode", "N001");
        obj.put("PayChannel", "2");
        obj.put("TxnDate", "20180427");
        obj.put("TxnTime", "162238");
        obj.put("TxnAmt", "43.9");
        obj.put("TxnStatus", "1");
        obj.put("BankType", "");
        obj.put("OfficeId", "91524817356294700943");
        obj.put("ChannelId", "9115248173556540156342038");
        obj.put("attach", "");
        obj.put("CrdFlg", "");

        String dataText = obj.toString();

        System.out.println("原数据:"+dataText);
        //使用模和指数生成公钥和私钥    
     //   RSAPublicKey pubKey = RsaUtils.getPublicKey(modulus, public_exponent);    
     //   RSAPrivateKey priKey = RsaUtils.getPrivateKey(modulus, private_exponent); 
        /*
         *读取公钥 
         */
        File filepubkey = new File("C:/Users/hy133/Desktop/172_rsa_public_key.pem");
        FileInputStream finpubkey = new FileInputStream(filepubkey);
        RSAPublicKey pubKey =loadPublicKey(finpubkey);
         /*
         * 公钥加密
         * */  
//        String pubDate = encryptByPublicKey(dataText, pubKey);

//        System.out.println("加密数据:"+pubDate);

        //对加密数据进行MD5加密 *记得拼接MD5密钥*
//        String MD5key = "e10adc3949ba59abbe56e057f20f883e";
//        String MD5Data = pubDate + MD5key;//加签数据
//        System.out.println("MD5加签原数据:"+MD5Data);
//        
//        String signValue = MD5MsgDigestDemo(MD5Data);
//        System.out.println("MD5加签后数据:"+signValue);

        //组请求数据  测试。。
//        JSONObject object = new JSONObject();
//        object.put("Datas", pubDate);
//        object.put("singValue", md5MsgDigestData);
//        
//        System.out.println(object.toString());
        /*
         *读取私钥
         */
        File file = new File("C:/Users/hy133/Desktop/日常文件/172_pkcs8_rsa_private_key.pem");
        FileInputStream fin = new FileInputStream(file);
        RSAPrivateKey priKey = loadPrivateKey(fin);

        /*
         * 私钥解密
         * */  

        String pubDate = "8E6382D2E54CD984F3A8A15EE278E4ADD8914A1ADE4E746EDDF3A6E57EF5853DECAA6465D58B5EE56C48412E339C6FAFA7DA8FCA690A6FB4FFD914A7F87A8CA6BDFF41147CBB44652E7357E02C3D65138245AAEEA856114A0BE0B89ADD76E3D967C23A8D526F4C17969D57C022D0BEE74BBD638F91318B72858240E0BCE4C26A6184AE3C22A447D2B5A0B71DC9612F3D4D132FFCFE044521730A03A25E1CA9F59C30AA850FC4194F50D903745020F11ECC7A3BE849EACED699439EBC371BFDFE6C4AACD59E1F6E4A8E9C67BE5EBDF5F5C004ED99EA90108BD4058141117428687FD26CA8D672469891FA7BF114D9605466CE2237F61F87B86A4455DD6632B265874270D9FC9EB8B9EA8A4810BB13756D09224C843807D83D549E5EF1D31009B0A23D5FDA807B094FC776167F86E11ED4608F03E0E8ED645BCC6106CBD2530FF43CF1C86048CA85406F4F325923105EA1661673C72A0C9F4DB4CE64CE72FB657D3B8329EB83D3DD4704153EC6A6D802F941CCCB069A9E3F670FC8CC27C714150813CAD95596920116F8B12A61D5B617918C058A2791E9CC965DB97F0702BA82F66F386E9AC27EF81510837662EB538AA9C64D34CA1E6B7A327795A5429F5CE6A69CC4AE608CE2D2813825ABC4EE590209F45876A272F3ACB0617C889F1F3893CA024647777A58EC07D85D270908DE06870666CFD40DB78C0E699A5D15A4F04911";
        String priDate = decryptByPrivateKey(pubDate, priKey);   

        System.out.println("解密数据:"+priDate);

//        long time = System.currentTimeMillis();  
//        System.out.println(System.currentTimeMillis()-time); 
    }    




}

results matching ""

    No results matching ""