首先,作为一个菜鸟表示,虽然网上的集成框架很多,但是我要自己写,就算被坑的死去活来也要自己写。
1. 支付宝
这个支付宝的文档是个好东西,挺简单,挺容易看懂的,其次还有沙箱测试,表示支付宝一次通过很开心。 支付沙箱联调指南
首先导包,这里使用 20170710 的包,提前说明如果使用沙箱需要在支付的activity中添加
EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX);
支付的账号和密码去沙箱中获取,支付沙箱联调指南页面的最下方,点击沙箱工具,跳转过后,页面左侧研发服务-沙箱账号
@Override
protected void onCreate(Bundle savedInstanceState) {
EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pay);
initView();
}
这个直接上代码:ALiPay.java
public class AliPay {
// 2017/7/20 沙盒测试通过 代码无误
//支付宝支付业务:沙箱使用app_id
public static final String APPID = "2016102200739552";
//支付宝支付业务:沙箱使用RSA2_PRIVATE
public static final String RSA2_PRIVATE = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCTci2x3b/OPT2ESK4+yZpBP7yQxxWdpB2Ht4XL7Vxa+Oa1je3lnnGImtAPsk9Z/LcgrMdg1mrjAP0cHUUHCVs8e70NR1P0YN6zFFDjZ/I4HWYUvAHAB0b/zPCT24xHbekIuyjpvTQKTRPu1QRPyCAlscJjVf6u0aUNh8tTsekxtS5tBOvQ5cm9kHw09XYaO+XtI39ocDj7rzTOkC1uQDfed2YZLXKVbiHm3WNJ71isL9bbC0nkjkBKRDTAGvyDTecvBy/mi8OnVJr9sg270UMNAtgTLONV0eKZp5JSuLwL0PGFXKc07OPb33Na/nsX9qpl6utjETNjJw2AatKMoVEpAgMBAAECggEAZyyXYwX7aHKb8Ev3ecISqaPS7DATJRso+sXl3vv6C0JuFg75sBp98Yv7GXC5bAuVjUy5uz5uzufrNVgZ7EGU0S747CjES5XZX9BQhcA/0xpnSwz/4IJ3IRokIiKX94emcFCe4Whe2PJ0h0QR3033iMjpcB9FtsjGSUCDe85bkkbYbpzTPiv0B5BQlkuSYPR5jR4aeURLJ4qDBiLF+pJHrNy9w0cxPEgKSpY6fusi9fEfs3dKq1a0TbgOMlpHE4ZlOqkjWZld9Z6yU6AYtxfhVzYeYobhPjicmf7cTahfYPYyP4zYfR3PrFOtTCSEOthyzr5TASZeXNMWmyuOb+jnjQKBgQDP4oXSwA7gVMiAk/P7SMRbyn4TEh97CJeWIRWAFJ8r8lCzBuNOMBNbjcsC+3kRdy8rFuFH3yjfKIo2Av/zYT/b441Xm9csVlHH7NLcRD6EYV1DLvgEEc4T+k8jmwRWeri+qzkyY2fH0X8ZdUdMvE271oMViyju5rgQClYMbzVViwKBgQC1kpEsnuXCds7CT89XUqX/nLSqTIfnejm9z/CnR7sPEISI8nQvdokTtyuRm9oV1LZqtN5Zj0Qqo+j3Gvmzgb0pw0sCVAYfmn4+ldicGYNkF1PNgPk8xlLwB395P6Fu8XbyQqlXuTrFVcTz0HWGdkAUuEuhFQgeD9OrqXXCA9BSmwKBgDYe/UQe6ECTEhgXbL+Q9D4Je8UvRK7dT8mwF07fD4l7bnMNagQjFAcT5TSDj8NySf9n14LEoHloroLdSRFt0hhHJ7cVRXGvj18DUuoxgi0oxAUHp1433HTrB8t3QivZi1tobF2n747gBbz1AXkC1SH/+OSU9DUuL+FNL5XRJgt9AoGAKq1kigRfJLIgLvPrXC8E7Wu72ztZxkKoR8EUY30sroHHZRj3ziAiYAvxpavoOrFgnvwcNxjBgPQ90bb5cgPQnnxUqRtuxQbfHX7DBw3IIEKLZAYojuxemiRpBeq62wTOXGrmusPC2JcsT9JzjUNGFJiszhPPcKFvsy2FjDCxSnkCgYEAt+BPODj5cV+aRKvMWdGAapPo/GOVBmQHTtNN/Cw7Lahg9sdunpbAGvyMxfosygnRKV+Ny7VDE4k9wRbm6bIu7esEj5pZv/5fuXU5yCGDNe04GbrUlTbo5Vmw2ChjppMFZSDQDheNn5wiPkTan9Rkx9mZeaHzrZZaKs2vAskKamI=";
private static AliPay mAliPay;
private Activity mContext;
private PayListener mPayListener;
private String orderTradeNo;
private AliPay(Activity context) {
mContext = context;
}
public static AliPay getInstance(Activity context) {
if (mAliPay == null) {
synchronized (AliPay.class) {
if (mAliPay == null) {
mAliPay = new AliPay(context);
}
}
}
return mAliPay;
}
public void startAliPay(final String signOrderInfo, PayListener listener) {
mPayListener = listener;
Runnable payRunnable = new Runnable() {
@Override
public void run() {
PayTask alipay = new PayTask(mContext);
Map<String, String> result = alipay.payV2(signOrderInfo, true);
Message msg = new Message();
msg.obj = result;
mHandler.sendMessage(msg);
}
};
Thread payThread = new Thread(payRunnable);
payThread.start();
}
private Handler mHandler = new Handler(Looper.getMainLooper()) {
@SuppressWarnings("unchecked")
public void handleMessage(Message msg) {
PayResult payResult = new PayResult((Map<String, String>) msg.obj);
String resultStatus = payResult.getResultStatus();
String resultInfo = payResult.getResult();
Log.i("resultinfo", resultInfo);
if (mPayListener == null) {
return;
}
// https://doc.open.alipay.com/doc2/detail.htm?spm=a219a.7629140.0.0.xN1NnL&treeId=204&articleId=105302&docType=1
if (payResult == null) {
mPayListener.onPayError("结果解析错误");
return;
}
if (TextUtils.equals(resultStatus, "9000")) {
//支付成功
mPayListener.onPaySuccess();
} else if (TextUtils.equals(resultStatus, "8000")) {
//正在处理中,支付结果未知(有可能已经支付成功),请查询商户订单列表中订单的支付状态
mPayListener.onPayError("正在处理结果中");
} else if (TextUtils.equals(resultStatus, "6001")) {
//支付取消
mPayListener.onPayCancel();
} else if (TextUtils.equals(resultStatus, "6002")) {
//网络连接出错
mPayListener.onPayError("网络连接出错");
} else if (TextUtils.equals(resultStatus, "4000")) {
//支付错误
mPayListener.onPayError("订单支付失败");
} else {
mPayListener.onPayError(resultInfo);
}
}
};
/**
* 使用支付宝创建订单信息
* 这个签名工作要放在后台服务器中进行处理
* 前端将 付订单参数信息 上传到服务器 后台进行签名处理后,将签名后的订单信息发回来
*
* @param itemName
* @param itemDescribe
* @param itemPrice
*/
public Map createOrderParamMap(String itemName, String itemDescribe, double itemPrice, String itemTradeNo) {
//这里默认使用rsa2私钥
Map<String, String> params = buildOrderParamMap(APPID, itemName, itemDescribe, itemPrice, itemTradeNo, true);
return params;
}
public String createOrderInfo(Map<String, String> params) {
return buildOrderParam(params);
}
/**
* 构造支付订单参数列表
*
* @param app_id
* @param itemName 商品名称
* @param itemDecribe 商品详情
* @param itemPrice 商品价格
* @param rsa2 是否使用rsa2 建议true;
* @return
*/
private Map<String, String> buildOrderParamMap(String app_id, String itemName, String itemDecribe, double itemPrice, String itemTradeNo, boolean rsa2) {
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\":\"" + itemPrice + "\",\"subject\":\"" + itemName + "\",\"body\":\"" + itemDecribe + "\",\"out_trade_no\":\"" + itemTradeNo + "\"}");
keyValues.put("charset", "utf-8");
keyValues.put("method", "alipay.trade.app.pay");
keyValues.put("sign_type", rsa2 ? "RSA2" : "RSA");
keyValues.put("timestamp", Comm.formatNowTime());
keyValues.put("version", "1.0");
return keyValues;
}
/**
* 构造支付订单参数信息
*
* @param map 支付订单参数
* @return
*/
private String buildOrderParam(Map<String, String> map) {
List<String> keys = new ArrayList<String>(map.keySet());
StringBuilder sb = new StringBuilder();
StringBuilder s = 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("&");
s.append(key);
s.append("=");
s.append(value);
s.append("&");
}
String tailKey = keys.get(keys.size() - 1);
String tailValue = map.get(tailKey);
sb.append(buildKeyValue(tailKey, tailValue, true));
s.append(tailKey);
s.append("=");
s.append(tailValue);
Log.i("orderinfo",s.toString());
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();
}
/**
* 要求外部订单号必须唯一。
*
* @return
*/
public 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);
orderTradeNo = key;
return key;
}
/**
* 对支付参数信息进行签名
*
* @param map 待签名授权信息
* @return
*/
public String getSign(Map<String, String> map) {
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(), RSA2_PRIVATE, true);
String encodedSign = "";
try {
encodedSign = URLEncoder.encode(oriSign, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return "sign=" + encodedSign;
}
}
然后自定义一个支付结果接口: PayListener
public interface PayListener {
//支付成功
void onPaySuccess();
//支付失败
void onPayError(String resultStatus);
//支付取消
void onPayCancel();
}
然后调用:
注意:
1、这里的signOrderInfo必须要从服务器获取,我的后台这里还是给力的,上传订单id就把signOrderInfo传过来了,都不用自己写,多亏了我的口才。
2、这里的orderInfo是是进行 URLEncoder.encode() 转码的(键为原文,值进行转码),具体内容可以打印出来看下,然后加密sign内容是使用原文(没有进行转码)进行加密
3、这里的Base64.java PayResult.java SignUtils.java 去支付宝的demo中获取(测试阶段)
这里的Base64.java PayResult.java SignUtils.java 去支付宝的demo中获取(测试阶段)
这里的Base64.java PayResult.java SignUtils.java 去支付宝的demo中获取(测试阶段)
这里就是Demo的下载地址了 https://docs.open.alipay.com/54/104509
自己测试的时候可以这么写:
AliPay aliPay = AliPay.getInstance(this);
Map orderParamMap = aliPay.createOrderParamMap("商品名字", "商品描述", 0.01, "123123123123123");
String orderInfo = aliPay.createOrderInfo(orderParamMap);
String sign = aliPay.getSign(orderParamMap);
String signOrderInfo = orderInfo + "&" + sign;
AliPay.getInstance(PayActivity.this).startAliPay(signOrderInfo, new PayListener() {
@Override
public void onPaySuccess() {
Comm.Tip(mContext, "支付成功");
}
@Override
public void onPayError(String resultStatus) {
Comm.Tip(mContext, "支付失败");
}
@Override
public void onPayCancel() {
Comm.Tip(mContext, "支付取消");
}
});
2.微信支付
微信支付满满的都是坑,特别是官方Demo,看的我头晕,然后还是没弄好,很尴尬。
首先注意一个大坑:我们后台申请接入时候签名填写错误,我就想说ios没有签名,安卓要签名,然后导致了ios成功,安卓失败,我找了一下午的原因。
注意:微信开发平台的申请的应用签名,如果在手机上安装debug包获取的签名,那么只能debug包才能调用微信支付,如果是release包获取的签名,那么只有release包才能调用微信支付,当然,微信开发平台上的签名和包名都是可以更换的。
首先导个包,gradle方式也可以,然而我用AS下不下来,只好导包
简单粗暴上代码:WechatPay.java
public class WechatPay {
private static WechatPay wechatPay;
private Context mContext;
private IWXAPI mIWXAPI;
private PayListener mPayListener;
private WechatPay(Activity context) {
mContext = context;
}
public static WechatPay getInstance(Activity context) {
if (wechatPay == null) {
synchronized (WechatPay.class) {
if (wechatPay == null) {
wechatPay = new WechatPay(context);
}
}
}
return wechatPay;
}
/**
* 初始化微信支付接口
*
* @param appId
*/
public void init(String appId) {
mIWXAPI = WXAPIFactory.createWXAPI(mContext, null);
mIWXAPI.registerApp(appId);
}
/**
* 获取微信接口
*
* @return
*/
public IWXAPI getWXApi() {
return mIWXAPI;
}
/**
* 调起支付
*
* @param appId
* @param partnerId
* @param prepayId
* @param nonceStr
* @param timeStamp
* @param sign
*/
public void startWeChatPay(String appId, String partnerId, String prepayId,
String nonceStr, String timeStamp, String sign, PayListener listener) {
mPayListener = listener;
init(appId);
if (!checkWx()) {
if (listener != null) {
listener.onPayError("未安装微信或者微信版本过低");
}
return;
}
PayReq request = new PayReq();
request.appId = appId;
request.partnerId = partnerId;
request.prepayId = prepayId;
request.packageValue = "Sign=WXPay";
request.nonceStr = nonceStr;
request.timeStamp = timeStamp;
request.sign = sign;
mIWXAPI.sendReq(request);
}
/**
* 响应支付回调
*
* @param error_code
* @param message
*/
public void onResp(int error_code, String message) {
if (error_code == 0) {
//支付成功
mPayListener.onPaySuccess();
} else if (error_code == -1) {
//支付异常
mPayListener.onPayError(message);
} else if (error_code == -2) {
//支付取消
mPayListener.onPayCancel();
}
mPayListener = null;
}
//检测微信客户端是否支持微信支付
private boolean checkWx() {
return isWeixinAvilible() && mIWXAPI.isWXAppInstalled() && mIWXAPI.getWXAppSupportAPI() >= Build.PAY_SUPPORTED_SDK_INT;
}
/**
* 判断微信是否安装
*
* @return
*/
private boolean isWeixinAvilible() {
return appIsAvilible("com.tencent.mm");
}
/**
* 判断app是否安装
*
* @param packageName
* @return
*/
private boolean appIsAvilible(String packageName) {
final PackageManager packageManager = mContext.getPackageManager();// 获取packagemanager
List<PackageInfo> pinfo = packageManager.getInstalledPackages(0);// 获取所有已安装程序的包信息
if (pinfo != null) {
for (int i = 0; i < pinfo.size(); i++) {
String pn = pinfo.get(i).packageName;
if (pn.equals(packageName)) {
return true;
}
}
}
return false;
}
}
其次是:WXPayEntryActivity .java
public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (WechatPay.getInstance(this) != null) {
WechatPay.getInstance(this).getWXApi().handleIntent(getIntent(), this);
} else {
finish();
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
if (WechatPay.getInstance(this) != null) {
WechatPay.getInstance(this).getWXApi().handleIntent(intent, this);
}
}
@Override
public void onReq(BaseReq baseReq) {
}
@Override
public void onResp(BaseResp baseResp) {
if (baseResp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {
if (WechatPay.getInstance(this) != null) {
WechatPay.getInstance(this).onResp(baseResp.errCode, baseResp.errStr);
finish();
}
}
}
}
再来清单文件中:
改下第二行和最后一行的地址
<!-- 微信 -->
<activity
android:name=".Controller.wxapi.WXPayEntryActivity"
android:configChanges="orientation|keyboardHidden|navigation|screenSize"
android:launchMode="singleTop"
android:theme="@android:style/Theme.Translucent.NoTitleBar"/>
<activity-alias
android:name=".wxapi.WXPayEntryActivity"
android:exported="true"
android:targetActivity=".Controller.wxapi.WXPayEntryActivity"/>
好的,现在开始调用:
我的order类当然也是后台发给我的喽
WechatPay.getInstance(PayActivity.this).startWeChatPay(order.getAppid(), order.getPartid(), order.getPrepayid(), order.getNoncestr(), order.getTimestamp(), order.getSign(), new PayListener() {
@Override
public void onPaySuccess() {
Comm.Tip(mContext, "支付成功");
}
@Override
public void onPayError(String resultStatus) {
Comm.Tip(mContext, "支付失败");
}
@Override
public void onPayCancel() {
Comm.Tip(mContext, "支付取消");
}
});
好的,打完收工,完成之后发现,其实也就是那么回事。