首先是OrderInfoUtil2_0这个基础类。后面调用的不少方法都抽出来了集成在这个类里面,就当这个是一个工具类吧。web
public class OrderInfoUtil2_0 { express
/** * 构造受权参数列表 * * @param pid * @param app_id * @param target_id * @return */ public static Map<String, String> buildAuthInfoMap(String pid, String app_id, String target_id) { Map<String, String> keyValues = new HashMap<String, String>(); // 商户签约拿到的app_id,如:2013081700024223 keyValues.put("app_id", app_id); // 商户签约拿到的pid,如:2088102123816631 keyValues.put("pid", pid); // 服务接口名称, 固定值 keyValues.put("apiname", "com.alipay.account.auth"); // 商户类型标识, 固定值 keyValues.put("app_name", "mc"); // 业务类型, 固定值 keyValues.put("biz_type", "openservice"); // 产品码, 固定值 keyValues.put("product_id", "APP_FAST_LOGIN"); // 受权范围, 固定值 keyValues.put("scope", "kuaijie"); // 商户惟一标识,如:kkkkk091125 keyValues.put("target_id", target_id); // 受权类型, 固定值 keyValues.put("auth_type", "AUTHACCOUNT"); // 签名类型 keyValues.put("sign_type", "RSA"); return keyValues; } /** * 构造支付订单参数列表 * * @return */ public static Map<String, String> buildOrderParamMap(String app_id, String total_amount, String product_info, String time,String out_trade_no) { Map<String, String> keyValues = new HashMap<String, String>(); keyValues.put("app_id", app_id); keyValues.put("biz_content", "{\"timeout_express\":\"30m\",\"product_code\":\"QUICK_MSECURITY_PAY\",\"total_amount\":\"" + total_amount + "\",\"subject\":\"" + product_info + "\",\"body\":\"兑换\",\"out_trade_no\":\"" +out_trade_no + "\"}"); keyValues.put("charset", "utf-8"); keyValues.put("method", "alipay.trade.app.pay"); keyValues.put("sign_type", "RSA"); keyValues.put("timestamp", time);// "2016-07-29 16:55:53" keyValues.put("version", "1.0"); keyValues.put("notify_url", "成功回调地址"); return keyValues; } /** * 构造支付订单参数信息 * * @param map 支付订单参数 * @return */ public static String buildOrderParam(Map<String, String> map) { List<String> keys = new ArrayList<String>(map.keySet()); StringBuilder sb = new StringBuilder(); for (int i = 0; i < keys.size() - 1; i++) { String key = keys.get(i); String value = map.get(key); sb.append(buildKeyValue(key, value, true)); sb.append("&"); } String tailKey = keys.get(keys.size() - 1); String tailValue = map.get(tailKey); sb.append(buildKeyValue(tailKey, tailValue, true)); return sb.toString(); } /** * 拼接键值对 * * @param key * @param value * @param isEncode * @return */ private static String buildKeyValue(String key, String value, boolean isEncode) { StringBuilder sb = new StringBuilder(); sb.append(key); sb.append("="); if (isEncode) { try { sb.append(URLEncoder.encode(value, "UTF-8")); } catch (UnsupportedEncodingException e) { sb.append(value); } } else { sb.append(value); } return sb.toString(); } /** * 对支付参数信息进行签名 * * @param map 待签名受权信息 * @return */ public static String getSign(Map<String, String> map, String rsaKey) { List<String> keys = new ArrayList<String>(map.keySet()); // key排序 Collections.sort(keys); StringBuilder authInfo = new StringBuilder(); for (int i = 0; i < keys.size() - 1; i++) { String key = keys.get(i); String value = map.get(key); authInfo.append(buildKeyValue(key, value, false)); authInfo.append("&"); } String tailKey = keys.get(keys.size() - 1); String tailValue = map.get(tailKey); authInfo.append(buildKeyValue(tailKey, tailValue, false)); String oriSign = SignUtils.sign(authInfo.toString(), rsaKey); String encodedSign = ""; try { encodedSign = URLEncoder.encode(oriSign, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return "sign=" + encodedSign; } /** * 要求外部订单号必须惟一。 * * @return */ private static String getOutTradeNo() { SimpleDateFormat format = new SimpleDateFormat("MMddHHmmss", Locale.getDefault()); Date date = new Date(); String key = format.format(date); Random r = new Random(); key = key + r.nextInt(); key = key.substring(0, 15); return key; } }
/** * 重要说明: * <p/> * 这里只是为了方便直接向商户展现支付宝的整个支付流程;因此Demo中加签过程直接放在客户端完成; * 真实App里,privateKey等数据严禁放在客户端,加签过程务必要放在服务端完成; * 防止商户私密数据泄露,形成没必要要的资金损失,及面临各类安全风险; */ public class PayDemoActivity extends FragmentActivity { /** * 支付宝支付业务:入参app_id */ public static final String APPID = "入参app_id"; /** * 支付宝帐户登陆受权业务:入参pid值 */ public static final String PID = "入参pid值"; /** * 支付宝帐户登陆受权业务:入参target_id值 */ public static final String TARGET_ID = "入参target_id值"; /** * 商户私钥,pkcs8格式 */ public static final String RSA_PRIVATE = "商户私钥"; private static final int SDK_PAY_FLAG = 1; private static final int SDK_AUTH_FLAG = 2; @SuppressLint("HandlerLeak") private Handler mHandler = new Handler() { @SuppressWarnings("unused") public void handleMessage(Message msg) { switch (msg.what) { case SDK_PAY_FLAG: { @SuppressWarnings("unchecked") PayResult payResult = new PayResult((Map<String, String>) msg.obj); /** 对于支付结果,请商户依赖服务端的异步通知结果。同步通知结果,仅做为支付结束的通知。 */ String resultInfo = payResult.getResult();// 同步返回须要验证的信息 String resultStatus = payResult.getResultStatus(); // 判断resultStatus 为9000则表明支付成功 if (TextUtils.equals(resultStatus, "9000")) { // 该笔订单是否真实支付成功,须要依赖服务端的异步通知。 MyToast.makeText(PayDemoActivity.this, "支付成功", Toast.LENGTH_SHORT).show(); } else { // 该笔订单真实的支付结果,须要依赖服务端的异步通知。 MyToast.makeText(PayDemoActivity.this, "支付失败", Toast.LENGTH_SHORT).show(); } break; } case SDK_AUTH_FLAG: { @SuppressWarnings("unchecked") AuthResult authResult = new AuthResult((Map<String, String>) msg.obj, true); String resultStatus = authResult.getResultStatus(); // 判断resultStatus 为“9000”且result_code // 为“200”则表明受权成功,具体状态码表明含义可参考受权接口文档 if (TextUtils.equals(resultStatus, "9000") && TextUtils.equals(authResult.getResultCode(), "200")) { // 获取alipay_open_id,调支付时做为参数extern_token 的value // 传入,则支付帐户为该受权帐户 MyToast.makeText(PayDemoActivity.this, "受权成功\n" + String.format("authCode:%s", authResult.getAuthCode()), Toast.LENGTH_SHORT) .show(); } else { // 其余状态值则为受权失败 MyToast.makeText(PayDemoActivity.this, "受权失败" + String.format("authCode:%s", authResult.getAuthCode()), Toast.LENGTH_SHORT).show(); } break; } default: break; } } ; }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.pay_main); SimpleDateFormat dfs = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String date_e = dfs.format(new Date()); payV2("0.02", "团币兑换", date_e); } /** * 支付宝支付业务 */ public void payV2(String price, String product_info, String time) { if (TextUtils.isEmpty(APPID) || TextUtils.isEmpty(RSA_PRIVATE)) { new AlertDialog.Builder(this).setTitle("警告").setMessage("须要配置APPID | RSA_PRIVATE") .setPositiveButton("肯定", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialoginterface, int i) { finish(); } }).show(); return; } /** * 这里只是为了方便直接向商户展现支付宝的整个支付流程;因此Demo中加签过程直接放在客户端完成; * 真实App里,privateKey等数据严禁放在客户端,加签过程务必要放在服务端完成; * 防止商户私密数据泄露,形成没必要要的资金损失,及面临各类安全风险; * * orderInfo的获取必须来自服务端; */ // Map<String, String> params = OrderInfoUtil2_0.buildOrderParamMap(APPID, price, product_info, time); Map<String, String> params=new HashMap<String, String>(); String orderParam = OrderInfoUtil2_0.buildOrderParam(params); String sign = OrderInfoUtil2_0.getSign(params, RSA_PRIVATE); final String orderInfo = orderParam + "&" + sign; Runnable payRunnable = new Runnable() { @Override public void run() { PayTask alipay = new PayTask(PayDemoActivity.this); Map<String, String> result = alipay.payV2(orderInfo, true); Log.e("msp", result.toString()); Message msg = new Message(); msg.what = SDK_PAY_FLAG; msg.obj = result; mHandler.sendMessage(msg); } }; Thread payThread = new Thread(payRunnable); payThread.start(); } /** * 支付宝帐户受权业务 * * @param v */ public void authV2(View v) { if (TextUtils.isEmpty(PID) || TextUtils.isEmpty(APPID) || TextUtils.isEmpty(RSA_PRIVATE) || TextUtils.isEmpty(TARGET_ID)) { new AlertDialog.Builder(this).setTitle("警告").setMessage("须要配置PARTNER |APP_ID| RSA_PRIVATE| TARGET_ID") .setPositiveButton("肯定", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialoginterface, int i) { } }).show(); return; } /** * 这里只是为了方便直接向商户展现支付宝的整个支付流程;因此Demo中加签过程直接放在客户端完成; * 真实App里,privateKey等数据严禁放在客户端,加签过程务必要放在服务端完成; * 防止商户私密数据泄露,形成没必要要的资金损失,及面临各类安全风险; * * authInfo的获取必须来自服务端; */ Map<String, String> authInfoMap = OrderInfoUtil2_0.buildAuthInfoMap(PID, APPID, TARGET_ID); String info = OrderInfoUtil2_0.buildOrderParam(authInfoMap); String sign = OrderInfoUtil2_0.getSign(authInfoMap, RSA_PRIVATE); final String authInfo = info + "&" + sign; Runnable authRunnable = new Runnable() { @Override public void run() { // 构造AuthTask 对象 AuthTask authTask = new AuthTask(PayDemoActivity.this); // 调用受权接口,获取受权结果 Map<String, String> result = authTask.authV2(authInfo, true); Message msg = new Message(); msg.what = SDK_AUTH_FLAG; msg.obj = result; mHandler.sendMessage(msg); } }; // 必须异步调用 Thread authThread = new Thread(authRunnable); authThread.start(); } /** * get the sdk version. 获取SDK版本号 */ public void getSDKVersion() { PayTask payTask = new PayTask(this); String version = payTask.getVersion(); MyToast.makeText(this, version, Toast.LENGTH_SHORT).show(); } /** * 原生的H5(手机网页版支付切natvie支付) 【对应页面网页支付按钮】 * * @param v */ public void h5Pay(View v) { Intent intent = new Intent(this, H5PayDemoActivity.class); Bundle extras = new Bundle(); /** * url是测试的网站,在app内部打开页面是基于webview打开的,demo中的webview是H5PayDemoActivity, * demo中拦截url进行支付的逻辑是在H5PayDemoActivity中shouldOverrideUrlLoading方法实现, * 商户能够根据本身的需求来实现 */ String url = "http://m.taobao.com"; // url能够是一号店或者淘宝等第三方的购物wap站点,在该网站的支付过程当中,支付宝sdk完成拦截支付 extras.putString("url", url); intent.putExtras(extras); startActivity(intent); } }
剩下就是支付宝支付的业务逻辑了。下面的out_trade_no是本身请求服务器返回过来的订单号,time就是当前时间,product_info就是显示在支付页面上的字段,本身设定,试试就知道。api
/** * 支付宝支付业务 */ public void payV2(String price, String product_info, String time, String out_trade_no) { if (TextUtils.isEmpty(APPID) || TextUtils.isEmpty(RSA_PRIVATE)) { new AlertDialog.Builder(this).setTitle("警告").setMessage("须要配置APPID | RSA_PRIVATE") .setPositiveButton("肯定", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialoginterface, int i) { finish(); } }).show(); return; } /** * 这里只是为了方便直接向商户展现支付宝的整个支付流程;因此Demo中加签过程直接放在客户端完成; * 真实App里,privateKey等数据严禁放在客户端,加签过程务必要放在服务端完成; * 防止商户私密数据泄露,形成没必要要的资金损失,及面临各类安全风险; * * orderInfo的获取必须来自服务端; */ Map<String, String> params = OrderInfoUtil2_0.buildOrderParamMap(APPID, price, product_info, time, out_trade_no); String orderParam = OrderInfoUtil2_0.buildOrderParam(params); String sign = OrderInfoUtil2_0.getSign(params, RSA_PRIVATE); final String orderInfo = orderParam + "&" + sign; Runnable payRunnable = new Runnable() { @Override public void run() { PayTask alipay = new PayTask(RechargeMoneyActivity.this); Map<String, String> result = alipay.payV2(orderInfo, true); Message msg = new Message(); msg.what = SDK_PAY_FLAG; msg.obj = result; mHandler.sendMessage(msg); } }; Thread payThread = new Thread(payRunnable); payThread.start(); }
handler处理
安全
private Handler mHandler = new Handler() {服务器
@SuppressWarnings("unused") public void handleMessage(Message msg) { switch (msg.what) { case SDK_PAY_FLAG: { @SuppressWarnings("unchecked") PayResult payResult = new PayResult((Map<String, String>) msg.obj); /** 对于支付结果,请商户依赖服务端的异步通知结果。同步通知结果,仅做为支付结束的通知。 */ String resultInfo = payResult.getResult();// 同步返回须要验证的信息 String memo = payResult.getMemo(); String resultStatus = payResult.getResultStatus(); // 判断resultStatus 为9000则表明支付成功 if (TextUtils.equals(resultStatus, "9000")) { // 该笔订单是否真实支付成功,须要依赖服务端的异步通知。 // showHintDialog("支付成功"); } else { pay_state = "0"; } String sign = TGmd5.getMD5(logid + pay_state + memo); tuanbiExchangePresenter.doAlipay(logid, pay_state, memo, sign); break; } case SDK_AUTH_FLAG: { @SuppressWarnings("unchecked") AuthResult authResult = new AuthResult((Map<String, String>) msg.obj, true); String resultStatus = authResult.getResultStatus(); // 判断resultStatus 为“9000”且result_code // 为“200”则表明受权成功,具体状态码表明含义可参考受权接口文档 if (TextUtils.equals(resultStatus, "9000") && TextUtils.equals(authResult.getResultCode(), "200")) { // 获取alipay_open_id,调支付时做为参数extern_token 的value // 传入,则支付帐户为该受权帐户 MyToast.makeText(RechargeMoneyActivity.this, "受权成功\n" + String.format("authCode:%s", authResult.getAuthCode()), Toast.LENGTH_SHORT) .show(); } else { // 其余状态值则为受权失败 MyToast.makeText(RechargeMoneyActivity.this, "受权失败" + String.format("authCode:%s", authResult.getAuthCode()), Toast.LENGTH_SHORT).show(); } break; } default: break; } } ; };
这样一个支付宝支付就集成了,是否是很简单。复制代码就能写出,注意里面代码注解。填写各类id。app
源码免费下载:http://www.jinhusns.com/Products/Download/?type=yhqdom