这个开发包包含了开发的详细指导,包括私钥公钥生成,接口文档,服务端开发例子,客户端开发例子等诸多说明。详细如图所示:
我们要使用支付宝的快捷支付功能,必须先去支付宝进行申请,与支付宝达成合约之后,它会为我们(商家)分配一个PID(partnerID)和安全检验码,其中PID有用到,安全检验码则未用到。详细如图所示:
我们先看看项目的布局,图工程1.png;
支付订单调用的Alipay.pay(String param)中的param就是上面的签名字符串。请求正确的话,界面就会跳转到支付界面,如图支付.png
/* * Copyright (C) 2010 The MobileSecurePay Project * All right reserved. * author: [email protected] * * 提示:如何获取安全校验码和合作身份者id * 1.用您的签约支付宝账号登录支付宝网站(www.alipay.com) * 2.点击“商家服务”(https://b.alipay.com/order/myorder.htm) * 3.点击“查询合作者身份(pid)”、“查询安全校验码(key)” */ package com.alipay.android.msp.demo; // // 请参考 Android平台安全支付服务(msp)应用开发接口(4.2 RSA算法签名)部分,并使用压缩包中的openssl RSA密钥生成工具,生成一套RSA公私钥。 // 这里签名时,只需要使用生成的RSA私钥。 // Note: 为安全起见,使用RSA私钥进行签名的操作过程,应该尽量放到商家服务器端去进行。 public final class Keys { // 合作身份者id,以2088开头的16位纯数字,==支付宝给申请移动支付的商家的一个ID,签约的支付宝账号对应的支付宝唯一用户号== public static final String DEFAULT_PARTNER = "2088812345675113"; // 收款支付宝账号==卖家的支付宝账号,可以是邮箱,电话号码或上面的2088开头的那个ID== public static final String DEFAULT_SELLER = "[email protected]"; //交易订单号 public static final String TRADE_NO = "032014012345676884"; //异步通知的地址 public static final String notify_url = "http://152.110.113:4275/manager/pay/alipay/notify"; // 商户私钥,自助生成,客户端自己通过下载下来的文件WS_SECURE_PAY_SDK\openssl\bin\spenssl。exe生成私钥和公钥 /** * RSA密钥使用逻辑: * 商户在使用RSA签名方式的支付宝接口时,真正会用到的密钥是商户私钥与支付 * 宝公钥。商户上传公钥给支付宝,支付宝把公钥给商户,是公钥互换的操作。这就 使得商户使用自己的私钥做签名时,支付宝端会根据商户上传的公钥做验证签名。 * 商户使用支付宝公钥做验证签名时,同理,也是因为支付宝用支付宝私钥做了签名。 */ public static final String PRIVATE = "签约支付宝商户有效的私钥字符串,可惜我没有0.0"; // 商户公钥,是用我们自己生成的公钥跟支付宝交换的公钥。 public static final String PUBLIC = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnxj/9qwVfgoUh/y2W89L6BkRAFljhNhgPdyPuBV64bfQNN1PjbCzkIM6qRdKBoLPXmKKMiFYnkd6rAoprih3/PrQEB/VsW8OoM8fxn67UDYuyBTqA23MML9q1+ilIZwBC2AQ2UBVOrFXfFl75p6/B5KsiNG9zpgmLCUYuLkxpLQIDAQAB"; }
package com.alipay.android.msp.demo; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import android.app.Activity; import android.content.res.XmlResourceParser; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import com.alipay.android.app.sdk.AliPay; public class ExternalPartner extends Activity implements OnItemClickListener, OnClickListener { public static final String TAG = "alipay-sdk"; private static final int RQF_PAY = 1; private static final int RQF_LOGIN = 2; private EditText mUserId; private Button mLogon; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.external_partner); initProducts(); initListView(); } /* * (non-Javadoc) * * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu) */ @Override public boolean onCreateOptionsMenu(Menu menu) { menu.add(Menu.NONE, Menu.FIRST, 1, "快速登录"); return true; } /* * (non-Javadoc) * * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem) */ @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case Menu.FIRST: setContentView(R.layout.trustlogin); mUserId = (EditText) findViewById(R.id.user_id); mLogon = (Button) findViewById(R.id.get_token); mLogon.setOnClickListener(this); break; } return false; } /** * 初始化产品列表 */ private void initProducts() { if (sProducts != null) return; XmlResourceParser parser = getResources().getXml(R.xml.products); ArrayList<Product> products = new ArrayList<Product>(); Product product = null; try { int eventType = parser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { if (eventType == XmlPullParser.START_TAG && parser.getName().equalsIgnoreCase("product")) { product = new Product(); product.subject = parser.getAttributeValue(0); product.body = parser.getAttributeValue(1); product.price = parser.getAttributeValue(2); products.add(product); } eventType = parser.next(); } sProducts = new Product[products.size()]; products.toArray(sProducts); } catch (XmlPullParserException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } @Override public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) { try { Log.i("ExternalPartner", "onItemClick"); String info = getNewOrderInfo(position); // String sign = Rsa.sign(info, Keys.PRIVATE);//获取sign字符串 String sign = ""; sign = URLEncoder.encode(sign);// 由于我无法申请到支付宝的快捷支付服务,所以无法获取有效的私钥公钥,只能根据项目中获取的参数适当的进行调整,到最后,调整来调整去发现,调整的参数必须与原数据保持高度的一致。可能是支付宝那端也是将订单信息中的sign字段进行解密,然后比对参数是否一致 sign = "DSt%2BKjh7gTCk%2BB04tr7eRNE6H4FXI617zTBTan8jO8kTB9oNaHeBMwN8jTRXhYsUhUH%2FtHu57L7mELm8agWwEMKkeFwS9GmTWDHWTkv2d9vgdEKTQoUwFF76UJxkJVQmRY9Fi%2FEIfv2CRwJR9kuNhnKb6eZYrfdCdAfvdzncQXI%3D"; info += "&sign=\"" + sign + "\"&sign_type=\"RSA\""; Log.i("ExternalPartner", "start pay"); // start the pay. Log.i(TAG, "info = " + info); //从公司获取的支付参数 // info = // "partner=\"2088812345675113\"&out_trade_no=\"032011234511576884\"&subject=\"阿迪达斯三叶草_2014-07-17\"&body=\"上衣\"&total_fee=\"0.01\"¬ify_url=\"http%3A%2F%2F110.123.152%3A4085%2Fmanager%2Fpay%2Falipay%2Fnotify\"&service=\"mobile.securitypay.pay\"&_input_charset=\"utf-8\"&payment_type=\"1\"&seller_id=\"[email protected]\"&it_b_pay=\"13m\"&sign=\"DSt%2BKjh7gTCk%2BB04tr7eRNE6H4FXI617zTBTan8jO8kTB9oNaHeBMwN8jTRXhYsUhUH%2FtHu57L7mELm8agWwEMKkeFwS9GmTWDHWTkv2d9vgdEKTQoUwFF76UJxkJVQmRY9Fi%2FEIfv2CRwJR9kuNhnKb6eZYrfdCdAfvdzncQXI%3D\"&sign_type=\"RSA\""; final String orderInfo = info; new Thread() { public void run() { AliPay alipay = new AliPay(ExternalPartner.this, mHandler); // 设置为沙箱模式,不设置默认为线上环境 // alipay.setSandBox(true); String result = alipay.pay(orderInfo); Log.i(TAG, "result = " + result); Message msg = new Message(); msg.what = RQF_PAY; msg.obj = result; mHandler.sendMessage(msg); } }.start(); } catch (Exception ex) { ex.printStackTrace(); Toast.makeText(ExternalPartner.this, R.string.remote_call_failed, Toast.LENGTH_SHORT).show(); } } /** * 支付接口待签名字符串,字符串都是以key="value"中间用&分隔形式组成 ==订单信息未加密前的字符串== */ private String getNewOrderInfo(int position) { StringBuilder sb = new StringBuilder(); sb.append("partner=\"");// 支付宝合作商户ID sb.append(Keys.DEFAULT_PARTNER); sb.append("\"&out_trade_no=\"");// 订单号 sb.append(getOutTradeNo()); sb.append("\"&subject=\"");// 订单主题 sb.append(sProducts[position].subject); sb.append("\"&body=\"");// 订单内容 sb.append(sProducts[position].body); sb.append("\"&total_fee=\"");// 订单价格 sb.append(sProducts[position].price.replace("一口价:", "")); sb.append("\"¬ify_url=\"");// 异步通知到哪儿(url指定) sb.append(URLEncoder.encode(Keys.notify_url));// URl需要进行编码 sb.append("\"&service=\"mobile.securitypay.pay");// 调用的SDK服务接口,是个固定值 sb.append("\"&_input_charset=\"utf-8");// 指定字符集,固定值UTF-8,支付宝只支持这,不知道GBK // sb.append("\"&return_url=\"");//不知道干嘛的 // sb.append(URLEncoder.encode("http://m.alipay.com")); sb.append("\"&payment_type=\"1");// 字符类型,1表示购买商品;或许还有其它的类型,我暂时不知道 sb.append("\"&seller_id=\"");// 卖家支付宝账号,需与申请快捷支付的支付宝账号一致 sb.append(Keys.DEFAULT_SELLER); // 如果show_url值为空,可不传 // sb.append("\"&show_url=\"");//商品展示地址 sb.append("\"&it_b_pay=\"13m");// 支付超时时间,超过设置时间自动取消 sb.append("\""); return new String(sb); } /** * tradeNo 由商户端产生的唯一订单号,并不是支付宝的交易单号 * @return */ private String getOutTradeNo() { // SimpleDateFormat format = new SimpleDateFormat("MMddHHmmss"); // Date date = new Date(); // String key = format.format(date); // // java.util.Random r = new java.util.Random(); // key += r.nextInt(); // key = key.substring(0, 15); // Log.d(TAG, "outTradeNo: " + key); // return key; return "032014071711576884"; } private void initListView() { ListView lv = (ListView) findViewById(R.id.list_view); lv.setAdapter(new ExternalPartnerAdapter()); lv.setOnItemClickListener(this); } private void doLogin() { final String orderInfo = getUserInfo(); new Thread() { public void run() { String result = new AliPay(ExternalPartner.this, mHandler) .pay(orderInfo); Log.i(TAG, "result = " + result); Message msg = new Message(); msg.what = RQF_LOGIN; msg.obj = result; mHandler.sendMessage(msg); } }.start(); } private String getUserInfo() { String userId = mUserId.getText().toString(); return trustLogin(Keys.DEFAULT_PARTNER, userId); } private String trustLogin(String partnerId, String appUserId) { StringBuilder sb = new StringBuilder(); sb.append("app_name=\"mc\"&biz_type=\"trust_login\"&partner=\""); sb.append(partnerId); Log.d("TAG", "UserID = " + appUserId); if (!TextUtils.isEmpty(appUserId)) { appUserId = appUserId.replace("\"", ""); sb.append("\"&app_id=\""); sb.append(appUserId); } sb.append("\""); String info = sb.toString(); // 请求信息签名 String sign = Rsa.sign(info, Keys.PRIVATE); try { sign = URLEncoder.encode(sign, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } info += "&sign=\"" + sign + "\"&sign_type=\"RSA\""; return info; } @Override public void onClick(View v) { if (v instanceof Button) { switch (v.getId()) { case R.id.get_token: doLogin(); break; } } } private class ExternalPartnerAdapter extends BaseAdapter { @Override public int getCount() { return sProducts.length; } @Override public Object getItem(int arg0) { return sProducts[arg0]; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { LayoutInflater factory = LayoutInflater .from(ExternalPartner.this); convertView = factory.inflate(R.layout.product_item, null); } Product product = (Product) getItem(position); TextView tv = (TextView) convertView.findViewById(R.id.subject); tv.setText(product.subject); tv = (TextView) convertView.findViewById(R.id.body); tv.setText(product.body); tv = (TextView) convertView.findViewById(R.id.price); tv.setText(product.price); return convertView; } } Handler mHandler = new Handler() { public void handleMessage(android.os.Message msg) { Result result = new Result((String) msg.obj); switch (msg.what) { case RQF_PAY: case RQF_LOGIN: { Toast.makeText(ExternalPartner.this, result.getResult(), Toast.LENGTH_SHORT).show(); } break; default: break; } }; }; public static class Product { public String subject; public String body; public String price; } public static Product[] sProducts; }