当前位置:   article > 正文

Android App接入支付功能——支付宝支付_android 支付宝支付

android 支付宝支付

接入前准备

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

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

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

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

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

  1. // 支付宝 SDK AAR 包所需的配置
  2. compile (name: 'alipaysdk-15.8.03.210428205839', ext: 'aar')
  3. //这里alipaysdk-15.8.03.210428205839必须和导入的sdk名字一样

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

  1. <activity
  2. android:name="com.alipay.sdk.pay.demo.PayDemoActivity"
  3. android:label="@string/app_name" >
  4. <intent-filter>
  5. <action android:name="android.intent.action.MAIN" />
  6. <category android:name="android.intent.category.LAUNCHER" />
  7. </intent-filter>
  8. </activity>
  9. <activity
  10. android:name="com.alipay.sdk.pay.demo.H5PayDemoActivity"
  11. android:configChanges="orientation|keyboardHidden|navigation"
  12. android:exported="false"
  13. android:screenOrientation="behind" >
  14. </activity>

 ②添加权限声明:

  1. <uses-permission android:name="android.permission.INTERNET" />
  2. <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  3. <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
  4. <uses-permission android:name="android.permission.READ_PHONE_STATE" />
  5. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  1. <!-- 如果您的 App 的 targetSdkVersion 大于或等于 30,则需要在 AndroidManifest.xml 中提供下面的应用可见性声明,
  2. 让支付宝 SDK 感知设备上是否已经安装了支付宝 App。同时,您可能还需要升级 Gradle Plugin 到最新版本。
  3. 关于 Android 11 的 "应用可见性" 机制,参见 https://developer.android.com/about/versions/11/privacy/package-visibility?hl=zh-cn -->
  4. <queries>
  5. <package android:name="com.eg.android.AlipayGphone" /> <!-- 支付宝 -->
  6. <package android:name="hk.alipay.wallet" /> <!-- AlipayHK -->
  7. </queries>

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

  1. -keep class com.alipay.android.app.IAlixPay{*;}
  2. -keep class com.alipay.android.app.IAlixPay$Stub{*;}
  3. -keep class com.alipay.android.app.IRemoteServiceCallback{*;}
  4. -keep class com.alipay.android.app.IRemoteServiceCallback$Stub{*;}
  5. -keep class com.alipay.sdk.app.PayTask{ public *;}
  6. -keep class com.alipay.sdk.app.AuthTask{ public *;}
  7. -keep class com.alipay.sdk.app.H5PayCallback {
  8. <fields>;
  9. <methods>;
  10. }
  11. -keep class com.alipay.android.phone.mrpc.core.** { *; }
  12. -keep class com.alipay.apmobilesecuritysdk.** { *; }
  13. -keep class com.alipay.mobile.framework.service.annotation.** { *; }
  14. -keep class com.alipay.mobilesecuritysdk.face.** { *; }
  15. -keep class com.alipay.tscenter.biz.rpc.** { *; }
  16. -keep class org.json.alipay.** { *; }
  17. -keep class com.alipay.tscenter.** { *; }
  18. -keep class com.ta.utdid2.** { *;}
  19. -keep class com.ut.device.** { *;}

