DevStore首页 > DEV资源 > 服务评测下载 > 银联支付安卓版服务评测

银联支付安卓版服务评测

查看服务>>
DevStore编辑 刘艳丽 | 2014-07-25 11:00    浏览量(18879)    纠错    评论(11)    银联服务详情>>

服务评测作者

  • 8 能力值
  • 1 源码
  • 3 评测
  • 0 资料

评测目录

集成测试

评测环境

3G/wifi网络下:

测试对象

银联手机手机支付

测试版本

银联手机支付2.1.3

测试系统

Android 4.1.2

测试手机

HTC ONE

手机网络

3G/wifi

测试方法

客户端集成银联手机支付资源并调用API进行支付测试

测试时长

1小时

测试难度

简单

测试环境

Window7+Eclipse+HTC ONE

测试效果

稳定,流畅,支付安全


基本参数:

集成难度

SDK集成简单,但版本升级时需注意资源替换位置

操作难度

简单

支持系统

Android、IOS

稳定性

安全性

集成过程

客户端集成

一 客户端集成

银联手机支付SDK以分为无卡交易版本和有卡交易版本。有卡交易支持银联的迷你IC卡,智能SD卡及VIPOS等产品,普通手机客户端支付集成无卡交易版本即可。

1.获取SDK资源及文档

银联目前还未开放SDK下载,需和银联商务索取。

集成无卡交易支付需导入资源主要包括以下三部分:

    1)UPPayAssistEx.jar,UPPayPluginEx.jar;

    2)data.bin;

    3)armeabi/libentryex.so

    armeabi-v7a/libentryex.so

    mips/libentryex.so

    x86/libentryex

2.导入资源

    1)将sdknocard/UPPayAssistEx.jar和sdknocard/jar/UPPayPluginEx.jar复制到工程目录libs下,将sdknocard/jar/ 下的armeabi,armeabi-v7a,mips和x86四个文件夹及文件夹下的.so文件复制到工程目录的libs下。集成完效果图如下:

导入资源

    2)data.bin为图片资源文件,必须放在工程的res/drawable目录下。

集成效果图:

集成效果图

3.修改Manifest

    1)在工程的AndroidManifest.xml增加Activity声明:

<activity
            android:name="com.unionpay.uppay.PayActivityEx"
            android:configChanges="orientation|keyboardHidden|screenSize"
            android:excludeFromRecents="true"
            android:screenOrientation="portrait"
            android:windowSoftInputMode="adjustResize" >
        </activity>
        <activity android:name="com.unionpay.uppay.PayActivity" >

    2)添加权限声明:

<uses-permission 
        android:name="android.permission.INTERNET"/>
    <uses-permission 
        android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission 
        android:name="android.permission.CHANGE_NETWORK_STATE"/>
    <uses-permission 
        android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission 
        android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission 
        android:name="android.permission.ACCESS_WIFI_STATE"/>

至此,银联SDK资源导入完毕。


二 订单支付流程

在调用客户端支付接口之前,商户服务器需先向银联服务器获取交易流水号(TN),该值将作为调用客户端API参数之一。商户消费流程图:

商户消费流程图

获取TN号后就可调用SDK的API发起支付操作。

1.服务器获取交易流水号

商户服务器根据商户属性及订单属性向银联换取交易流水号(TN)

Map<String, String> req = new HashMap<String, String>();
req.put("version", "2.1.3");// 版本号
req.put("charset", "UTF-8");// 字符编码
req.put("transType", "01");// 交易类型
req.put("merId", "*******");// 商户代码
req.put("backEndUrl", "**********");// 通知URL
req.put("frontEndUrl", "*********");// 前台通知URL(可选)
req.put("orderDescription", "订单描述");// 订单描述(可选)
req.put("orderTime", new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()));// 交易开始日期时间yyyyMMddHHmmss
req.put("orderTimeout", "");// 订单超时时间yyyyMMddHHmmss(可选)
req.put("orderNumber", "*******");// 订单号(商户根据自己需要生成订单号)
req.put("orderAmount", "**********");// 订单金额
req.put("orderCurrency", "156");// 交易币种(可选)
req.put("reqReserved", "");// 请求方保留域(可选,用于透传商户信息)
req.put("merReserved", "");// 商户保留域(可选)
RequestParams params = new RequestParams(req);
//网络请求
//...
//网络请求
String tn = resp.get("tn");

