Android移动开发-集成调用支付宝支付开发的实现
如今移动支付比较火,尤其是在中国的市场。移动支付也称为手机支付,就是允许用户使用其移动终端(通常是手机)对所消费的商品或服务进行账务支付的一种服务方式。单位或个人通过移动设备、互联网或者近距离传感直接或间接向银行金融机构发送支付指令产生货币支付与资金转移行为,从而实现移动支付功能。移动支付将终端设备、互联网、应用提供商以及金融机构相融合,为用户提供货币支付、缴费等金融业务。
谈到移动支付,不得不说阿里旗下的蚂蚁金融的支付以及腾讯旗下的微信支付。那么现在在就谈谈如何Android项目里集成调用支付宝支付开发的实现方式。首先访问支付宝的官方平台蚂蚁金服开放平台,网址为:https://open.alipay.com/platform/home.htm 。然后用自己的支付宝登录并认证为开发者,接着在平台首页依次点击“文档中心”进入查阅相关的Android集成的开发文档,接着下载集成支付宝的SDK和demo,下载地址为: https://gw.alipayobjects.com/os/rmsportal/vPZJQoVJUyvrEUdlwYxl.zip 。
- Android项目集成调用支付宝的流程
流程说明(以Android平台为例):
第4步:调用支付接口:此消息就是本接口所描述的开发包提供的支付对象PayTask,将商户签名后的订单信息传进pay方法唤起支付宝收银台。
第5步:支付请求:手机支付宝支付开发包将会按照商户客户端提供的请求参数发送支付请求。
第8步:接口返回支付结果:商户客户端在第4步中调用的支付接口,会返回最终的支付结果(即同步通知)。
第12步:异步发送支付通知:手机支付宝支付服务器端发送异步通知消息给商户服务器端(备注:第12步一定发生在第6步之后,但不一定晚于7~11步)。
1.构造订单数据并签名
商户服务器端根据手机支付宝支付开发包的接口规则,通过程序生成得到签名结果及要传输给手机支付宝支付开发包的数据集合。签名相关的公私钥生成及配置规则。
2.发送请求数据
把构造完成的数据集合传递给手机支付宝支付开发包。
3.手机支付宝支付开发包对请求数据进行处理
手机支付宝支付开发包将请求数据根据业务规则包装后传递给手机支付宝支付服务器端,服务器端得到这些集合后,会先进行安全校验等验证,一系列验证通过后便会处理完成这次发送过来的数据请求。
4.返回处理的结果数据
(1)对于处理完成的交易,支付宝会以两种方式把数据分别反馈给商户客户端和商户服务器端。在手机客户端上,手机支付宝支付开发包直接把处理的数据结果反馈给商户客户端;
(2)在服务器端上,手机支付宝支付服务器端主动发起通知,调用商户在请求时设定好的页面路径。
5.商户对获取的返回结果数据进行处理
商户在客户端同步通知接收模块或服务器端异步通知接收模块获取到支付宝返回的结果数据后,可以结合商户自身业务逻辑进行数据处理(如:订单更新、自动充值到会员账号中等)。同步通知结果仅用于结果展示,入库数据需以异步通知为准。
- 导入开发资源
将下载的alipaySDK-XXX.jar包放入商户应用工程的libs目录下,如下图。
- 修改Manifest
在商户应用工程的AndroidManifest.xml文件里面添加声明:
<activity
android:name="com.alipay.sdk.app.H5PayActivity"
android:configChanges="orientation|keyboardHidden|navigation"
android:exported="false"
android:screenOrientation="behind" >
</activity>
<activity
android:name="com.alipay.sdk.auth.AuthActivity"
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" />
- AlipayTask.java逻辑代码如下:
package com.fukaimei.alipay.task;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Random;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.text.TextUtils;
import android.widget.Toast;
import com.alipay.sdk.app.PayTask;
import com.fukaimei.alipay.bean.AlipayConstants;
import com.fukaimei.alipay.bean.PayResult;
import com.fukaimei.alipay.util.SignUtils;
public class AlipayTask extends AsyncTask<String, Void, String> {
private static final String TAG = "AlipayTask";
private Context mContext;
private ProgressDialog dialog;
private int mType;
public AlipayTask(Context context, int type) {
mContext = context;
mType = type;
}
@Override
protected void onPreExecute() {
if (TextUtils.isEmpty(AlipayConstants.PARTNER)
|| TextUtils.isEmpty(AlipayConstants.RSA_PRIVATE)
|| TextUtils.isEmpty(AlipayConstants.SELLER)) {
new AlertDialog.Builder(mContext).setTitle("警告").setMessage("需要配置PARTNER | RSA_PRIVATE| SELLER")
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialoginterface, int i) {
}
}).show();
cancel(true);
} else {
dialog = ProgressDialog.show(mContext, "提示", "正在启动支付宝...");
}
}
@Override
protected String doInBackground(String... params) {
String orderInfo = getOrderInfo(params[0], params[1], params[2]);
// 特别注意,这里的签名逻辑需要放在服务端,切勿将私钥泄露在代码中!
String sign = sign(orderInfo);
try {
// 仅需对sign 做URL编码
sign = URLEncoder.encode(sign, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
//完整的符合支付宝参数规范的订单信息
final String payInfo = orderInfo + "&sign=\"" + sign + "\"&" + getSignType();
// 构造PayTask 对象
PayTask alipay = new PayTask((Activity) mContext);
// 调用支付接口,获取支付结果
String result = alipay.pay(payInfo, false);
return result;
}
@Override
protected void onPostExecute(String result) {
if (dialog != null) {
dialog.dismiss();
}
PayResult payResult = new PayResult(result);
// 支付宝返回此次支付结果及加签,建议对支付宝签名信息拿签约时支付宝提供的公钥做验签
String resultInfo = payResult.getResult();
Toast.makeText(mContext, "resultInfo=" + resultInfo, Toast.LENGTH_SHORT).show();
String resultStatus = payResult.getResultStatus();
// 判断resultStatus 为“9000”则代表支付成功,具体状态码代表含义可参考接口文档
if (TextUtils.equals(resultStatus, "9000")) {
Toast.makeText(mContext, "支付宝支付成功", Toast.LENGTH_SHORT).show();
} else {
// 判断resultStatus 为非“9000”则代表可能支付失败
// “8000”代表支付结果因为支付渠道原因或者系统原因还在等待支付结果确认,最终交易是否成功以服务端异步通知为准(小概率状态)
if (TextUtils.equals(resultStatus, "8000")) {
Toast.makeText(mContext, "支付宝支付结果确认中", Toast.LENGTH_SHORT).show();
} else {
// 其他值就可以判断为支付失败,包括用户主动取消支付,或者系统返回的错误
Toast.makeText(mContext, "支付宝支付失败" + payResult.getResult(), Toast.LENGTH_SHORT).show();
}
}
// if (mType == 1) {
// Intent intent = new Intent(mContext, TaxResultActivity.class);
// mContext.startActivity(intent);
// }
}
private String getOrderInfo(String subject, String body, String price) {
// 签约合作者身份ID
String orderInfo = "partner=" + "\"" + AlipayConstants.PARTNER + "\"";
// 签约卖家支付宝账号
orderInfo += "&seller_id=" + "\"" + AlipayConstants.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;
}
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;
}
private String sign(String content) {
return SignUtils.sign(content, AlipayConstants.RSA_PRIVATE);
}
private String getSignType() {
return "sign_type=\"RSA\"";
}
}
- PayResult.java逻辑代码如下:
package com.fukaimei.alipay.bean;
import android.text.TextUtils;
public class PayResult {
private String resultStatus;
private String result;
private String memo;
public PayResult(String rawResult) {
if (TextUtils.isEmpty(rawResult)) {
return;
}
String[] resultParams = rawResult.split(";");
for (String resultParam : resultParams) {
if (resultParam.startsWith("resultStatus")) {
resultStatus = gatValue(resultParam, "resultStatus");
}
if (resultParam.startsWith("result")) {
result = gatValue(resultParam, "result");
}
if (resultParam.startsWith("memo")) {
memo = gatValue(resultParam, "memo");
}
}
}
@Override
public String toString() {
return "resultStatus={" + resultStatus + "};memo={" + memo
+ "};result={" + result + "}";
}
private String gatValue(String content, String key) {
String prefix = key + "={";
return content.substring(content.indexOf(prefix) + prefix.length(),
content.lastIndexOf("}"));
}
/**
* @return the resultStatus
*/
public String getResultStatus() {
return resultStatus;
}
/**
* @return the memo
*/
public String getMemo() {
return memo;
}
/**
* @return the result
*/
public String getResult() {
return result;
}
}
- 在AlipayConstants里配置相关与支付宝签约的商家密钥和账号等,AlipayConstants.java逻辑代码如下:
package com.fukaimei.alipay.bean;
public class AlipayConstants {
// 商户PID
public static final String PARTNER = "这里配置商户PID";
// 商户收款账号
public static final String SELLER = "这里配置商户收款账号";
// 商户私钥,pkcs8格式
public static final String RSA_PRIVATE = "这里配置商户私钥";
// 支付宝公钥
public static final String RSA_PUBLIC = "这里配置支付宝公钥";
public static final int SDK_PAY_FLAG = 1;
public static final int SDK_CHECK_FLAG = 2;
}
- layout/activity_main.xml界面布局代码如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="5dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="150dp"
android:scaleType="fitCenter"
android:src="@drawable/dabaitu" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="商品名称:"
android:textColor="@color/black"
android:textSize="17sp" />
<EditText
android:id="@+id/et_goods_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/editext_selector"
android:padding="5dp"
android:text="小米MIX 2 "
android:textColor="@color/black"
android:textSize="17sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="商品描述:"
android:textColor="@color/black"
android:textSize="17sp" />
<EditText
android:id="@+id/et_goods_desc"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/editext_selector"
android:padding="5dp"
android:text="全面屏2.0,骁龙835处理器,全陶瓷机身。"
android:textColor="@color/black"
android:textSize="17sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="商品价格:"
android:textColor="@color/black"
android:textSize="17sp" />
<EditText
android:id="@+id/et_goods_price"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/editext_selector"
android:enabled="false"
android:inputType="numberDecimal"
android:padding="5dp"
android:text="0.01"
android:textColor="@color/black"
android:textSize="17sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="元"
android:textColor="@color/black"
android:textSize="17sp" />
</LinearLayout>
<Button
android:id="@+id/btn_alipay"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="支付宝支付"
android:textColor="@color/black"
android:textSize="17sp" />
</LinearLayout>
- MainActivity.java逻辑代码如下:
package com.fukaimei.alipay;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;
import com.fukaimei.alipay.task.AlipayTask;
public class MainActivity extends AppCompatActivity implements OnClickListener {
private static final String TAG = "MainActivity";
private EditText et_goods_title;
private EditText et_goods_desc;
private EditText et_goods_price;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_goods_title = (EditText) findViewById(R.id.et_goods_title);
et_goods_desc = (EditText) findViewById(R.id.et_goods_desc);
et_goods_price = (EditText) findViewById(R.id.et_goods_price);
findViewById(R.id.btn_alipay).setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.btn_alipay) {
String title = et_goods_title.getText().toString();
String desc = et_goods_desc.getText().toString();
String price = et_goods_price.getText().toString();
new AlipayTask(this, 0).execute(title, desc, price);
}
}
}
- Demo程序运行效果界面截图如下: