银联支付安卓版

复制于  http://www.360doc.com/content/15/0225/21/22062232_450809874.shtml 文章很详细,谢谢该作者!
 

参看 银联支付安卓版服务评测 中的测试文档

http://www.devstore.cn/evaluation/testInfo/98-133.html

Android-银联支付

 http://blog.csdn.net/qq285016127/article/details/38435585

银联支付也是一般比较常用的支付功能,这里简单了介绍android app如果短期快速应用这一方面的东西。直接上代码:

1.导入银联支付的依赖包:


2.在res目录下增加资源包:


3.配置AndroidManifest.xml文件配置打开的activity:

  1. <activity  
  2.             android:name="com.unionpay.uppay.PayActivity"  
  3.             android:configChanges="orientation|keyboardHidden"  
  4.             android:excludeFromRecents="true"  
  5.             android:label="@string/app_name"  
  6.             android:screenOrientation="portrait"  
  7.             android:windowSoftInputMode="adjustResize" />  
至此,我们的项目环境就搭好了.(以上资料文件,请查看银联测试文档目录的androiddemo);


银联支付代码3步骤:

1.获取TN号  2.请求控件界面(PayActivity)   

3.ActivityResult处理支付结果(但该结果并不一定正确 ,因为银联系统通过异步返回给服务器和我们的app是同时异步的)


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

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

商户服务器根据商户属性及订单属性向银联换取交易流水号(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");

后面的run方法中从服务器获取上面产生的tn.发送消息到MainActivity.


客户端从后台获取 tn,启动支付

 

UPPayAssistEx.startPayByJAR(this, PayActivity.classnullnulltnmode);

参数说明:

序号

说明

1

调用支付的Activity上下文

2

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

3

null即可

4

null即可

5

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

6

连接环境,传入“00”则连接银联生产环境,传入“01”则连接银联测试环境

 

I. 支付结果获取和处理

a) 客户端同步获取

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

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

requestCode=10Bundle[{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;

}

}

b) 商户服务器异步获取

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


1、 遇到问题

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

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

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

2、 上手难易

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

3、 开发文档和支持

 

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

 

UPMP商户接入接口规范》

 

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

 

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

4、 测试日志:

获取交易流水号:

 

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

 

5、 测试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 = "";

        /*

         * 支付控件返回字符串:successfailcancel

         *      分别代表支付成功,支付失败,支付取消

         */

        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.jarlibs目录下\n"

                + "2:根据需要拷贝sdk/jar/data.bin(或sdkPro/jar/data.bin)至工程的res/drawable目录下\n"

                + "3:根据需要拷贝sdk/jar/XXX/XXX.so(或sdkPro/jar/XXX/XXX.solibs目录下\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) + "]毫秒");

    }

}

 

6、 综合点评

1、 仔细阅读文档,熟悉银联支付接入流程;

2、 接入之前最好先运行资源包中附带的Demo,出现Demo和文档不一致的情况以Demo为准;

3、 一定要注意data.bin的位置,根据本人经验,不同2.1.3以上的银联版本把data.bin放到了jar包中,所以从2.1.3升级时一定要记得把之前放到drawable下的data.bin删除,否则会出现ANR

 

集成后对APK体积影响

237KB

响应是否及时

客户端同步响应,商户服务器约有1~3秒延迟

支付一次耗费流量

99.1KB

是否支持横屏

是,但需在AndroidManifest.xml中对银联所需的Activity的属性进行设置

Monkey测试

测试命令:adb shell monkey -p com.example.upay  1000

并未出现崩溃等异常,内存占用率平稳。

 

7、 适用人群

客户端:熟悉Android开发,java开发,最好对第三方支付接入有一定了解。

服务器:熟悉Java开发,为满足支付的高并发请求和尽量避免掉单,请设计好线程池调度。

8、 客服反馈

技术支持反馈速度一般且态度比较亲和,如果遇到技术问题他们会找相关技术,客服不是技术出身,回复没有预想的及时了,技术支持QQ769929732

 