三.支付接口调用

     PayDemoActivity.java

  1. public class PayDemoActivity extends AppCompatActivity {
  2. public static int price=100;
  3. /**
  4. * 用于支付宝支付业务的入参 app_id。
  5. */
  6. public static final String APPID = "************";
  7. /**
  8. * 用于支付宝账户登录授权业务的入参 pid。
  9. */
  10. public static final String PID = "************";
  11. /**
  12. * 用于支付宝账户登录授权业务的入参 target_id。商家的收款账号
  13. */
  14. public static final String TARGET_ID = "************";
  15. /**
  16. * pkcs8 格式的商户私钥。
  17. *
  18. * 如下私钥,RSA2_PRIVATE 或者 RSA_PRIVATE 只需要填入一个,如果两个都设置了,本 Demo 将优先
  19. * 使用 RSA2_PRIVATE。RSA2_PRIVATE 可以保证商户交易在更加安全的环境下进行,建议商户使用
  20. * RSA2_PRIVATE。
  21. *
  22. * 建议使用支付宝提供的公私钥生成工具生成和获取 RSA2_PRIVATE。
  23. * 工具地址:https://doc.open.alipay.com/docs/doc.htm?treeId=291&articleId=106097&docType=1
  24. */
  25. public static final String RSA2_PRIVATE = "************";//密钥
  26. public static final String RSA_PRIVATE = "";
  27. private static final int SDK_PAY_FLAG = 1;
  28. private static final int SDK_AUTH_FLAG = 2;
  29. @SuppressLint("HandlerLeak")
  30. private Handler mHandler = new Handler() {
  31. @SuppressWarnings("unused")
  32. public void handleMessage(Message msg) {
  33. switch (msg.what) {
  34. case SDK_PAY_FLAG: {
  35. @SuppressWarnings("unchecked")
  36. PayResult payResult = new PayResult((Map<String, String>) msg.obj);
  37. /**
  38. * 对于支付结果,请商户依赖服务端的异步通知结果。同步通知结果,仅作为支付结束的通知。
  39. */
  40. String resultInfo = payResult.getResult();// 同步返回需要验证的信息
  41. String resultStatus = payResult.getResultStatus();
  42. // 判断resultStatus 为9000则代表支付成功
  43. if (TextUtils.equals(resultStatus, "9000")) {
  44. // 该笔订单是否真实支付成功,需要依赖服务端的异步通知。
  45. showAlert(PayDemoActivity.this, getString(R.string.pay_success) + payResult);
  46. } else {
  47. // 该笔订单真实的支付结果,需要依赖服务端的异步通知。
  48. showAlert(PayDemoActivity.this, getString(R.string.pay_failed) + payResult);
  49. }
  50. break;
  51. }
  52. }
  53. };
  54. };
  55. @Override
  56. protected void onCreate(Bundle savedInstanceState) {
  57. // EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX);//沙箱环境需要的代码
  58. super.onCreate(savedInstanceState);
  59. setContentView(R.layout.pay_main);
  60. Button button = findViewById(R.id.payV2);
  61. button.setOnClickListener(new View.OnClickListener() {
  62. @Override
  63. public void onClick(View v) {
  64. payV2(v);
  65. }
  66. });
  67. }
  68. /**
  69. * 支付宝支付业务示例
  70. */
  71. public void payV2(View v) {
  72. if (TextUtils.isEmpty(APPID) || (TextUtils.isEmpty(RSA2_PRIVATE) && TextUtils.isEmpty(RSA_PRIVATE))) {
  73. showAlert(this, getString(R.string.error_missing_appid_rsa_private));
  74. return;
  75. }
  76. /*
  77. * 这里只是为了方便直接向商户展示支付宝的整个支付流程;所以Demo中加签过程直接放在客户端完成;
  78. * 真实App里,privateKey等数据严禁放在客户端,加签过程务必要放在服务端完成;
  79. * 防止商户私密数据泄露,造成不必要的资金损失,及面临各种安全风险;
  80. *
  81. * orderInfo 的获取必须来自服务端;
  82. */
  83. boolean rsa2 = (RSA2_PRIVATE.length() > 0);
  84. Map<String, String> params = OrderInfoUtil2_0.buildOrderParamMap(APPID, rsa2,price);
  85. String orderParam = OrderInfoUtil2_0.buildOrderParam(params);
  86. String privateKey = rsa2 ? RSA2_PRIVATE : RSA_PRIVATE;
  87. String sign = OrderInfoUtil2_0.getSign(params, privateKey, rsa2);
  88. final String orderInfo = orderParam + "&" + sign;
  89. final Runnable payRunnable = new Runnable() {
  90. @Override
  91. public void run() {
  92. PayTask alipay = new PayTask(PayDemoActivity.this);
  93. Map<String, String> result = alipay.payV2(orderInfo, true);
  94. Log.i("msp", result.toString());
  95. Message msg = new Message();
  96. msg.what = SDK_PAY_FLAG;
  97. msg.obj = result;
  98. mHandler.sendMessage(msg);
  99. }
  100. };
  101. // 必须异步调用
  102. Thread payThread = new Thread(payRunnable);
  103. payThread.start();
  104. }
  105. private static void showAlert(Context ctx, String info) {
  106. showAlert(ctx, info, null);
  107. }
  108. private static void showAlert(Context ctx, String info, DialogInterface.OnDismissListener onDismiss) {
  109. new AlertDialog.Builder(ctx)
  110. .setMessage(info)
  111. .setPositiveButton(R.string.confirm, null)
  112. .setOnDismissListener(onDismiss)
  113. .show();
  114. }
  115. private static void showToast(Context ctx, String msg) {
  116. Toast.makeText(ctx, msg, Toast.LENGTH_LONG).show();
  117. }
  118. private static String bundleToString(Bundle bundle) {
  119. if (bundle == null) {
  120. return "null";
  121. }
  122. final StringBuilder sb = new StringBuilder();
  123. for (String key: bundle.keySet()) {
  124. sb.append(key).append("=>").append(bundle.get(key)).append("\n");
  125. }
  126. return sb.toString();
  127. }
  128. }

