接入前准备

接入APP支付能力前,开发者需要完成以下前置步骤。

本文档展示了如何从零开始,使用支付宝开放平台服务端 SDK 快速接入App支付产品,完成与支付宝对接的部分。

接入准备——支付宝开发能力

一.下载官方sdk,将sdk放入自己工程libs文件中:

并且在我们的app/build.gradle里配置一下

// 支付宝 SDK AAR 包所需的配置

compile (name: 'alipaysdk-15.8.03.210428205839', ext: 'aar')

//这里alipaysdk-15.8.03.210428205839必须和导入的sdk名字一样

二.配置清单文件AndroidManifest.xml: ①添加Activity声明:

android:name="com.alipay.sdk.pay.demo.PayDemoActivity"

android:label="@string/app_name" >

android:name="com.alipay.sdk.pay.demo.H5PayDemoActivity"

android:configChanges="orientation|keyboardHidden|navigation"

android:exported="false"

android:screenOrientation="behind" >

②添加权限声明:

如果想混淆代码,在工程proguard-rules.pro添加如下代码:

-keep class com.alipay.android.app.IAlixPay{*;}

-keep class com.alipay.android.app.IAlixPay$Stub{*;}

-keep class com.alipay.android.app.IRemoteServiceCallback{*;}

-keep class com.alipay.android.app.IRemoteServiceCallback$Stub{*;}

-keep class com.alipay.sdk.app.PayTask{ public *;}

-keep class com.alipay.sdk.app.AuthTask{ public *;}

-keep class com.alipay.sdk.app.H5PayCallback {

;

;

}

-keep class com.alipay.android.phone.mrpc.core.** { *; }

-keep class com.alipay.apmobilesecuritysdk.** { *; }

-keep class com.alipay.mobile.framework.service.annotation.** { *; }

-keep class com.alipay.mobilesecuritysdk.face.** { *; }

-keep class com.alipay.tscenter.biz.rpc.** { *; }

-keep class org.json.alipay.** { *; }

-keep class com.alipay.tscenter.** { *; }

-keep class com.ta.utdid2.** { *;}

-keep class com.ut.device.** { *;}

三.支付接口调用

PayDemoActivity.java

public class PayDemoActivity extends AppCompatActivity {

public static int price=100;

/**

* 用于支付宝支付业务的入参 app_id。

*/

public static final String APPID = "************";

/**

* 用于支付宝账户登录授权业务的入参 pid。

*/

public static final String PID = "************";

/**

* 用于支付宝账户登录授权业务的入参 target_id。商家的收款账号

*/

public static final String TARGET_ID = "************";

/**

* pkcs8 格式的商户私钥。

*

* 如下私钥,RSA2_PRIVATE 或者 RSA_PRIVATE 只需要填入一个,如果两个都设置了,本 Demo 将优先

* 使用 RSA2_PRIVATE。RSA2_PRIVATE 可以保证商户交易在更加安全的环境下进行,建议商户使用

* RSA2_PRIVATE。

*

* 建议使用支付宝提供的公私钥生成工具生成和获取 RSA2_PRIVATE。

* 工具地址:https://doc.open.alipay.com/docs/doc.htm?treeId=291&articleId=106097&docType=1

*/

public static final String RSA2_PRIVATE = "************";//密钥

public static final String RSA_PRIVATE = "";

private static final int SDK_PAY_FLAG = 1;

private static final int SDK_AUTH_FLAG = 2;

@SuppressLint("HandlerLeak")

private Handler mHandler = new Handler() {

@SuppressWarnings("unused")

public void handleMessage(Message msg) {

switch (msg.what) {

case SDK_PAY_FLAG: {

@SuppressWarnings("unchecked")

PayResult payResult = new PayResult((Map) msg.obj);

/**

* 对于支付结果,请商户依赖服务端的异步通知结果。同步通知结果,仅作为支付结束的通知。

*/

String resultInfo = payResult.getResult();// 同步返回需要验证的信息

String resultStatus = payResult.getResultStatus();

// 判断resultStatus 为9000则代表支付成功

if (TextUtils.equals(resultStatus, "9000")) {

// 该笔订单是否真实支付成功,需要依赖服务端的异步通知。

showAlert(PayDemoActivity.this, getString(R.string.pay_success) + payResult);

} else {

// 该笔订单真实的支付结果,需要依赖服务端的异步通知。

showAlert(PayDemoActivity.this, getString(R.string.pay_failed) + payResult);

}

break;

}

}

};

};

@Override

protected void onCreate(Bundle savedInstanceState) {

// EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX);//沙箱环境需要的代码

super.onCreate(savedInstanceState);

setContentView(R.layout.pay_main);

Button button = findViewById(R.id.payV2);

button.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

payV2(v);

}

});

}

/**

* 支付宝支付业务示例

*/

public void payV2(View v) {

if (TextUtils.isEmpty(APPID) || (TextUtils.isEmpty(RSA2_PRIVATE) && TextUtils.isEmpty(RSA_PRIVATE))) {

showAlert(this, getString(R.string.error_missing_appid_rsa_private));

return;

}

/*

* 这里只是为了方便直接向商户展示支付宝的整个支付流程;所以Demo中加签过程直接放在客户端完成;

* 真实App里,privateKey等数据严禁放在客户端,加签过程务必要放在服务端完成;

* 防止商户私密数据泄露,造成不必要的资金损失,及面临各种安全风险;

*

* orderInfo 的获取必须来自服务端;

*/

boolean rsa2 = (RSA2_PRIVATE.length() > 0);

Map params = OrderInfoUtil2_0.buildOrderParamMap(APPID, rsa2,price);

String orderParam = OrderInfoUtil2_0.buildOrderParam(params);

String privateKey = rsa2 ? RSA2_PRIVATE : RSA_PRIVATE;

String sign = OrderInfoUtil2_0.getSign(params, privateKey, rsa2);

final String orderInfo = orderParam + "&" + sign;

final Runnable payRunnable = new Runnable() {

@Override

public void run() {

PayTask alipay = new PayTask(PayDemoActivity.this);

Map result = alipay.payV2(orderInfo, true);

Log.i("msp", result.toString());

Message msg = new Message();

msg.what = SDK_PAY_FLAG;

msg.obj = result;

mHandler.sendMessage(msg);

}

};

// 必须异步调用

Thread payThread = new Thread(payRunnable);

payThread.start();

}

private static void showAlert(Context ctx, String info) {

showAlert(ctx, info, null);

}

private static void showAlert(Context ctx, String info, DialogInterface.OnDismissListener onDismiss) {

new AlertDialog.Builder(ctx)

.setMessage(info)

.setPositiveButton(R.string.confirm, null)

.setOnDismissListener(onDismiss)

.show();

}

private static void showToast(Context ctx, String msg) {

Toast.makeText(ctx, msg, Toast.LENGTH_LONG).show();

}

private static String bundleToString(Bundle bundle) {

if (bundle == null) {

return "null";

}

final StringBuilder sb = new StringBuilder();

for (String key: bundle.keySet()) {

sb.append(key).append("=>").append(bundle.get(key)).append("\n");

}

return sb.toString();

}

}

OrderInfoUtil2_0.java

