記錄自己的支付整合(微信&支付寶)
首先,作為一個菜鳥表示,雖然網上的整合框架很多,但是我要自己寫,就算被坑的死去活來也要自己寫。
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中獲取(測試階段)
自己測試的時候可以這麼寫:
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, "支付取消");
}
});
好的,打完收工,完成之後發現,其實也就是那麼回事。