银联支付的使用(手机控件支付)

时间:2024-02-15 19:16:51

银联开发平台官网:https://open.unionpay.com/ajweb/product/detail?id=3  下载手机控件支付的sdk与代码:

下载解压如下:指导文档和jar包啥的都有 (测试账号银行卡也有)

 

 

集成步骤:

1.导入jar包与so文件:

2.清单文件的注册:

<!-- 银联支付需要的   application标签下-->
        <uses-library
            android:name="org.simalliance.openmobileapi"
            android:required="false" />

        <activity
            android:name="com.unionpay.uppay.PayActivity"
            android:configChanges="orientation|keyboardHidden"
            android:excludeFromRecents="true"
            android:label="@string/app_name"
            android:screenOrientation="portrait"
            android:windowSoftInputMode="adjustResize" />
        <activity
            android:name="com.unionpay.UPPayWapActivity"
            android:configChanges="orientation|keyboardHidden"
            android:screenOrientation="portrait"
            android:windowSoftInputMode="adjustResize" />

3.支付类:MainActivity.java

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

import org.json.JSONException;
import org.json.JSONObject;

import com.unionpay.UPPayAssistEx;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity implements Callback,
        Runnable {
    public static final String LOG_TAG = "PayDemo";
    private Context mContext = null;
    private int mGoodsIdx = 0;
    private Handler mHandler = null;
    private ProgressDialog mLoadingDialog = null;

    public static final int PLUGIN_VALID = 0;
    public static final int PLUGIN_NOT_INSTALLED = -1;
    public static final int PLUGIN_NEED_UPGRADE = 2;

    /*****************************************************************
     * mMode参数解释: "00" - 启动银联正式环境 "01" - 连接银联测试环境
     *****************************************************************/
    private final String mMode = "01";
    
    /**
     * 获取订单号的网址  实际需要改为后台的接口
     */
    private static final String TN_URL_01 = "http://101.231.204.84:8091/sim/getacptn";

    /**
     * 单击事件的回调,去支付
     */
    private final View.OnClickListener mClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.e(LOG_TAG, " " + v.getTag());
            mGoodsIdx = (Integer) v.getTag();

            mLoadingDialog = ProgressDialog.show(mContext, // context
                    "", // title
                    "正在努力的获取tn中,请稍候...", // message
                    true); // 进度是否是不确定的,这只和创建进度条有关

            /*************************************************
             * 步骤1:从网络开始,获取交易流水号即TN(订单号)
             ************************************************/
            new Thread(MainActivity.this).start();
        }
    };

    /**
     * 吊起支付
     * @param activity 上下文对象
     * @param tn   订单号
     * @param mode 模式 // “00” – 银联正式环境                      “01” – 银联测试环境,该环境中不发生真实交易

     */
    public void doStartUnionPayPlugin(Activity activity, String tn,String mode){
        // mMode参数解释:
        // 0 - 启动银联正式环境
        // 1 - 连接银联测试环境  tn:订单号
        int ret = UPPayAssistEx.startPay(this, null, null, tn, mode);
        if (ret == PLUGIN_NEED_UPGRADE || ret == PLUGIN_NOT_INSTALLED) {
            // 需要重新安装控件
            Log.e(LOG_TAG, "插件没有发现或需要升级");//需要去安装
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle("提示");
            builder.setMessage("完成购买需要安装银联支付控件,是否安装?");

            builder.setNegativeButton("确定",
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            UPPayAssistEx.installUPPayPlugin(MainActivity.this);
                            dialog.dismiss();
                        }
                    });

            builder.setPositiveButton("取消",
                    new DialogInterface.OnClickListener() {

                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            dialog.dismiss();
                        }
                    });
            builder.create().show();

        }
        Log.e(LOG_TAG, "" + ret);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mContext = this;
        mHandler = new Handler(this);

        setContentView(R.layout.activity_main);

        Button btn0 = (Button) findViewById(R.id.btn0);
        btn0.setTag(0);
        btn0.setOnClickListener(mClickListener);//按钮,点击去支付
    }

    @Override
    public boolean handleMessage(Message msg) {
        Log.e(LOG_TAG, " " + "" + msg.obj);
        if (mLoadingDialog.isShowing()) {
            mLoadingDialog.dismiss();
        }

        String tn = "";
        if (msg.obj == null || ((String) msg.obj).length() == 0) {
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle("错误提示");
            builder.setMessage("网络连接失败,请重试!");
            builder.setNegativeButton("确定",
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            dialog.dismiss();
                        }
                    });
            builder.create().show();
        } else {
            tn = (String) msg.obj;
            /*************************************************
             * 步骤2:通过银联工具类启动支付插件
             ************************************************/
            doStartUnionPayPlugin(this, tn, mMode);//接口回调了---->去启动支付
        }

        return false;
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        /*************************************************
         * 步骤3:处理银联手机支付控件返回的支付结果
         ************************************************/
        if (data == null) {
            return;
        }

        String msg = "";
        /*
         * 支付控件返回字符串:success、fail、cancel 分别代表支付成功,支付失败,支付取消
         */
        String str = data.getExtras().getString("pay_result");
        if (str.equalsIgnoreCase("success")) {
            
            // 如果想对结果数据验签,可使用下面这段代码,但建议不验签,直接去商户后台查询交易结果
            // result_data结构见c)result_data参数说明
            if (data.hasExtra("result_data")) {
                String result = data.getExtras().getString("result_data");
                try {
                    JSONObject resultJson = new JSONObject(result);
                    String sign = resultJson.getString("sign");
                    String dataOrg = resultJson.getString("data");
                    // 此处的verify建议送去商户后台做验签
                    // 如要放在手机端验,则代码必须支持更新证书 
                    boolean ret = verify(dataOrg, sign, mMode);//下面直接返回true了
                    if (ret) {
                        // 验签成功,显示支付结果
                        msg = "支付成功!";
                    } else {
                        // 验签失败
                        msg = "支付失败!";
                    }
                } catch (JSONException e) {
                }
            } 
            // 结果result_data为成功时,去商户后台查询一下再展示成功
            msg = "支付成功!";
        } else if (str.equalsIgnoreCase("fail")) {
            msg = "支付失败!";
        } else if (str.equalsIgnoreCase("cancel")) {
            msg = "用户取消了支付";
        }

        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("支付结果通知");
        builder.setMessage(msg);
        builder.setInverseBackgroundForced(true);
        // builder.setCustomTitle();
        builder.setNegativeButton("确定", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();//支付完成界面返回来的显示
            }
        });
        builder.create().show();
    }

    @Override
    public void run() {
        String tn = null;
        InputStream is;
        try {

            String url = TN_URL_01;

            URL myURL = new URL(url);
            URLConnection ucon = myURL.openConnection();
            ucon.setConnectTimeout(120000);
            is = ucon.getInputStream();
            int i = -1;
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            while ((i = is.read()) != -1) {
                baos.write(i);
            }

            tn = baos.toString();
            is.close();
            baos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        Message msg = mHandler.obtainMessage();
        msg.obj = tn;
        mHandler.sendMessage(msg);
    }

    int startpay(Activity act, String tn, int serverIdentifier) {
        return 0;
    }

    private boolean verify(String msg, String sign64, String mode) {
        // 此处的verify,商户需送去商户后台做验签  这里直接返回true了
        return true;

    }

}