2.客户端调用SDK支付API

UPPayAssistEx.startPayByJAR(this①, PayActivity.class②, null③, null④, tn⑤, mode⑥);

参数说明:

序号

说明

1

调用支付的Activity上下文

2

支付插件代表类,填入“PayActivity.class”即可

3

填null即可

4

填null即可

5

传入通过银联获取的交易流水号(TN)

6

连接环境,传入则连接银联生产环境,传入则连接银联测试环境

3.支付结果获取和处理

    1)客户端同步获取

客户端需在onActivityResult中监听银联支付结果。银联将支付结果包装到了Intent data中,且requestCode=10。

客户端支付结果回调信息:

requestCode=10,Bundle[{pay_result=success}]

客户端支付结果代码示例:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case 10:
Bundle b = data.getExtras();
        String str = b.getString("pay_result");
        if (str.equalsIgnoreCase("success")) {
         Log.d("UPTest","onActivityResult,银联支付成功");
        } else if (str.equalsIgnoreCase("fail")) {
         Log.d("UPTest","onActivityResult,银联支付失败");
        } else if (str.equalsIgnoreCase("cancel")) {
         Log.d("UPTest","onActivityResult,银联支付取消");
        }
break;
 
default:
break;
}
}

    2)商户服务器异步获取

成功进行支付操作后,银联会通过post形式将商品支付信息通知到商户服务器。银联为支付通知设置了10秒超时时间,所以商户接到银联支付结果并确认无误后应立即答复银联返回码为200的通知,否则银联会认为通知失败。此外,超过10秒未答复银联也会认为是通知失败。银联会多次发送通知失败的支付结果,次数最多为5次。此外,由于网络等客观环境原因,商户可能会多次通知同一次支付结果,这时需商户在自己服务器进行逻辑判断并处理。

主要功能

常用银行卡支付:

常用银行卡支付

支持银行列表:

支持银行列表

银联账号登录:

银联账号登录

手机号码支付:

手机号码支付

功能特色

1、支持多达两千多个银行;

2、支持信用卡、借记卡及预付卡三种银行卡进行支付;

3、支持Debug联调模式,方便商户进行测试联调。

DEMO展示

一 客户端测试Demo

BaseActivity:

public abstract class BaseActivity 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 String mMode = "01";
    private static final String TN_URL_01 = "http://222.66.233.198:8080/sim/gettn";
 
    private 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(BaseActivity.this).run();
        }
    };
 
    public abstract void doStartUnionPayPlugin(Activity activity, String tn,
            String mode);
 
    @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);
 
        TextView tv = (TextView) findViewById(R.id.guide);
        tv.setTextSize(16);
        updateTextView(tv);
    }
 
    public abstract void updateTextView(TextView tv);
 
    @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")) {
            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;
    }
}

JARActivity:

public class JARActivity extends BaseActivity {
 
    @Override
    public void doStartUnionPayPlugin(Activity activity, String tn, String mode) {
        UPPayAssistEx.startPayByJAR(activity, PayActivity.class, null, null,
                tn, mode);
    }
 
    @Override
    public void updateTextView(TextView tv) {
        String txt = "接入指南:\n1:拷贝sdk目录下的UPPayAssistEx.jar到libs目录下\n"
                + "2:根据需要拷贝sdk/jar/data.bin(或sdkPro/jar/data.bin)至工程的res/drawable目录下\n"
                + "3:根据需要拷贝sdk/jar/XXX/XXX.so(或sdkPro/jar/XXX/XXX.so)libs目录下\n"
                + "4:根据需要拷贝sdk/jar/UPPayPluginEx.jar(或sdkPro/jar/UPPayPluginExPro.jar)到工程的libs目录下"
                + "5:获取tn后通过UPPayAssistEx.startPayByJar(...)方法调用控件";
        tv.setText(txt);
    }
}