public class OrderInfoUtil2_0 {

/**

* 构造授权参数列表

*

* @param pid

* @param app_id

* @param target_id

* @return

*/

public static Map buildAuthInfoMap(String pid, String app_id, String target_id, boolean rsa2) {

Map keyValues = new HashMap();

// 商户签约拿到的app_id,如:2013081700024223

keyValues.put("app_id", app_id);

// 商户签约拿到的pid,如:2088102123816631

keyValues.put("pid", pid);

// 服务接口名称, 固定值

keyValues.put("apiname", "com.alipay.account.auth");

// 服务接口名称, 固定值

keyValues.put("methodname", "alipay.open.auth.sdk.code.get");

// 商户类型标识, 固定值

keyValues.put("app_name", "mc");

// 业务类型, 固定值

keyValues.put("biz_type", "openservice");

// 产品码, 固定值

keyValues.put("product_id", "APP_FAST_LOGIN");

// 授权范围, 固定值

keyValues.put("scope", "kuaijie");

// 商户唯一标识,如:kkkkk091125

keyValues.put("target_id", target_id);

// 授权类型, 固定值

keyValues.put("auth_type", "AUTHACCOUNT");

// 签名类型

keyValues.put("sign_type", rsa2 ? "RSA2" : "RSA");

return keyValues;

}

/**

* 构造支付订单参数列表

*/

public static Map buildOrderParamMap(String app_id, boolean rsa2,int price) {

Map keyValues = new HashMap();

keyValues.put("app_id", app_id);

keyValues.put("biz_content", "{\"timeout_express\":\"30m\",\"product_code\":\"QUICK_MSECURITY_PAY\",\"total_amount\":\""+price+"\",\"subject\":\"1\",\"body\":\"我是测试数据\",\"out_trade_no\":\"" + getOutTradeNo() + "\"}");

keyValues.put("charset", "utf-8");

keyValues.put("method", "alipay.trade.app.pay");

keyValues.put("sign_type", rsa2 ? "RSA2" : "RSA");

keyValues.put("timestamp", "2016-07-29 16:55:53");

keyValues.put("version", "1.0");

return keyValues;

}

/**

* 构造支付订单参数信息

*

* @param map

* 支付订单参数

* @return

*/

public static String buildOrderParam(Map map) {

List keys = new ArrayList(map.keySet());

StringBuilder sb = 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("&");

}

String tailKey = keys.get(keys.size() - 1);

String tailValue = map.get(tailKey);

sb.append(buildKeyValue(tailKey, tailValue, true));

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();

}

/**

* 对支付参数信息进行签名

*

* @param map

* 待签名授权信息

*

* @return

*/

public static String getSign(Map map, String rsaKey, boolean rsa2) {

List keys = new ArrayList(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(), rsaKey, rsa2);

String encodedSign = "";

try {

encodedSign = URLEncoder.encode(oriSign, "UTF-8");

} catch (UnsupportedEncodingException e) {

e.printStackTrace();

}

return "sign=" + encodedSign;

}

/**

* 要求外部订单号必须唯一。

* @return

*/

private static 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;

}

}

工具类

1.SignUtils.java

public class SignUtils {

private static final String ALGORITHM = "RSA";

private static final String SIGN_ALGORITHMS = "SHA1WithRSA";

private static final String SIGN_SHA256RSA_ALGORITHMS = "SHA256WithRSA";

private static final String DEFAULT_CHARSET = "UTF-8";

private static String getAlgorithms(boolean rsa2) {

return rsa2 ? SIGN_SHA256RSA_ALGORITHMS : SIGN_ALGORITHMS;

}

public static String sign(String content, String privateKey, boolean rsa2) {

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(getAlgorithms(rsa2));

signature.initSign(priKey);

signature.update(content.getBytes(DEFAULT_CHARSET));

byte[] signed = signature.sign();

return Base64.encode(signed);

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

}

2.PayResult.java

public class PayResult {

private String resultStatus;

private String result;

private String memo;

public PayResult(Map rawResult) {

if (rawResult == null) {

return;

}

for (String key : rawResult.keySet()) {

if (TextUtils.equals(key, "resultStatus")) {

resultStatus = rawResult.get(key);

} else if (TextUtils.equals(key, "result")) {

result = rawResult.get(key);

} else if (TextUtils.equals(key, "memo")) {

memo = rawResult.get(key);

}

}

}

@Override

public String toString() {

return "resultStatus={" + resultStatus + "};memo={" + memo

+ "};result={" + result + "}";

}

/**

* @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.ExternalFragment.java

public class ExternalFragment extends Fragment {

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {

return inflater.inflate(R.layout.pay_external, container, false);

}

}

4.在 Android_Demo中移过来这三个

五.布局文件

pay_external.xml

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent" >

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:orientation="vertical">

android:id="@+id/payV2"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_margin="10dp"

android:onClick="payV2"

android:textAllCaps="false"

android:text="@string/pay_with_alipay" />

pay_main.xml

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:orientation="vertical"

android:gravity="center_horizontal"

android:background="#FFFFFF">

android:layout_marginTop="16dp"

android:layout_marginBottom="16dp"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:scaleType="centerInside"

android:src="@drawable/alipay_logo" />

android:id="@+id/fragment"

android:name="com.alipay.sdk.pay.demo.ExternalFragment"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

tools:layout="@layout/pay_external" />

本方法调用的返回结果,参数说明见"客户端同步返回参数说明"。

至此,支付宝支付代码已完成!