集成支付宝支付
ONE Goal ,ONE Passion!
第一步:正式准备阶段.
服务端.
负责生成订单及签名,及接受支付异步通知。
客户端
负责使用服务端传来的订单信息调用支付宝支付接口,及根据SDK同步返回的支付结果展示结果页。
注意: 私钥必须放在服务端,签名过程必须放在服务端。
第二步,开始接入的准备工作.
解压接口压缩文件(文件名是WS_MOBILE_PAY_SDK_BASE.zip),找到安卓的压缩文件(文件名是支付宝钱包支付开发包标准版(Android).zip)。标准开发包以jar包方式提供给商户应用工程集成,打开alipay-sdk-common文件夹获取alipaySDK-20150602.jar,后8位数字标识发布日期,商户可根据日期时间判断SDK版本的新旧。
1, 将alipaySDK-20150602.jar包放入商户应用工程的libs目录下,如下图。
2,进入商户应用工程的Java Build Path,将libs目录下的alipaySDK-20150602.jar导入,如下图。
3,选中Order and Export,勾选alipaySDK-20150602.jar,如下图。
修改Manifest
在商户应用工程的AndroidManifest.xml文件里面添加声明:
<activity
android:name="你的支付界面(即PayDemoActivity )"
android:configChanges="orientation|keyboardHidden|navigation"
android:exported="false"
android:screenOrientation="behind" >
</activity>
权限声明:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
第3步 :支付接口调用
需要在新线程中调用支付接口。(可参考alipay_demo实现)
获取PayTask支付对象调用支付(支付或者授权的行为需要在独立的非ui线程中执行)
alipay.pay(payInfo, true)方法中payInfo一些参数含义:
参数 | 意义 |
---|---|
orderInfo | 订单信息—getOrderInfo(“测试的商品”, “该测试商品的详细描述”, “0.01”)返回的数据 |
sign | 后台给我们的签名(后台就是拿着订单信息进行签名) |
getSignType() | “sign_type=\”RSA\”” (固定值) |
我们的支付activity
public class PayDemoActivity extends FragmentActivity {
// 商户PID
public static final String PARTNER = "这些字段我们不写在代码中,而是由服务器处理";
// 商户收款账号
public static final String SELLER = "这些字段我们不写在代码中,而是由服务器处理";
// 商户私钥,pkcs8格式
public static final String RSA_PRIVATE = "这些字段我们不写在代码中,而是由服务器处理";
// 支付宝公钥
public static final String RSA_PUBLIC = "这些字段我们不写在代码中,而是由服务器处理";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout."你的布局");
}
/**
* 点击--调用SDK支付
* sign是从后台获得的,因为签名过程涉及一些秘钥之类的,最好不要暴
* 露在代码中,因此我们让后台对数据处理生成签名后直接给我们.
*/
public void pay(View v) {
try {
/**
* 我们仅需对sign 做URL编码
*/
sign = URLEncoder.encode(sign, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
//完整的符合支付宝参数规范的订单信息
final String payInfo = orderInfo + "&sign=\"" + sign + "\"&" + getSignType();
Runnable payRunnable = new Runnable() {
@Override
public void run() {
// 构造PayTask 对象
PayTask alipay = new PayTask(PayDemoActivity.this);
// 调用支付接口,获取支付结果
String result = alipay.pay(payInfo, true);
// result中已经有支付结果了,不过为了安全性.我们一般去查询自己公司服务器接口,从而得到支付结果.然后再做处理
}
};
// 必须异步调用
Thread payThread = new Thread(payRunnable);
payThread.start();
}
/**
* get the sign type we use. 获取签名方式
*
*/
private String getSignType() {
return "sign_type=\"RSA\"";
}
}
对订单信息进行签名——-后台操作
/**
* sign the order info. 对订单信息进行签名---后台操作
*
* @param content
* 待签名订单信息
*/
private String sign(String content) {
return SignUtils.sign(content, RSA_PRIVATE);
}
/**
* 签名工具类---后台用,这里只是让我们看看签名算法
* @author fy
*
*/
public class SignUtils {
private static final String ALGORITHM = "RSA";
private static final String SIGN_ALGORITHMS = "SHA1WithRSA";
private static final String DEFAULT_CHARSET = "UTF-8";
public static String sign(String content, String privateKey) {
try {
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(
Base64.decode(privateKey));
KeyFactory keyf = KeyFactory.getInstance(ALGORITHM);
PrivateKey priKey = keyf.generatePrivate(priPKCS8);
java.security.Signature signature = java.security.Signature
.getInstance(SIGN_ALGORITHMS);
signature.initSign(priKey);
signature.update(content.getBytes(DEFAULT_CHARSET));
byte[] signed = signature.sign();
return Base64.encode(signed);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
生成商户订单号,可以android端也可以服务器端确定.尽量让服务器端确定
/**
* get the out_trade_no for an order. 生成商户订单号参考方
* 法,该值
* 在商户端应保持唯一(可自定义格式规范)
*/
private 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;//订单号
}
创建订单信息
1,商品名称
2,商品详情
3,商品金额
把这些数据传递给后台,后台生产订单信息给你.
/**
* create the order info. 创建订单信息
* 由服务器生成
*/
private String getOrderInfo(String subject, String body, String price) {
// 签约合作者身份ID
String orderInfo = "partner=" + "\"" + PARTNER + "\"";
// 签约卖家支付宝账号
orderInfo += "&seller_id=" + "\"" + SELLER + "\"";
// 商户网站唯一订单号
orderInfo += "&out_trade_no=" + "\"" + getOutTradeNo() + "\"";
// 商品名称
orderInfo += "&subject=" + "\"" + subject + "\"";
// 商品详情
orderInfo += "&body=" + "\"" + body + "\"";
// 商品金额
orderInfo += "&total_fee=" + "\"" + price + "\"";
// 服务器异步通知页面路径
orderInfo += "¬ify_url=" + "\"" + "http://notify.msp.hk/notify.htm" + "\"";
// 服务接口名称, 固定值
orderInfo += "&service=\"mobile.securitypay.pay\"";
// 支付类型, 固定值
orderInfo += "&payment_type=\"1\"";
// 参数编码, 固定值
orderInfo += "&_input_charset=\"utf-8\"";
// 设置未付款交易的超时时间
// 默认30分钟,一旦超时,该笔交易就会自动被关闭。
// 取值范围:1m~15d。
// m-分钟,h-小时,d-天,1c-当天(无论交易何时创建,都在0点关闭)。
// 该参数数值不接受小数点,如1.5h,可转换为90m。
orderInfo += "&it_b_pay=\"30m\"";
// extern_token为经过快登授权获取到的alipay_open_id,带上此参数用户将使用授权的账户进行支付
// orderInfo += "&extern_token=" + "\"" + extern_token + "\"";
// 支付宝处理完请求后,当前页面跳转到商户指定页面的路径,可空
orderInfo += "&return_url=\"m.alipay.com\"";
// 调用银行卡支付,需配置此参数,参与签名, 固定值 (需要签约《无线银行卡快捷支付》才能使用)
// orderInfo += "&paymethod=\"expressGateway\"";
return orderInfo;
}
app支付已经完成喽!
如果你们公司已经做出了网页版的支付,而且想直接使用网页版支付那就直接调用webview.
/**
* 原生的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.meituan.com";
// url可以是一号店或者美团等第三方的购物wap站点,在该网站的支付过程中,支付宝sdk完成拦截支付
extras.putString("url", url);
intent.putExtras(extras);
startActivity(intent);
}
这是调用显示webview的页面.可以根据支付宝提供的H5PayDemoActivity .如果需要的话自己改一下就好了
@SuppressLint({ "SetJavaScriptEnabled", "InlinedApi" })
public class H5PayDemoActivity extends Activity {
private WebView mWebView;
@SuppressWarnings("deprecation")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle extras = null;
try {
extras = getIntent().getExtras();
} catch (Exception e) {
finish();
return;
}
if (extras == null) {
finish();
return;
}
String url = null;
try {
url = extras.getString("url");
} catch (Exception e) {
finish();
return;
}
if (TextUtils.isEmpty(url)) {
// 测试H5支付,必须设置要打开的url网站
new AlertDialog.Builder(H5PayDemoActivity.this).setTitle("警告")
.setMessage("必须配置需要打开的url 站点,请在PayDemoActivity类的h5Pay中配置")
.setPositiveButton("确定", new OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {
finish();
}
}).show();
}
super.requestWindowFeature(Window.FEATURE_NO_TITLE);
LinearLayout layout = new LinearLayout(getApplicationContext());
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
layout.setOrientation(LinearLayout.VERTICAL);
setContentView(layout, params);
mWebView = new WebView(getApplicationContext());
params.weight = 1;
mWebView.setVisibility(View.VISIBLE);
layout.addView(mWebView, params);
WebSettings settings = mWebView.getSettings();
settings.setUserAgentString(
"User-Agent:Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50");
settings.setRenderPriority(RenderPriority.HIGH);
settings.setSupportMultipleWindows(true);
settings.setJavaScriptEnabled(true);
settings.setSavePassword(false);
settings.setJavaScriptCanOpenWindowsAutomatically(true);
settings.setMinimumFontSize(settings.getMinimumFontSize() + 8);
settings.setAllowFileAccess(false);
settings.setTextSize(WebSettings.TextSize.NORMAL);
mWebView.setVerticalScrollbarOverlay(true);
mWebView.setWebViewClient(new MyWebViewClient());
mWebView.loadUrl(url);
}
@Override
public void onBackPressed() {
if (mWebView.canGoBack()) {
mWebView.goBack();
} else {
finish();
}
}
@Override
public void finish() {
super.finish();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
private class MyWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(final WebView view, String url) {
final PayTask task = new PayTask(H5PayDemoActivity.this);
final String ex = task.fetchOrderInfoFromH5PayUrl(url);
if (!TextUtils.isEmpty(ex)) {
new Thread(new Runnable() {
public void run() {
H5PayResultModel result = task.h5Pay(ex, true);
if (!TextUtils.isEmpty(result.getReturnUrl())) {
view.loadUrl(result.getReturnUrl());
}
}
}).start();
} else {
view.loadUrl(url);
}
return true;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mWebView != null) {
mWebView.removeAllViews();
try {
mWebView.destroy();
} catch (Throwable t) {
}
mWebView = null;
}
}
}
最后:支付结果获取和处理
String result = alipay.pay(payInfo, true);
// result中已经有支付结果了,不过为了安全性.我们一般去查询自己公司服务器接口(可以轮询),从而得到支付结果.然后再做处理.....
另附:正确的请求参数,以及返回数据
1,—–payInfo字符串格式,形式一般如下:
partner="2088101568358171"&seller_id="xxx@alipay.com"&out_trade_no="0819145412-6177"&subject="测试"&body="测试测试"&total_fee="0.01"¬ify_url="http://notify.msp.hk/notify.htm"&service="mobile.securitypay.pay"&payment_type="1"&_input_charset="utf-8"&it_b_pay="30m"&sign="lBBK%2F0w5LOajrMrji7DUgEqNjIhQbidR13GovA5r3TgIbNqv231yC1NksLdw%2Ba3JnfHXoXuet6XNNHtn7VE%2BeCoRO1O%2BR1KugLrQEZMtG5jmJIe2pbjm%2F3kb%2FuGkpG%2BwYQYI51%2BhA3YBbvZHVQBYveBqK%2Bh8mUyb7GM1HxWs9k4%3D"&sign_type="RSA"
2,—–返回值,字符串格式,形式一般如下
当然我们不必参考这个返回值.我们只关心自己后台返回值就行了.
resultStatus={9000};memo={};result={partner="2088101568358171"&seller_id="xxx@alipay.com"&out_trade_no="0819145412-6177"&subject="测试"&body="测试测试"&total_fee="0.01"¬ify_url="http://notify.msp.hk/notify.htm"&service="mobile.securitypay.pay"&payment_type="1"&_input_charset="utf-8"&it_b_pay="30m"&success="true"&sign_type="RSA"&sign="hkFZr+zE9499nuqDNLZEF7W75RFFPsly876QuRSeN8WMaUgcdR00IKy5ZyBJ4eldhoJ/2zghqrD4E2G2mNjs3aE+HCLiBXrPDNdLKCZgSOIqmv46TfPTEqopYfhs+o5fZzXxt34fwdrzN4mX6S13cr3UwmEV4L3Ffir/02RBVtU="}
说明:
返回结果需要通过resultStatus以及result字段的值来综合判断并确定支付结果。在resultStatus=9000,并且success=“true”以及sign=“xxx”校验通过的情况下,证明支付成功,其它情况归为失败。较低安全级别的场合,也可以只通过检查resultStatus以及success=“true”来判定支付结果。
Android平台和iOS平台的返回结果串唯一不同之处是resultStatus这个key,在iOS的返回结果串中原始的数据是ResultStatus(为了兼容历史版本首字母大写),Android平台是resultStatus