二 商户服务器测试Demo

获取交易流水号:

public Object execute(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        Map<String, String> result = new HashMap<String, String>();
        Map<String, String> map = new HashMap<String, String>();
        String tn = "";
        String status = "";
        try {
            String data = request.getParameter("data");
            log.info(MessageFormat.format("[UPMPCREATETN]INFO[START]DATA[{0}]", data));
            String orderId = ""; // 订单编号
            String goodsPrice = ""; // 商品价格
            JSONObject jsonobject = JSONObject.fromObject(data);
            if (jsonobject.has("orderId")) {
                orderId = jsonobject.getString("orderId");// 订单编号
            }
            if (jsonobject.has("goodsPrice")) {
                goodsPrice = jsonobject.getString("goodsPrice");// 商品价格
            }
            log.info(MessageFormat.format("[UPMPCREATETN]ORDERID[{0}]GOOGSPRICE[{1}]]", orderId,
                    goodsPrice));
            if (StringUtils.isEmpty(orderId) || StringUtils.isEmpty(goodsPrice)) {
                status = "-1";// 参数不全
                log.error(MessageFormat.format("[UPMPCREATETN]ERROR[{0}]", "appkey or orderId or goodsPrice is null"));
            } else {
                SdkOrder order_exizt = OrderService.getOrderByIdAndPrice(orderId, Double.valueOf(goodsPrice));
                if (null != order_exizt) {
                    result = RequestUpmpService.RequestTn(orderId, AmountUtils.changeY2F(goodsPrice));
                    log.info("[请求流水号返回结果]:" + result);
                    if (result.containsKey("tn")) {
                        if (StringUtils.isEmpty(result.get("tn"))) {
                            if (result.containsKey("status")) {
                                status = result.get("status");
                            } else {
                                status = "-3";
                            }
                        } else {
                            tn = result.get("tn");
                            status = "1";
                        }
                    } else {
                        status = "-3";
                    }
                }
                else{
                    status = "-2";// 订单不存在
                    log.error(MessageFormat.format("ERROR[{0}]ORDERID[{1}]GOODPRICE[{2}]", "order is not exizt!",
                            orderId, goodsPrice));
               }
            }
} catch (Exception e) {
            status = "-3";
log.error(MessageFormat.format("[UPMPCREATETN]ERROR[{0}]", CommonUtil.getStackTrace(e)));
}
        map.put("status", status);
        map.put("tn", tn);
        return map;
}

测试银联支付回调:

public class SDKUpmpPayServlet extends HttpServlet {
    /**
     * @Fields serialVersionUID : Description
     */
 
    private static final long serialVersionUID = 1L;
 