9、 特色功能

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

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

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


  1. /** 
  2.  *      UnionPay Test 
  3.  * 
  4.  *  @author Lean  @date:2014-8-8   
  5.  */  
  6. public class MainActivity extends Activity implements Runnable {  
  7.   
  8.     private String mMode = "01";//设置测试模式:01为测试 00为正式环境  
  9.     private static final String TN_URL_01 = "http://202.101.25.178:8080/sim/gettn";//自己后台需要实现的给予我们app的tn号接口  
  10.   
  11.     private Handler mHandler=new Handler(){  
  12.           
  13.         public void handleMessage(android.os.Message msg) {  
  14.   
  15.             String tn = "";  
  16.             if (msg.obj == null || ((String) msg.obj).length() == 0) {  
  17.                 AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);  
  18.                 builder.setTitle("错误提示");  
  19.                 builder.setMessage("网络连接失败,请重试!");  
  20.                 builder.setNegativeButton("确定",  
  21.                         new DialogInterface.OnClickListener() {  
  22.                             @Override  
  23.                             public void onClick(DialogInterface dialog, int which) {  
  24.                                 dialog.dismiss();  
  25.                             }  
  26.                         });  
  27.                 builder.create().show();  
  28.             } else {  
  29.                 tn = (String) msg.obj;  
  30.                 doStartUnionPayPlugin(MainActivity.this, tn, mMode);  
  31.             }  
  32.         }  
  33.     };  
  34.       
  35.     /** 
  36.      *  启动支付界面 
  37.      */  
  38.     public void doStartUnionPayPlugin(Activity activity, String tn, String mode) {  
  39.         UPPayAssistEx.startPayByJAR(activity, PayActivity.class, null, null,  
  40.                 tn, mode);  
  41.     }  
  42.       
  43.       
  44.     @Override  
  45.     protected void onCreate(Bundle savedInstanceState) {  
  46.         super.onCreate(savedInstanceState);  
  47.         setContentView(R.layout.activity_main);  
  48.   
  49.         new Thread(MainActivity.this).start();  
  50.   
  51.     }  
  52.   
  53.       
  54.     @Override  
  55.     public void run() {  
  56.         String tn = null;  
  57.         InputStream is;  
  58.         try {  
  59.   
  60.             String url = TN_URL_01;  
  61.   
  62.             URL myURL = new URL(url);  
  63.             URLConnection ucon = myURL.openConnection();  
  64.             ucon.setConnectTimeout(120000);  
  65.             is = ucon.getInputStream();  
  66.             int i = -1;  
  67.             ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  68.             while ((i = is.read()) != -1) {  
  69.                 baos.write(i);  
  70.             }  
  71.   
  72.             tn = baos.toString();  
  73.             is.close();  
  74.             baos.close();  
  75.         } catch (Exception e) {  
  76.             e.printStackTrace();  
  77.         }  
  78.           
  79.         Message msg = mHandler.obtainMessage();  
  80.         msg.obj = tn;  
  81.         mHandler.sendMessage(msg);  
  82.     }  
  83.   
  84.     @Override  
  85.     protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  86.         if (data == null) {  
  87.             return;  
  88.         }  
  89.         String msg = "";  
  90.         /* 
  91.          * 支付控件返回字符串:success、fail、cancel 分别代表支付成功,支付失败,支付取消 
  92.          */  
  93.         String str = data.getExtras().getString("pay_result");  
  94.         Log.v("zftphone", "2 "+data.getExtras().getString("merchantOrderId"));  
  95.         if (str.equalsIgnoreCase("success")) {  
  96.             msg = "支付成功!";  
  97.               
  98.         } else if (str.equalsIgnoreCase("fail")) {  
  99.             msg = "支付失败!";  
  100.               
  101.         } else if (str.equalsIgnoreCase("cancel")) {  
  102.               
  103.             msg = "用户取消了支付";  
  104.         }  
  105.         //支付完成,处理自己的业务逻辑!  
  106.     }  
  107.   
  108. }  
展开阅读全文

没有更多推荐了,返回首页