OrderInfoUtil2_0.java

  1. public class OrderInfoUtil2_0 {
  2. /**
  3. * 构造授权参数列表
  4. *
  5. * @param pid
  6. * @param app_id
  7. * @param target_id
  8. * @return
  9. */
  10. public static Map<String, String> buildAuthInfoMap(String pid, String app_id, String target_id, boolean rsa2) {
  11. Map<String, String> keyValues = new HashMap<String, String>();
  12. // 商户签约拿到的app_id,如:2013081700024223
  13. keyValues.put("app_id", app_id);
  14. // 商户签约拿到的pid,如:2088102123816631
  15. keyValues.put("pid", pid);
  16. // 服务接口名称, 固定值
  17. keyValues.put("apiname", "com.alipay.account.auth");
  18. // 服务接口名称, 固定值
  19. keyValues.put("methodname", "alipay.open.auth.sdk.code.get");
  20. // 商户类型标识, 固定值
  21. keyValues.put("app_name", "mc");
  22. // 业务类型, 固定值
  23. keyValues.put("biz_type", "openservice");
  24. // 产品码, 固定值
  25. keyValues.put("product_id", "APP_FAST_LOGIN");
  26. // 授权范围, 固定值
  27. keyValues.put("scope", "kuaijie");
  28. // 商户唯一标识,如:kkkkk091125
  29. keyValues.put("target_id", target_id);
  30. // 授权类型, 固定值
  31. keyValues.put("auth_type", "AUTHACCOUNT");
  32. // 签名类型
  33. keyValues.put("sign_type", rsa2 ? "RSA2" : "RSA");
  34. return keyValues;
  35. }
  36. /**
  37. * 构造支付订单参数列表
  38. */
  39. public static Map<String, String> buildOrderParamMap(String app_id, boolean rsa2,int price) {
  40. Map<String, String> keyValues = new HashMap<String, String>();
  41. keyValues.put("app_id", app_id);
  42. keyValues.put("biz_content", "{\"timeout_express\":\"30m\",\"product_code\":\"QUICK_MSECURITY_PAY\",\"total_amount\":\""+price+"\",\"subject\":\"1\",\"body\":\"我是测试数据\",\"out_trade_no\":\"" + getOutTradeNo() + "\"}");
  43. keyValues.put("charset", "utf-8");
  44. keyValues.put("method", "alipay.trade.app.pay");
  45. keyValues.put("sign_type", rsa2 ? "RSA2" : "RSA");
  46. keyValues.put("timestamp", "2016-07-29 16:55:53");
  47. keyValues.put("version", "1.0");
  48. return keyValues;
  49. }
  50. /**
  51. * 构造支付订单参数信息
  52. *
  53. * @param map
  54. * 支付订单参数
  55. * @return
  56. */
  57. public static String buildOrderParam(Map<String, String> map) {
  58. List<String> keys = new ArrayList<String>(map.keySet());
  59. StringBuilder sb = new StringBuilder();
  60. for (int i = 0; i < keys.size() - 1; i++) {
  61. String key = keys.get(i);
  62. String value = map.get(key);
  63. sb.append(buildKeyValue(key, value, true));
  64. sb.append("&");
  65. }
  66. String tailKey = keys.get(keys.size() - 1);
  67. String tailValue = map.get(tailKey);
  68. sb.append(buildKeyValue(tailKey, tailValue, true));
  69. return sb.toString();
  70. }
  71. /**
  72. * 拼接键值对
  73. *
  74. * @param key
  75. * @param value
  76. * @param isEncode
  77. * @return
  78. */
  79. private static String buildKeyValue(String key, String value, boolean isEncode) {
  80. StringBuilder sb = new StringBuilder();
  81. sb.append(key);
  82. sb.append("=");
  83. if (isEncode) {
  84. try {
  85. sb.append(URLEncoder.encode(value, "UTF-8"));
  86. } catch (UnsupportedEncodingException e) {
  87. sb.append(value);
  88. }
  89. } else {
  90. sb.append(value);
  91. }
  92. return sb.toString();
  93. }
  94. /**
  95. * 对支付参数信息进行签名
  96. *
  97. * @param map
  98. * 待签名授权信息
  99. *
  100. * @return
  101. */
  102. public static String getSign(Map<String, String> map, String rsaKey, boolean rsa2) {
  103. List<String> keys = new ArrayList<String>(map.keySet());
  104. // key排序
  105. Collections.sort(keys);
  106. StringBuilder authInfo = new StringBuilder();
  107. for (int i = 0; i < keys.size() - 1; i++) {
  108. String key = keys.get(i);
  109. String value = map.get(key);
  110. authInfo.append(buildKeyValue(key, value, false));
  111. authInfo.append("&");
  112. }
  113. String tailKey = keys.get(keys.size() - 1);
  114. String tailValue = map.get(tailKey);
  115. authInfo.append(buildKeyValue(tailKey, tailValue, false));
  116. String oriSign = SignUtils.sign(authInfo.toString(), rsaKey, rsa2);
  117. String encodedSign = "";
  118. try {
  119. encodedSign = URLEncoder.encode(oriSign, "UTF-8");
  120. } catch (UnsupportedEncodingException e) {
  121. e.printStackTrace();
  122. }
  123. return "sign=" + encodedSign;
  124. }
  125. /**
  126. * 要求外部订单号必须唯一。
  127. * @return
  128. */
  129. private static String getOutTradeNo() {
  130. SimpleDateFormat format = new SimpleDateFormat("MMddHHmmss", Locale.getDefault());
  131. Date date = new Date();
  132. String key = format.format(date);
  133. Random r = new Random();
  134. key = key + r.nextInt();
  135. key = key.substring(0, 15);
  136. return key;
  137. }
  138. }

 工具类

     1.SignUtils.java

  1. public class SignUtils {
  2. private static final String ALGORITHM = "RSA";
  3. private static final String SIGN_ALGORITHMS = "SHA1WithRSA";
  4. private static final String SIGN_SHA256RSA_ALGORITHMS = "SHA256WithRSA";
  5. private static final String DEFAULT_CHARSET = "UTF-8";
  6. private static String getAlgorithms(boolean rsa2) {
  7. return rsa2 ? SIGN_SHA256RSA_ALGORITHMS : SIGN_ALGORITHMS;
  8. }
  9. public static String sign(String content, String privateKey, boolean rsa2) {
  10. try {
  11. PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(
  12. Base64.decode(privateKey));
  13. KeyFactory keyf = KeyFactory.getInstance(ALGORITHM);
  14. PrivateKey priKey = keyf.generatePrivate(priPKCS8);
  15. java.security.Signature signature = java.security.Signature
  16. .getInstance(getAlgorithms(rsa2));
  17. signature.initSign(priKey);
  18. signature.update(content.getBytes(DEFAULT_CHARSET));
  19. byte[] signed = signature.sign();
  20. return Base64.encode(signed);
  21. } catch (Exception e) {
  22. e.printStackTrace();
  23. }
  24. return null;
  25. }
  26. }

 2.PayResult.java

  1. public class PayResult {
  2. private String resultStatus;
  3. private String result;
  4. private String memo;
  5. public PayResult(Map<String, String> rawResult) {
  6. if (rawResult == null) {
  7. return;
  8. }
  9. for (String key : rawResult.keySet()) {
  10. if (TextUtils.equals(key, "resultStatus")) {
  11. resultStatus = rawResult.get(key);
  12. } else if (TextUtils.equals(key, "result")) {
  13. result = rawResult.get(key);
  14. } else if (TextUtils.equals(key, "memo")) {
  15. memo = rawResult.get(key);
  16. }
  17. }
  18. }
  19. @Override
  20. public String toString() {
  21. return "resultStatus={" + resultStatus + "};memo={" + memo
  22. + "};result={" + result + "}";
  23. }
  24. /**
  25. * @return the resultStatus
  26. */
  27. public String getResultStatus() {
  28. return resultStatus;
  29. }
  30. /**
  31. * @return the memo
  32. */
  33. public String getMemo() {
  34. return memo;
  35. }
  36. /**
  37. * @return the result
  38. */
  39. public String getResult() {
  40. return result;
  41. }
  42. }

 3.ExternalFragment.java

  1. public class ExternalFragment extends Fragment {
  2. @Override
  3. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  4. Bundle savedInstanceState) {
  5. return inflater.inflate(R.layout.pay_external, container, false);
  6. }
  7. }

