1. 首先去支付宝官网下载开发者文档
2. 然后按着开发者文档将支付宝的sdk导入到你的工程中,并关联到工程中,步骤入下图:
(1)将从支付宝官方网站获得的支付宝的sdk的jar包拷贝到工程中的libs目录下,如图
(2)右键工程选择Properties,弹出如下窗口,首先选择1,其次选中2,最后点击3将拷到工程中的三个支付宝的jar包一一关联到工程中
3. 最后修改工程的AndroidManifest.xml文件,添加新的权限和支付宝的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" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
支付宝的Activity:
<!-- alipay sdk begin --> <activity android:name="com.alipay.sdk.app.H5PayActivity" android:configChanges="orientation|keyboardHidden|navigation" android:exported="false" android:screenOrientation="behind" android:windowSoftInputMode="adjustResize|stateHidden" > </activity> <!-- alipay sdk end -->
到此为止支付宝sdk导入工程的工作算是完成了,接下来就是使用sdk完成付费了。
虽然破坏了java的封装性,但是为了方便起见我还是将支付宝的使用写到了一个java文件里了,我把使用放到了工程自带的HelloCpp.java文件中了,代码如/****************************************************************************Copyright (c) 2010-2012 cocos2d-x.orghttp://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ****************************************************************************/ package org.cocos2dx.hellocpp2; 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 org.cocos2dx.lib.Cocos2dxActivity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.text.TextUtils; import android.util.Log; import android.view.View; import android.widget.Toast; import com.alipay.sdk.app.PayTask; public class HelloCpp extends Cocos2dxActivity{ //private long exitTime = 0; // 私有的类对象 private static HelloCpp hiCpp = null; // 传回支付结果 public static native void payResultState(int payState);
// 以下参数根据自己的实际情况而定 //商户PID public static final String PARTNER = "2088811977704990"; //商户收款账号 public static final String SELLER = "dev@ifensi.com"; //商户私钥,pkcs8格式 public static final String RSA_PRIVATE = "MIICXQIBAAKBgQDlQ468L1A7Q+GG80/Z8f3IsSiiFIluSxfTTSuJ/XSPzvYS+bMZ" +"AQLMqq/nGhkp+1Q5pHF9LAQtQS3gL2pqzbKdtvZSsy/tNFFQcGCsgK2ygMl+MW/F" +"g/ufx7c1jy1kZAeDyl1m302dnRrtSgDalkgH7FKRcmDxbXPTnFGHbg9zMQIDAQAB" +"AoGAa28wGQF28H7L1Yh5V+FtkrlqGCHVkQjBfnRAPea205kheRzoD4SIwk4OJhb1" +"ydWLz4M+53BT+Lz9eXveu3PvCdQe9zMIVC5dKUNVYCvvcHZ+Ot8HriiuwGPb3Quu" +"twbnLGM5gxxPDo0yUyWrfaVn/qR35mS6TDfmgowVG8CmBpECQQDzuhodR/Jgxrtn" +"tka+88alyy+BfjUZqNloPuE7JfXrpOxH5lodk7Y4lTki/dlo5BrK+hrismLFr9Du" +"ueAJ7G9dAkEA8M8C6VnpUMAK5+rYcjKnQssDqcMfurKYEil1BD/TUdSbLI6v8p02" +"mv1ApuTVtQQypZJKIFfurGk0g0QlvzLZ5QJAGfY38+iHDAH/UnPbI1oKTfzPyaZs" +"95fB2NXh3hAUGw7NUHdcIAxs+6gBlxWdRAwQQpDTrlQ8KzyoL9XC5Ku3zQJBALO8" +"j5vEtFTFQl6f9zYlgJpmFTHcpg4fx0mnD+RAD2aAneHADquzlFJSvLLVEn2tyG+0" +"pQdHGqotTDi94L65IdECQQDb1h+5kugCu47IxsDkrLRsKVcr8dSDMORyeT1L0HWR" +"ctramBu+2PBz2UKC6+9dQ+ZQH4XTKpBSvkyZH4mYi1de"; //支付宝公钥 public static final String RSA_PUBLIC = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnxj/9qwVfgoUh/y2W89L6BkRAFljhNhgPdyPuBV64bfQNN1PjbCzkIM6qRdKBoLPXmKKMiFYnkd6rAoprih3/PrQEB/VsW8OoM8fxn67UDYuyBTqA23MML9q1+ilIZwBC2AQ2UBVOrFXfFl75p6/B5KsiNG9zpgmLCUYuLkxpLQIDAQAB"; private static final int SDK_PAY_FLAG = 1; private static final int SDK_CHECK_FLAG = 2; // 支付结果的处理(接收子线程反馈回来的消息) private Handler mHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case 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")) { //Toast.makeText(HelloCpp.this, "支付成功", Toast.LENGTH_SHORT).show(); HelloCpp.payResultState( 1 ); } else { // 判断resultStatus 为非“9000”则代表可能支付失败 // “8000”代表支付结果因为支付渠道原因或者系统原因还在等待支付结果确认,最终交易是否成功以服务端异步通知为准(小概率状态) if (TextUtils.equals(resultStatus, "8000")) { // Toast.makeText(HelloCpp.this, "支付结果确认中", Toast.LENGTH_SHORT).show(); HelloCpp.payResultState( 3 ); } else { // 其他值就可以判断为支付失败,包括用户主动取消支付,或者系统返回的错误 // Toast.makeText(HelloCpp.this, "支付失败", Toast.LENGTH_SHORT).show(); HelloCpp.payResultState( 2 ); } } break; } case SDK_CHECK_FLAG: { Toast.makeText(HelloCpp.this, "检查结果为:" + msg.obj, Toast.LENGTH_SHORT).show(); break; } default: break; } }; }; protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); hiCpp = this; } public static Object rtnActivity() { System.out.println("----------rtnActivity"); return hiCpp ; } static { System.loadLibrary("hellocpp"); } /** * call alipay sdk pay. 调用SDK支付 * */ public void pay(String title, String content, float f) { String price = Float.toString( f ); // 订单 String orderInfo = getOrderInfo( title, content, price ); // 对订单做 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() { // 构造PayTask 对象 PayTask alipay = new PayTask(HelloCpp.this); // 调用支付接口,获取支付结果 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(); } /** * check whether the device has authentication alipay account. * 查询终端设备是否存在支付宝认证账户 * */ public void check(View v) { Runnable checkRunnable = new Runnable() { @Override public void run() { // 构造PayTask 对象 PayTask payTask = new PayTask(HelloCpp.this); // 调用查询接口,获取查询结果 boolean isExist = payTask.checkAccountIfExist(); Message msg = new Message(); msg.what = SDK_CHECK_FLAG; msg.obj = isExist; mHandler.sendMessage(msg); } }; Thread checkThread = new Thread(checkRunnable); checkThread.start(); } /** * get the sdk version. 获取SDK版本号 * */ public void getSDKVersion() { PayTask payTask = new PayTask(this); String version = payTask.getVersion(); Toast.makeText(this, version, Toast.LENGTH_SHORT).show(); } /** * create the order info. 创建订单信息 * */ public String getOrderInfo(String subject, String body, String price) { // 签约合作者身份ID String orderInfo = "partner=" + "\"" + PARTNER + "\""; // 签约卖家支付宝账号 orderInfo += "&seller_id=" + "\"" + 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; } /** * get the out_trade_no for an order. 生成商户订单号,该值在商户端应保持唯一(可自定义格式规范) * */ 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); return key; } /** * 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\""; } }
还有支付宝封装好的几个java文件,我把它们跟HelloCpp.java文件放到了同一个包中,它们分别是Base64.java,PayResult.java,SignUtils.java,源码如下:
(1)Base64.java文件源码
package org.cocos2dx.hellocpp2; public 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; } }
(2)PayResult.java文件源码
package org.cocos2dx.hellocpp2; 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; } }
(3)SignUtils.java文件源码
package org.cocos2dx.hellocpp2; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.spec.PKCS8EncodedKeySpec; public 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); KeyFactory keyf = KeyFactory.getInstance(ALGORITHM, "BC"); 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; } }
至此cocos2dx工程中接入支付宝的工作完成!
2015-04-14 18:04:56