如今移动支付比较火,尤其是在中国的市场。移动支付也称为手机支付,就是允许用户使用其移动终端(通常是手机)对所消费的商品或服务进行账务支付的一种服务方式。单位或个人通过移动设备、互联网或者近距离传感直接或间接向银行金融机构发送支付指令产生货币支付与资金转移行为,从而实现移动支付功能。移动支付将终端设备、互联网、应用提供商以及金融机构相融合,为用户提供货币支付、缴费等金融业务。
谈到移动支付,不得不说阿里旗下的蚂蚁金融的支付以及腾讯旗下的微信支付。那么现在在就谈谈如何Android项目里集成调用微信支付开发的实现方式。首先访问微信开放平台官网,网址为:https://open.weixin.qq.com/ 。然后用自己的邮箱注册并认证为开发者,接着在平台首页依次点击“资源中心”——“Android资源下载”,即可在打开的页面中下载开发工具包jar包与Demo程序示例代码,下载地址为: https://res.wx.qq.com/open/zh_CN/htmledition/res/dev/download/sdk/WeChatSDK_Android221cbf.zip 。
注意这里的开发包libmmsdk.jar同时集成了微信分享与微信支付的SDK。
使用微信支付需要先申请测试应用,在微信开放平台登录成功后,依次单击链接“管理中心”——“创建移动应用”,填写应用相关信息,提交成功后返回应用列表页面。然后查看应用详情页,在接口信息栏目中发现默认已获得微信分享的权限,而微信支付权限需要另外申请开通。
商户系统和微信支付系统主要交互说明:
步骤1:用户在商户APP中选择商品,提交订单,选择微信支付。
步骤2:商户后台收到用户支付单,调用微信支付统一下单接口。
步骤3:统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。参与签名的字段名为appid,partnerid,prepayid,noncestr,timestamp,package。注意:package的值格式为Sign=WXPay
步骤4:商户APP调起微信支付。
步骤5:商户后台接收支付通知。
步骤6:商户后台查询支付结果。
将下载的jar包放入商户应用工程的libs目录下。
package com.fukaimei.wechatpay.task;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Random;
import org.json.JSONObject;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.message.BasicNameValuePair;
//import com.alibaba.fastjson.JSONObject;
import com.fukaimei.wechatpay.bean.GetPrepayIdResult;
import com.fukaimei.wechatpay.bean.LocalRetCode;
import com.fukaimei.wechatpay.bean.WechatConstants;
import com.fukaimei.wechatpay.util.MD5;
import com.fukaimei.wechatpay.util.WechatUtil;
import com.tencent.mm.sdk.modelpay.PayReq;
import com.tencent.mm.sdk.openapi.IWXAPI;
import com.tencent.mm.sdk.openapi.WXAPIFactory;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;
public class GetPrepayIdTask extends AsyncTask {
private static final String TAG = "GetPrepayIdTask";
private Context context;
private ProgressDialog dialog;
private String accessToken;
private String[] goods_info;
public GetPrepayIdTask(Context context, String accessToken) {
this.context = context;
this.accessToken = accessToken;
}
@Override
protected void onPreExecute() {
dialog = ProgressDialog.show(context, "提示", "正在获取预支付订单...");
}
@Override
protected GetPrepayIdResult doInBackground(String... params) {
goods_info = new String[] { params[0], params[1], params[2] };
String url = String.format("https://api.weixin.qq.com/pay/genprepay?access_token=%s", accessToken);
String entity = genProductArgs();
Log.d(TAG, "doInBackground, url = " + url + ", entity = " + entity);
GetPrepayIdResult result = new GetPrepayIdResult();
byte[] buf = WechatUtil.httpPost(url, entity);
if (buf == null || buf.length == 0) {
result.localRetCode = LocalRetCode.ERR_HTTP;
return result;
}
String content = new String(buf);
Log.d(TAG, "doInBackground, response content = " + content);
result.parseFrom(content);
return result;
}
@Override
protected void onPostExecute(GetPrepayIdResult result) {
if (dialog != null) {
dialog.dismiss();
}
if (result.localRetCode == LocalRetCode.ERR_OK) {
Toast.makeText(context, "获取prepayid成功", Toast.LENGTH_LONG).show();
payWithWechat(result);
} else {
Toast.makeText(context, "获取prepayid失败,原因" + result.localRetCode.name(), Toast.LENGTH_LONG).show();
}
}
private IWXAPI mWeixinApi;
// // 如果获取token和预付标识在服务器实现,只留下支付动作在客户端实现,那么下面要异步调用
// private void payWithWechat() {
// final String payInfo = "";
//
// Runnable payRunnable = new Runnable() {
// @Override
// public void run() {
// sendWXPayReq(payInfo);
// }
// };
//
// Thread payThread = new Thread(payRunnable);
// payThread.start();
// }
private String genPackage(List params) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < params.size(); i++) {
sb.append(params.get(i).getName());
sb.append('=');
sb.append(params.get(i).getValue());
sb.append('&');
}
sb.append("key=");
sb.append(WechatConstants.PARTNER_KEY); // 注意:不能hardcode在客户端,建议genPackage这个过程都由服务器端完成
// 进行md5摘要前,params内容为原始内容,未经过url encode处理
String packageSign = MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase(Locale.getDefault());
return URLEncodedUtils.format(params, "utf-8") + "&sign=" + packageSign;
}
private String genNonceStr() {
Random random = new Random();
return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());
}
private long genTimeStamp() {
return System.currentTimeMillis() / 1000;
}
private String getTraceId() {
return "crestxu_" + genTimeStamp();
}
private String genOutTradNo() {
Random random = new Random();
return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());
}
private long timeStamp;
private String nonceStr, packageValue;
private String genSign(List params) {
StringBuilder sb = new StringBuilder();
int i = 0;
for (; i < params.size() - 1; i++) {
sb.append(params.get(i).getName());
sb.append('=');
sb.append(params.get(i).getValue());
sb.append('&');
}
sb.append(params.get(i).getName());
sb.append('=');
sb.append(params.get(i).getValue());
String sha1 = WechatUtil.sha1(sb.toString());
Log.d(TAG, "genSign, sha1 = " + sha1);
return sha1;
}
private String genProductArgs() {
JSONObject json = new JSONObject();
try {
json.put("appid", WechatConstants.APP_ID);
String traceId = getTraceId(); // traceId
// 由开发者自定义,可用于订单的查询与跟踪,建议根据支付用户信息生成此id
json.put("traceid", traceId);
nonceStr = genNonceStr();
json.put("noncestr", nonceStr);
List packageParams = new LinkedList();
packageParams.add(new BasicNameValuePair("bank_type", "WX"));
packageParams.add(new BasicNameValuePair("body", goods_info[0]));
packageParams.add(new BasicNameValuePair("description", goods_info[1]));
packageParams.add(new BasicNameValuePair("fee_type", "1"));
packageParams.add(new BasicNameValuePair("input_charset", "UTF-8"));
packageParams.add(new BasicNameValuePair("notify_url", "http://weixin.qq.com"));
packageParams.add(new BasicNameValuePair("out_trade_no", genOutTradNo()));
packageParams.add(new BasicNameValuePair("partner", WechatConstants.PARTNER_ID));
packageParams.add(new BasicNameValuePair("spbill_create_ip", "196.168.1.1"));
packageParams.add(new BasicNameValuePair("total_fee", ""
+ (int) (Float.parseFloat(goods_info[2]) * 100)));
packageValue = genPackage(packageParams);
json.put("package", packageValue);
timeStamp = genTimeStamp();
json.put("timestamp", timeStamp);
List signParams = new LinkedList();
signParams.add(new BasicNameValuePair("appid", WechatConstants.APP_ID));
signParams.add(new BasicNameValuePair("appkey", WechatConstants.APP_KEY));
signParams.add(new BasicNameValuePair("noncestr", nonceStr));
signParams.add(new BasicNameValuePair("package", packageValue));
signParams.add(new BasicNameValuePair("timestamp", String.valueOf(timeStamp)));
signParams.add(new BasicNameValuePair("traceid", traceId));
json.put("app_signature", genSign(signParams));
json.put("sign_method", "sha1");
} catch (Exception e) {
Log.e(TAG, "genProductArgs fail, ex = " + e.getMessage());
return null;
}
return json.toString();
}
private void payWithWechat(GetPrepayIdResult result) {
PayReq req = new PayReq();
req.appId = WechatConstants.APP_ID;
req.partnerId = WechatConstants.PARTNER_ID;
req.prepayId = result.prepayId;
req.nonceStr = nonceStr;
req.timeStamp = String.valueOf(timeStamp);
req.packageValue = "Sign=" + packageValue;
List signParams = new LinkedList();
signParams.add(new BasicNameValuePair("appid", req.appId));
signParams.add(new BasicNameValuePair("appkey", WechatConstants.APP_KEY));
signParams.add(new BasicNameValuePair("noncestr", req.nonceStr));
signParams.add(new BasicNameValuePair("package", req.packageValue));
signParams.add(new BasicNameValuePair("partnerid", req.partnerId));
signParams.add(new BasicNameValuePair("prepayid", req.prepayId));
signParams.add(new BasicNameValuePair("timestamp", req.timeStamp));
req.sign = genSign(signParams);
Log.d(TAG, "WXAPIFactory.createWXAPI");
mWeixinApi = WXAPIFactory.createWXAPI(context, WechatConstants.APP_ID);
// 在支付之前,如果应用没有注册到微信,应该先调用IWXMsg.registerApp将应用注册到微信
Log.d(TAG, "mWeixinApi.sendReq");
mWeixinApi.sendReq(req);
}
}
package com.fukaimei.wechatpay.task;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;
import com.fukaimei.wechatpay.bean.GetAccessTokenResult;
import com.fukaimei.wechatpay.bean.LocalRetCode;
import com.fukaimei.wechatpay.bean.WechatConstants;
import com.fukaimei.wechatpay.util.WechatUtil;
public class GetAccessTokenTask extends AsyncTask {
private static final String TAG = "GetAccessTokenTask";
private Context context;
private ProgressDialog dialog;
private String[] goods_info;
public GetAccessTokenTask(Context context) {
this.context = context;
}
@Override
protected void onPreExecute() {
dialog = ProgressDialog.show(context, "提示", "正在获取access token...");
}
@Override
protected GetAccessTokenResult doInBackground(String... params) {
goods_info = new String[]{params[0], params[1], params[2]};
GetAccessTokenResult result = new GetAccessTokenResult();
String url = String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s",
WechatConstants.APP_ID, WechatConstants.APP_SECRET);
Log.d(TAG, "get access token, url = " + url);
byte[] buf = WechatUtil.httpGet(url);
if (buf == null || buf.length == 0) {
result.localRetCode = LocalRetCode.ERR_HTTP;
return result;
}
String content = new String(buf);
result.parseFrom(content);
return result;
}
@Override
protected void onPostExecute(GetAccessTokenResult result) {
if (dialog != null) {
dialog.dismiss();
}
Log.d(TAG, "RetCode=" + result.localRetCode + ", errCode=" + result.errCode + ", errMsg=" + result.errMsg);
if (result.localRetCode == LocalRetCode.ERR_OK) {
Toast.makeText(context, "获取access token成功, accessToken = " + result.accessToken, Toast.LENGTH_LONG).show();
GetPrepayIdTask getPrepayId = new GetPrepayIdTask(context, result.accessToken);
getPrepayId.execute(goods_info[0], goods_info[1], goods_info[2]);
} else {
Toast.makeText(context, "获取access token失败,原因: " + result.localRetCode.name(), Toast.LENGTH_LONG).show();
}
}
}
package com.fukaimei.wechatpay.bean;
public class WechatConstants {
// APP_ID 替换为你的应用从官方网站申请到的合法appId
public static final String APP_ID = "";
public static final String APP_SECRET = "";
// wxd930ea5d5a258f4f 对应的支付密钥
public static final String APP_KEY = "";
/** 商家向财付通申请的商家id */
public static final String PARTNER_ID = "";
public static final String PARTNER_KEY = "";
public static class ShowMsgActivity {
public static final String STitle = "showmsg_title";
public static final String SMessage = "showmsg_message";
public static final String BAThumbData = "showmsg_thumb_data";
}
}
package com.fukaimei.wechatpay;
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.wechatpay.task.GetAccessTokenTask;
public class WxpayActivity extends AppCompatActivity implements OnClickListener {
private static final String TAG = "WxpayActivity";
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_wxpay);
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_wechat).setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.btn_wechat) {
String title = et_goods_title.getText().toString();
String desc = et_goods_desc.getText().toString();
String price = et_goods_price.getText().toString();
new GetAccessTokenTask(this).execute(title, desc, price);
}
}
}
package com.fukaimei.wechatpay;
import com.fukaimei.wechatpay.bean.WechatConstants;
import com.tencent.mm.sdk.constants.ConstantsAPI;
import com.tencent.mm.sdk.modelbase.BaseReq;
import com.tencent.mm.sdk.modelbase.BaseResp;
import com.tencent.mm.sdk.modelbase.BaseResp.ErrCode;
import com.tencent.mm.sdk.openapi.IWXAPI;
import com.tencent.mm.sdk.openapi.IWXAPIEventHandler;
import com.tencent.mm.sdk.openapi.WXAPIFactory;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;
public class WXPayEntryActivity extends AppCompatActivity implements IWXAPIEventHandler {
private static final String TAG = "WXPayEntryActivity";
private IWXAPI api;
private TextView tv_result;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_wxpay_result);
tv_result = (TextView) findViewById(R.id.tv_result);
api = WXAPIFactory.createWXAPI(this, WechatConstants.APP_ID);
api.handleIntent(getIntent(), this);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
api.handleIntent(intent, this);
}
@Override
public void onReq(BaseReq req) {
}
@Override
public void onResp(BaseResp resp) {
Log.d(TAG, "onResp, errCode = " + resp.errCode);
String result = "";
if (resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {
switch (resp.errCode) {
case ErrCode.ERR_OK:
result = "微信支付成功";
break;
case ErrCode.ERR_COMM:
result = "微信支付失败:" + resp.errCode + "," + resp.errStr;
break;
case ErrCode.ERR_USER_CANCEL:
result = "微信支付取消:" + resp.errCode + "," + resp.errStr;
break;
default:
result = "微信支付未知异常:" + resp.errCode + "," + resp.errStr;
break;
}
}
Toast.makeText(this, result, Toast.LENGTH_LONG).show();
tv_result.setText(result);
}
}
———————— The end ————————
Demo程序源码下载地址一(GitHub)
Demo程序源码下载地址二(Gitee)