支付寶Pay,一個類直接搞定
* 支付寶支付
*
* @author super star boy
*/
public class Alipay {
// 商戶PID
public static final String PARTNER = "123456789";
// 商戶收款賬號
public static final String SELLER = "[email protected]";
// 商戶私鑰,pkcs8格式
public static final String RSA_PRIVATE = "rsa_private";
// 支付寶公鑰
public static final String RSA_PUBLIC = "rsa_public";
private static final int SDK_PAY_FLAG = 1;
private WeakReference<Activity> mActivity;
private OnAlipayListener mListener;
public Alipay(Activity activity) {
mActivity = new WeakReference<Activity>(activity);
}
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
if (msg.what == SDK_PAY_FLAG) {
PayResult payResult = new PayResult((String) msg.obj);
// 支付寶返回此次支付結果及加簽,建議對支付寶簽名信息拿簽約時支付寶提供的公鑰做驗籤
String resultInfo = payResult.getResult();
String resultStatus = payResult.getResultStatus();
// 判斷resultStatus 為“9000”則代表支付成功,具體狀態碼代表含義可參考介面文件
if (TextUtils.equals(resultStatus, "9000")) {
if (mListener != null) mListener.onSuccess();
} else {
// 判斷resultStatus 為非“9000”則代表可能支付失敗
// “8000”代表支付結果因為支付渠道原因或者系統原因還在等待支付結果確認,
// 最終交易是否成功以服務端非同步通知為準(小概率狀態)
if (TextUtils.equals(resultStatus, "8000")) {
if (mListener != null) mListener.onWait();
} else {
// 其他值就可以判斷為支付失敗,包括使用者主動取消支付,或者系統返回的錯誤
if (mListener != null) mListener.onCancel();
}
}
}
}
};
/**
* 支付
*
* @param title 標題 不能為空或者“”
* @param desc 描述 不能為空或者“”
* @param price 價格 不能為空或者“”
* @param sn 商品唯一貨號 不能為空或者“”
* @param url 伺服器回撥url 不能為空或者“”
*/
public void pay(String title, String desc, String price, String sn, String url) {
// 訂單
String orderInfo = getOrderInfo(title, desc, price, sn, url);
// 對訂單做RSA 簽名
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();
Runnable payRunnable = new Runnable() {
@Override
public void run() {
Activity activity = mActivity.get();
if(activity == null) return;
// 構造PayTask 物件
PayTask alipay = new PayTask(activity);
// 呼叫支付介面,獲取支付結果
String result = alipay.pay(payInfo);
Message msg = new Message();
msg.what = SDK_PAY_FLAG;
msg.obj = result;
mHandler.sendMessage(msg);
}
};
// 必須非同步呼叫
Thread payThread = new Thread(payRunnable);
payThread.start();
}
/**
* create the order info. 建立訂單資訊
*
*/
public String getOrderInfo(String subject, String body, String price,
String sn, String url) {
// 簽約合作者身份ID
String orderInfo = "partner=" + "/"" + PARTNER + "/"";
// 簽約賣家支付寶賬號
orderInfo += "&seller_id=" + "/"" + SELLER + "/"";
// 商戶網站唯一訂單號
orderInfo += "&out_trade_no=" + "/"" + sn + "/"";
// 商品名稱
orderInfo += "&subject=" + "/"" + subject + "/"";
// 商品詳情
orderInfo += "&body=" + "/"" + body + "/"";
// 商品金額
orderInfo += "&total_fee=" + "/"" + price + "/"";
// 伺服器非同步通知頁面路徑
orderInfo += "?ify_url=" + "/"" + url + "/"";
// 服務介面名稱, 固定值
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;
}
/**
* sign the order info. 對訂單資訊進行簽名
*
* @param content
*待簽名訂單資訊
*/
public String sign(String content) {
return SignUtils.sign(content, RSA_PRIVATE);
}
/**
* get the sign type we use. 獲取簽名方式
*
*/
public String getSignType() {
return "sign_type=/"RSA/"";
}
public void setListener(OnAlipayListener l) {
mListener = l;
}
/**
* 支付回撥介面
*
* @author lenovo
*
*/
public static class OnAlipayListener {
/**
* 支付成功
*/
public void onSuccess() {}
/**
* 支付取消
*/
public void onCancel() {}
/**
* 等待確認
*/
public void onWait() {}
}
}
final class Base64 {
private static final int BASELENGTH = 128;
private static final int LOOKUPLENGTH = 64;
private static final int TWENTYFOURBITGROUP = 24;
private static final int EIGHTBIT = 8;
private static final int SIXTEENBIT = 16;
private static final int FOURBYTE = 4;
private static final int SIGN = -128;
private static char PAD = '=';
private static byte[] base64Alphabet = new byte[BASELENGTH];
private static char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH];
static {
for (int i = 0; i < BASELENGTH; ++i) {
base64Alphabet[i] = -1;
}
for (int i = 'Z'; i >= 'A'; i--) {
base64Alphabet[i] = (byte) (i - 'A');
}
for (int i = 'z'; i >= 'a'; i--) {
base64Alphabet[i] = (byte) (i - 'a' + 26);
}
for (int i = '9'; i >= '0'; i--) {
base64Alphabet[i] = (byte) (i - '0' + 52);
}
base64Alphabet['+'] = 62;
base64Alphabet['/'] = 63;
for (int i = 0; i <= 25; i++) {
lookUpBase64Alphabet[i] = (char) ('A' + i);
}
for (int i = 26, j = 0; i <= 51; i++, j++) {
lookUpBase64Alphabet[i] = (char) ('a' + j);
}
for (int i = 52, j = 0; i <= 61; i++, j++) {
lookUpBase64Alphabet[i] = (char) ('0' + j);
}
lookUpBase64Alphabet[62] = (char) '+';
lookUpBase64Alphabet[63] = (char) '/';
}
private static boolean isWhiteSpace(char octect) {
return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);
}
private static boolean isPad(char octect) {
return (octect == PAD);
}
private static boolean isData(char octect) {
return (octect < BASELENGTH && base64Alphabet[octect] != -1);
}
/**
* Encodes hex octects into Base64
*
* @param binaryData
*Array containing binaryData
* @return Encoded Base64 array
*/
public static String encode(byte[] binaryData) {
if (binaryData == null) {
return null;
}
int lengthDataBits = binaryData.length * EIGHTBIT;
if (lengthDataBits == 0) {
return "";
}
int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1
: numberTriplets;
char encodedData[] = null;
encodedData = new char[numberQuartet * 4];
byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
int encodedIndex = 0;
int dataIndex = 0;
for (int i = 0; i < numberTriplets; i++) {
b1 = binaryData[dataIndex++];
b2 = binaryData[dataIndex++];
b3 = binaryData[dataIndex++];
l = (byte) (b2 & 0x0f);
k = (byte) (b1 & 0x03);
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
: (byte) ((b1) >> 2 ^ 0xc0);
byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4)
: (byte) ((b2) >> 4 ^ 0xf0);
byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6)
: (byte) ((b3) >> 6 ^ 0xfc);
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3];
encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f];
}
// form integral number of 6-bit groups
if (fewerThan24bits == EIGHTBIT) {
b1 = binaryData[dataIndex];
k = (byte) (b1 & 0x03);
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
: (byte) ((b1) >> 2 ^ 0xc0);
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4];
encodedData[encodedIndex++] = PAD;
encodedData[encodedIndex++] = PAD;
} else if (fewerThan24bits == SIXTEENBIT) {
b1 = binaryData[dataIndex];
b2 = binaryData[dataIndex + 1];
l = (byte) (b2 & 0x0f);
k = (byte) (b1 & 0x03);
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
: (byte) ((b1) >> 2 ^ 0xc0);
byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4)
: (byte) ((b2) >> 4 ^ 0xf0);
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2];
encodedData[encodedIndex++] = PAD;
}
return new String(encodedData);
}
/**
* Decodes Base64 data into octects
*
* @param encoded
*string containing Base64 data
* @return Array containind decoded data.
*/
public static byte[] decode(String encoded) {
if (encoded == null) {
return null;
}
char[] base64Data = encoded.toCharArray();
// remove white spaces
int len = removeWhiteSpace(base64Data);
if (len % FOURBYTE != 0) {
return null;// should be divisible by four
}
int numberQuadruple = (len / FOURBYTE);
if (numberQuadruple == 0) {
return new byte[0];
}
byte decodedData[] = null;
byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;
char d1 = 0, d2 = 0, d3 = 0, d4 = 0;
int i = 0;
int encodedIndex = 0;
int dataIndex = 0;
decodedData = new byte[(numberQuadruple) * 3];
for (; i < numberQuadruple - 1; i++) {
if (!isData((d1 = base64Data[dataIndex++]))
|| !isData((d2 = base64Data[dataIndex++]))
|| !isData((d3 = base64Data[dataIndex++]))
|| !isData((d4 = base64Data[dataIndex++]))) {
return null;
}// if found "no data" just return null
b1 = base64Alphabet[d1];
b2 = base64Alphabet[d2];
b3 = base64Alphabet[d3];
b4 = base64Alphabet[d4];
decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
}
if (!isData((d1 = base64Data[dataIndex++]))
|| !isData((d2 = base64Data[dataIndex++]))) {
return null;// if found "no data" just return null
}
b1 = base64Alphabet[d1];
b2 = base64Alphabet[d2];
d3 = base64Data[dataIndex++];
d4 = base64Data[dataIndex++];
if (!isData((d3)) || !isData((d4))) {// Check if they are PAD characters
if (isPad(d3) && isPad(d4)) {
if ((b2 & 0xf) != 0)// last 4 bits should be zero
{
return null;
}
byte[] tmp = new byte[i * 3 + 1];
System.arraycopy(decodedData, 0, tmp, 0, i * 3);
tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
return tmp;
} else if (!isPad(d3) && isPad(d4)) {
b3 = base64Alphabet[d3];
if ((b3 & 0x3) != 0)// last 2 bits should be zero
{
return null;
}
byte[] tmp = new byte[i * 3 + 2];
System.arraycopy(decodedData, 0, tmp, 0, i * 3);
tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
return tmp;
} else {
return null;
}
} else { // No PAD e.g 3cQl
b3 = base64Alphabet[d3];
b4 = base64Alphabet[d4];
decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
}
return decodedData;
}
/**
* remove WhiteSpace from MIME containing encoded Base64 data.
*
* @param data
*the byte array of base64 data (with WS)
* @return the new length
*/
private static int removeWhiteSpace(char[] data) {
if (data == null) {
return 0;
}
// count characters that's not whitespace
int newSize = 0;
int len = data.length;
for (int i = 0; i < len; i++) {
if (!isWhiteSpace(data[i])) {
data[newSize++] = data[i];
}
}
return newSize;
}
}
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;
}
}
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;
}
}
//前面的幾個常量是需要去支付寶官網獲取的,獲取後直接替換就ok,其他的程式碼基本都是從demo中copy出來的, 現在我們就將支付功能封裝到了一個類中,那麼如何使用呢?
Alipay alipay = new Alipay(OrderConfirmActivity.this);
alipay.setListener(mAlipayListener);
alipay.pay(desc, mOrder.getShopName(), String.valueOf(orderAmount), orderSn, url);
/**
* 支付寶支付回撥
*/
private Alipay.OnAlipayListener mAlipayListener = new Alipay.OnAlipayListener() {
@Override
public void onSuccess() {
onOrderSubmitSuccess();
}
@Override
public void onCancel() {
onUserOrderCanceled();
Toast.makeText(OrderConfirmActivity.this, R.string.pay_failed,
Toast.LENGTH_SHORT).show();
}
@Override
public void onWait() {
}
};