    private static final Logger logger = Logger.getLogger(SDKUpmpPayServlet.class);
 
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
 
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException {
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        String result = "";
        long start = System.currentTimeMillis();
        try {
logger.info("银联支付回调开始");
            Map<String, String> map = new HashMap<String, String>();
 
            Enumeration<String> enumeration = request.getParameterNames();
            while (enumeration.hasMoreElements()) {
                String key = enumeration.nextElement();
                String value = request.getParameter(key);
                map.put(key, value);
            }
            boolean flag = false;
logger.info("银联支付回调参数:[" + map + "]");
            String respSignature = "";
            String orderId_cy = "";
            String orderId_up = "";
            String goodsPrice = "";
            if (map.containsKey("signature") && map.containsKey("orderNumber") && map.containsKey("qn")
                    && map.containsKey("settleAmount")) {
                respSignature = map.get("signature");
                orderId_cy = map.get("orderNumber");
                orderId_up = map.get("qn");
                goodsPrice = map.get("settleAmount");
// 除去数组中的空值和签名参数
                Map<String, String> filteredReq = UpmpCore.paraFilter(map);
                String signature = UpmpCore.buildSignature(filteredReq);
                if (null != respSignature && respSignature.equals(signature)) {
                    flag = true;
                } else {
                    flag = false;
                }
                if (flag) {
flag = SDKUtil.requestBilling(orderId_cy, orderId_up,
AmountUtils.changeF2Y(goodsPrice),
ChannelPayWayMapper.UPMP);
                    if (flag) {
                        PrintWriter pw = response.getWriter();
                        result = " success";
                        pw.print(result);
                        pw.close();
                    } else {
                        result = " failed";
logger.error("银联支付回调失败:请求billing返回失败 ");
                    }
                } else {
                    result = " failed";
logger.error("银联支付回调失败:签名验证失败 ");
                }
            } else {
                result = " failed";
logger.error("银联支付回调失败:关键参数为空 ");
            }
        } catch (Exception e) {
logger.error("银联支付回调异常:" + e);
        }
logger.info("银联支付回调处理结束,返回[" + result + "]耗时["
+ (System.currentTimeMillis() - start) + "]毫秒");
    }
}

测试日志

获取交易流水号:

获取交易流水号

客户端收到银联支付结果:

客户端收到银联支付结果

遇到问题

1.一定要注意将data.bin放到项目的res/drawable目录下,否则图片读取会出错。

2.如果使用2.1.3以上版本,银联将data.bin放到了UPPayPluginEx.jar中,所以不必再将data.bin放到res/drawable目录。

3.请确保将.so文件成功打到apk包中(可将apk包当做zip解压检查/lib目录下是否存在.so文件),否则无法成功打开银联支付。

上手难易

银联的文档写得较乱,并且和内置的demo实际代码有冲突,所以接入初期会比较复杂,但银联的API调用比较简单,综上,客户端和服务器分别用半天时间进行接入调试应该就可以了。

开发文档

开发文档和支持:

《UPMP商户接入技术改造指南》

《UPMP商户接入接口规范》

《银联手机支付控件使用指南(Android平台Jar包集成)》

《中国银联手机支付控件使用指南》

此服务评测版权归DevStore所有,禁止转载,申请升级为 特约评测员 才可进行测评立即申请

声明:DevStore评测内容都是基于专业评测人员/开发者通过真实的测试之后得出的数据,服务版本实时都在更新,所以评测并不一定是此服务的最新版本,但我们会秉承公正专业精准的态度,对开发者负责,同时欢迎大家监督和建议,如对评测内容有异议,请提交纠错,由专业的评测团队再次评测,我们会尽最大努力为大家提供更贴心的服务。

DevStore_全球首家第三方开发者服务商店,最精准的服务对比、最专业的服务评测、最及时的行业动态,为开发者挑选服务提供最全面的参考和专业分析,加入DevStore,从此告别熬夜加班,你也可以这么帅!搜索微信号:DevStore

评论(11)
wb107

wb107 2015-05-20 16:08:26

没钱下载啊~~
回复(1) 赞(1) 赞(2)
 
  • m1l4p7:
    回复@wb107:我也是,只能下载一个
    回复2015-08-24 09:48:00
funsten

funsten 2015-04-22 13:07:29

1)在工程的AndroidManifest.xml增加Activity声明:
?
1
2
3
4
5
6
7
8


这个地方好像就有问题了
回复(0) 赞(2) 赞(3)
funsten

funsten 2015-04-21 22:24:11

多谢分享,现在就要做银联了,希望有帮助。
回复(0) 赞(1) 赞(2)
宇极

宇极 2015-04-15 17:06:21

哥们 有时间希望可以探讨一下啊 qq:906932256
回复(0) 赞(1) 赞(2)
weibai

weibai 2015-01-22 11:12:29

想问下,银联的东西怎么不全啊!
回复(0) 赞(1) 赞(2)