银联支付安卓版
来源:互联网 发布:江苏11选5遗漏数据 编辑:程序博客网 时间:2024/04/30 20:16
参看 银联支付安卓版服务评测 中的测试文档
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:
- <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" />
银联支付代码3步骤:
1.获取TN号 2.请求控件界面(PayActivity)
3.ActivityResult处理支付结果(但该结果并不一定正确 ,因为银联系统通过异步返回给服务器和我们的app是同时异步的)
获取TN号后就可调用SDK的API发起支付操作。
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.class②, null③, null④, tn⑤, mode⑥);
参数说明:
序号
说明
1
调用支付的Activity上下文
2
支付插件代表类,填入“PayActivity.class”即可
3
填null即可
4
填null即可
5
传入通过银联获取的交易流水号(TN)
6
连接环境,传入“00”则连接银联生产环境,传入“01”则连接银联测试环境
I. 支付结果获取和处理
a) 客户端同步获取
客户端需在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;
}
}
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 = "";
/*
* 支付控件返回字符串: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) + "]毫秒");
}
}
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、 客服反馈
技术支持反馈速度一般且态度比较亲和,如果遇到技术问题他们会找相关技术,客服不是技术出身,回复没有预想的及时了,技术支持QQ:769929732
9、 特色功能
1、 支持多达两千多个银行;
2、 支持信用卡、借记卡及预付卡三种银行卡进行支付;
3、 支持Debug联调模式,方便商户进行测试联调;
- /**
- * UnionPay Test
- *
- * @author Lean @date:2014-8-8
- */
- public class MainActivity extends Activity implements Runnable {
- private String mMode = "01";//设置测试模式:01为测试 00为正式环境
- private static final String TN_URL_01 = "http://202.101.25.178:8080/sim/gettn";//自己后台需要实现的给予我们app的tn号接口
- private Handler mHandler=new Handler(){
- public void handleMessage(android.os.Message msg) {
- String tn = "";
- if (msg.obj == null || ((String) msg.obj).length() == 0) {
- AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.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;
- doStartUnionPayPlugin(MainActivity.this, tn, mMode);
- }
- }
- };
- /**
- * 启动支付界面
- */
- public void doStartUnionPayPlugin(Activity activity, String tn, String mode) {
- UPPayAssistEx.startPayByJAR(activity, PayActivity.class, null, null,
- tn, mode);
- }
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- new Thread(MainActivity.this).start();
- }
- @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);
- }
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (data == null) {
- return;
- }
- String msg = "";
- /*
- * 支付控件返回字符串:success、fail、cancel 分别代表支付成功,支付失败,支付取消
- */
- String str = data.getExtras().getString("pay_result");
- Log.v("zftphone", "2 "+data.getExtras().getString("merchantOrderId"));
- if (str.equalsIgnoreCase("success")) {
- msg = "支付成功!";
- } else if (str.equalsIgnoreCase("fail")) {
- msg = "支付失败!";
- } else if (str.equalsIgnoreCase("cancel")) {
- msg = "用户取消了支付";
- }
- //支付完成,处理自己的业务逻辑!
- }
- }
- 银联支付安卓版
- javaWeb微信支付+支付宝支付+银联支付
- Android支付-银联支付
- 银联支付 支付代码
- 银联支付、支付宝支付、微信支付三大支付
- Android 支付宝支付、微信支付、银联支付 整合第三方支付接入方法
- 微信支付,支付宝支付,银联支付——三大支付总结
- 银联支付 - 手机控件支付和WAP网页支付
- 微信支付、银联支付、支付宝
- 银联支付接口+支付宝接口统一支付功能
- 第三方支付三部曲;银联支付
- Android app支付-银联支付
- 银联支付(WAP支付)
- 银联支付安卓版服务评测
- 银联支付安卓版服务评测
- 银联支付
- android银联支付
- Android-银联支付
- HDU 2585 Hotel
- 【Jason's_ACM_解题报告】Fabled Rooks
- Xampp使用
- 用实例给新手讲解RSA加密算法
- JavaScript总结
- 银联支付安卓版
- 关于对ac自动机的一点小小看法
- 信号量多线程同步
- html+css qq空间模仿
- C语言之贪吃蛇(curses库函数)
- [SDOI2010]猪国杀 解题报告
- SharePoint 2013 支持 Windows Phone 设备(configurationsupport Windows Phone devices)
- Android应用程序基础及原理概要
- Java环境变量说开去