4.在 Android_Demo中移过来这三个

五.布局文件

    pay_external.xml

  1. <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent" >
  5. <LinearLayout
  6. android:layout_width="match_parent"
  7. android:layout_height="wrap_content"
  8. android:orientation="vertical">
  9. <Button
  10. android:id="@+id/payV2"
  11. android:layout_width="match_parent"
  12. android:layout_height="wrap_content"
  13. android:layout_margin="10dp"
  14. android:onClick="payV2"
  15. android:textAllCaps="false"
  16. android:text="@string/pay_with_alipay" />
  17. </LinearLayout>
  18. </ScrollView>

     pay_main.xml

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="fill_parent"
  4. android:layout_height="fill_parent"
  5. android:orientation="vertical"
  6. android:gravity="center_horizontal"
  7. android:background="#FFFFFF">
  8. <ImageView
  9. android:layout_marginTop="16dp"
  10. android:layout_marginBottom="16dp"
  11. android:layout_width="wrap_content"
  12. android:layout_height="wrap_content"
  13. android:scaleType="centerInside"
  14. android:src="@drawable/alipay_logo" />
  15. <fragment
  16. android:id="@+id/fragment"
  17. android:name="com.alipay.sdk.pay.demo.ExternalFragment"
  18. android:layout_width="fill_parent"
  19. android:layout_height="wrap_content"
  20. tools:layout="@layout/pay_external" />
  21. </LinearLayout>

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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/代码探险家/article/detail/989135
推荐阅读
相关标签
  

闽ICP备14008679号