4.工具类,RSAUtil.java

import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
import android.util.Base64;

public class RSAUtil {

    public static final String RSA = "RSA";
    public static final String RSA_PADDING_MODE = "RSA";
    public static final String ALGORITHM_RSA_SIGN = "SHA1withRSA";
    private static final String RSA_PKCS1PADDING = "RSA/ECB/PKCS1Padding";
    private static final String RSA_NOPADDING = "RSA/ECB/NoPadding";
    public static final int RSAKEYLEN = 2048;

    /** key_lable:key_lable */
    public static final String KEY_LABEL = "key_label";

    /** data:data */
    public static final String DATA = "data";

    /** text:text */
    public static final String TEXT = "text";

    private static PrivateKey privateKey;
    private static PublicKey publicKey;

    public static PublicKey clientPublicKey;

    public static PublicKey getPublicKey() {
        return publicKey;
    }

    public static PrivateKey getPrivateKey() {
        return privateKey;
    }

    /**
     * RSA加密运算。
     * 
     * @param data
     * @param publicKey
     * @return 加密结果
     */
    public static byte[] encrypt(byte[] data, PublicKey publicKey) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_PADDING_MODE);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            return cipher.doFinal(data);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * RSA解密运算。
     * 
     * @param data
     * @param privateKey
     * @return 解密成功则返回解密结果,否则返回null.
     */
    public static byte[] decrypt(byte[] data) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_PADDING_MODE);
            cipher.init(Cipher.DECRYPT_MODE, getPrivateKey());
            return cipher.doFinal(data);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * RSA解密运算
     * 
     * @param priKey
     * @param data
     * @param padding
     * @return
     */
    public static byte[] decrypt(PrivateKey priKey, byte[] data,
            String padding, String provider) {
        try {
            Cipher cipher = Cipher.getInstance(padding, provider);
            cipher.init(Cipher.DECRYPT_MODE, priKey);
            return cipher.doFinal(data);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 根据模数和公钥指数生成公钥
     * 
     * @param modulus
     * @param publicExponent
     * @return 公钥
     */
    // public static PublicKey generateRSAPublicKey(String modulus,
    // String publicExponent) {
    // try {
    // KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    // RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger(
    // modulus), new BigInteger(publicExponent));
    //
    // PublicKey publicKey = keyFactory.generatePublic(pubKeySpec);
    // return publicKey;
    // // return keyFactory.generatePublic(pubKeySpec);
    // } catch (Exception e) {
    // throw new RuntimeException(e);
    // }
    // }

    /**
     * 根据字节流产生公钥
     * 
     * @param key
     * @return 公钥
     */
    public static PublicKey generateRSAPublicKey(byte[] key) {
        try {
            X509EncodedKeySpec bobPubKeySpec = new X509EncodedKeySpec(key);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PublicKey pubKey = keyFactory.generatePublic(bobPubKeySpec);
            return pubKey;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 根据字节流产生私钥
     * 
     * @param key
     * @return 私钥
     */
    public static PrivateKey generateRSAPrivateKey(byte[] key) {
        try {
            PKCS8EncodedKeySpec pkcs8keyspec = new PKCS8EncodedKeySpec(key);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey priKey = keyFactory.generatePrivate(pkcs8keyspec);
            return priKey;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 根据模和指数生成私钥
     * 
     * @param modulus
     * @param privateExponent
     * @return 私钥
     */
    public static PrivateKey generateRSAPrivateKey(String modulus,
            String privateExponent) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(RSA);
            RSAPrivateKeySpec pubKeySpec = new RSAPrivateKeySpec(
                    new BigInteger(modulus), new BigInteger(privateExponent));
            return keyFactory.generatePrivate(pubKeySpec);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 使用公钥对数据进行加密,并返回byte[]类型
     * 
     * @param publicKey
     * @param data
     * @return
     * @throws Exception
     */
    public static byte[] encryptDataBytes(PublicKey publicKey, byte[] data)
            throws Exception {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            int blockSize = cipher.getBlockSize();
            int outputSize = cipher.getOutputSize(data.length);
            int leavedSize = data.length % blockSize;
            int blocksSize = leavedSize != 0 ? data.length / blockSize + 1
                    : data.length / blockSize;
            byte[] raw = new byte[outputSize * blocksSize];
            int i = 0;
            while (data.length - i * blockSize > 0) {
                if (data.length - i * blockSize > blockSize) {
                    cipher.doFinal(data, i * blockSize, blockSize, raw, i
                            * outputSize);
                } else {
                    cipher.doFinal(data, i * blockSize, data.length - i
                            * blockSize, raw, i * outputSize);
                }
                i++;
            }
            return raw;
        } catch (Exception e) {
            throw new Exception(e.getMessage());
        }
    }

    public static PrivateKey getPrivateKey(String priKeyData) throws Exception {
        /*
         * n:512 e:512 d:512 p:256 q:256 dmp1:256 dmq1:256 iqmp:256
         */
        BigInteger modulus = new BigInteger(priKeyData.substring(8, 512 + 8),
                16);
        BigInteger publicExponent = new BigInteger(priKeyData.substring(
                512 + 8, 512 + 8 + 512), 16);
        BigInteger privateExponent = new BigInteger(priKeyData.substring(
                512 + 8 + 512, 512 + 8 + 512 + 512), 16);
        BigInteger primeP = new BigInteger(priKeyData.substring(
                512 + 8 + 512 + 512, 512 + 8 + 512 + 512 + 256), 16);
        BigInteger primeQ = new BigInteger(priKeyData.substring(512 + 8 + 512
                + 512 + 256, 512 + 8 + 512 + 512 + 256 + 256), 16);
        BigInteger primeExponentP = new BigInteger(
                priKeyData.substring(512 + 8 + 512 + 512 + 256 + 256, 512 + 8
                        + 512 + 512 + 256 + 256 + 256), 16);
        BigInteger primeExponentQ = new BigInteger(priKeyData.substring(512 + 8
                + 512 + 512 + 256 + 256 + 256, 512 + 8 + 512 + 512 + 256 + 256
                + 256 + 256), 16);
        BigInteger crtCoefficient = new BigInteger(priKeyData.substring(512 + 8
                + 512 + 512 + 256 + 256 + 256 + 256, 512 + 8 + 512 + 512 + 256
                + 256 + 256 + 256 + 256), 16);

        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        RSAPrivateCrtKeySpec rsaPrivateKeySpec = new RSAPrivateCrtKeySpec(
                modulus, publicExponent, privateExponent, primeP, primeQ,
                primeExponentP, primeExponentQ, crtCoefficient);
        return keyFactory.generatePrivate(rsaPrivateKeySpec);
    }

    public static PublicKey getPublicKey(String modulus, String publicExponent)
            throws NoSuchAlgorithmException, InvalidKeySpecException {

        BigInteger bigIntModulus = new BigInteger(modulus, 16);

        BigInteger bigIntPublicExponent = new BigInteger(publicExponent, 16);

        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(bigIntModulus,
                bigIntPublicExponent);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(keySpec);
        return publicKey;
    }

    /**
     * 根据模数和公钥指数生成公钥
     * 
     * @param modulus
     * @param publicExponent
     * @return 公钥
     */
    public static PublicKey generateRSAPublicKey(String modulus,
            String publicExponent) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger(
                    modulus), new BigInteger(publicExponent));
            return keyFactory.generatePublic(pubKeySpec);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static PublicKey getPublicKeyPM() {
        // 请将此处的module换成PM环境商户验签的公钥模数
        String modulus = "24870613246304283289263670822577417714537477136695312218046086562441084140352408862449003198972758030370375896331356438381534807815999481415930217971513079824183591552429779125222230389655838097565141139205829591128287005548898062000970767426912014994392229218979869216370190349843903870279325956661459861716847460988265260792970759967490015941772320263508330685602563839220027394572548955687677315821727057921756004005781874479358265172016335126486731385109336772938263090077762887508722625235251295041241798219236919770312254416281253815794530657627243362881204125234159183339122880098511453026644263131341899862471";
        String publicExponent = "65537";
        PublicKey publicKey = RSAUtil.generateRSAPublicKey(modulus,
                publicExponent);
        return publicKey;
    }

    public static PublicKey getPublicKeyProduct() {
        // 请将此处的module换成生产环境商户验签的公钥模数
        String modulus = "24882698307025187401768229621661046262584590315978248721358993520593720674589904440569546585666019820242051570504151753011145804842286060932917913063481673780509705461614953345565639235206110825500286080970112119864280897521494849627888301696007067301658192870705725665343356870712277918685009799388229000694331337917299248049043161583425309743997726880393752539043378681782404204317246630750179082094887254614603968643698185220012572776981256942180397391050384441191238689965500817914744059136226832836964600497185974686263216711646940573711995536080829974535604890076661028920284600607547181058581575296480113060083";
        String publicExponent = "65537";
        PublicKey publicKey = RSAUtil.generateRSAPublicKey(modulus,
                publicExponent);
        return publicKey;
    }

    public static boolean verifyPM(byte[] message, byte[] signature)
            throws Exception {
        Signature sig = Signature.getInstance("SHA1withRSA");
        sig.initVerify(getPublicKeyPM());
        sig.update(message);
        return sig.verify(signature);
    }

    public static boolean verifyProduct(byte[] message, byte[] signature)
            throws Exception {
        Signature sig = Signature.getInstance("SHA1withRSA");
        sig.initVerify(getPublicKeyProduct());
        sig.update(message);
        return sig.verify(signature);
    }

    public static String sha1(byte[] raw) {

        MessageDigest messageDigest;
        try {
            messageDigest = MessageDigest.getInstance("SHA-1");
            messageDigest.reset();
            messageDigest.update(raw);
            byte[] bytes = messageDigest.digest();
            return bytesToHex(bytes);
        } catch (Exception e) {

            return null;
        }
    }

    public static boolean verify(String msg, String sign64, String mode) {
        boolean ret = false;
        try {
            if ("01".equals(mode)) {
                ret = RSAUtil.verifyPM(RSAUtil.sha1(msg.getBytes()).getBytes(),
                        Base64.decode(sign64, Base64.NO_WRAP));
            } else if ("00".equals(mode)) {
                ret = RSAUtil.verifyProduct(RSAUtil.sha1(msg.getBytes())
                        .getBytes(), Base64.decode(sign64, Base64.NO_WRAP));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return ret;
    }

    /**
     * 将16进制的字符串转换成bytes
     * 
     * @param hex
     * @return 转化后的byte数组
     */
    public static byte[] hexToBytes(String hex) {
        return hexToBytes(hex.toCharArray());
    }

    /**
     * 将16进制的字符数组转换成byte数组
     * 
     * @param hex
     * @return 转换后的byte数组
     */
    public static byte[] hexToBytes(char[] hex) {
        int length = hex.length / 2;
        byte[] raw = new byte[length];
        for (int i = 0; i < length; i++) {
            int high = Character.digit(hex[i * 2], 16);
            int low = Character.digit(hex[i * 2 + 1], 16);
            int value = (high << 4) | low;
            if (value > 127) {
                value -= 256;
            }
            raw[i] = (byte) value;
        }
        return raw;
    }

    /**
     * 将byte数组转换成16进制字符串
     * 
     * @param bytes
     * @return 16进制字符串
     */
    public static String bytesToHex(byte[] bytes) {
        String hexArray = "0123456789abcdef";
        StringBuilder sb = new StringBuilder(bytes.length * 2);
        for (byte b : bytes) {
            int bi = b & 0xff;
            sb.append(hexArray.charAt(bi >> 4));
            sb.append(hexArray.charAt(bi & 0xf));
        }
        return sb.toString();
    }

    public static String publicDecrypt(PublicKey key, byte[] enc) {
        Cipher cipher = null;
        String decText = "";

        if (null == enc) {
            return decText;
        }

        try {
            cipher = Cipher.getInstance(RSA_NOPADDING);
            // byte[] data = PBOCUtils.hexStringToBytes(message);
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] enBytes = cipher.doFinal(enc);
            decText = bytesToHex(enBytes);
            // decText = new String(enBytes);
            // Log.e("RSA", "encText:" + decText);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return decText;

    }
}
View Code

支付流程图:

1.可见我们安卓端只需要访问后台的接口,拿订单号就行了

2.支付结果的确定(向后台确定为好)

 可参考:http://blog.csdn.net/qq_33078541/article/details/50580102

效果图: