HybridAPP接入支付宝APP支付(Android端)

前段时间项目需要接入支付宝支付,这份任务被分配到我来完成,折腾了好几天(主要是因为我菜),现在有时间来记录一下整个过程。
整个工作分为以下几步。
在这里插入图片描述

一. 登录并创建应用
首先到支付宝开放平台登录要接通支付的支付宝账号,登录之后看到如下页面
在这里插入图片描述
点击"网页&移动应用"进行应用创建,创建完成之后到我的应用查看你新创建的应用,会看到应用的唯一编号(APPID)

二. 配置你的应用

1. 为你的应用添加功能
添加功能
可以根据你的接入需求选择你要添加的功能,我这里添加的是APP支付(这里面的大部分功能都是需要签约的),这里是签约说明

2. 配置密钥

为了保证交易双方(商户和支付宝)的身份和数据安全,开发者在调用接口前,需要配置双方密钥,对交易数据进行双方校验。RSA
密钥包含应用私钥(APP_PRIVATE_KEY)、应用公钥(APP_PUBLIC_KEY)。生成密钥后,开发者需要在开放平台开发者中心进行密钥配置,配置完成后可以获取支付宝公钥(ALIPAY_PUBLIC_KEY)

支付宝开发平台提供了密钥的生成工具,自行下载使用
在这里插入图片描述
点击"生成密钥"即可.
然后进入自己新建的应用进行配置
在这里插入图片描述
如下图一样填入你的应用公钥
填入你的应用公钥
配置完成后会获得"支付宝公钥"(ALIPAY_PUBLIC_KEY)

三. 集成与开发

接入移动支付需要集成两个 SDK:客户端 SDK 需要集成在商户自己的 APP 中,用于唤起支付宝 APP
并发送交易数据,并在支付宝APP返回商户APP时获得支付结果;服务端SDK需要商户集成在自己的服务端系统中,用于协助解析并验证客户端同步返回的支付结果和异步通知。

官方文档集成和开发

客户端(Android)导入SDK

四. 调用接口

为了避免在线上生产环境联调过程中遇到问题,建议在沙箱环境中联调通过后再在线上生产环境进行联调,具体操作步骤见沙箱联调指南。如果需要在线上调用接口,需要参考下面步骤:应用上线后再进行接口调用,不然会出现“无权限错误”的报错信息。

系统交互流程如下图所示:
在这里插入图片描述
以 Android 平台为例:
图中虚线标识商户链路,实线标识支付宝链路。

第4步调用支付接口:此消息就是本接口所描述的支付宝客户端SDK提供的支付对象 PayTask,将商户签名后的订单信息传进 payv2
方法唤起支付宝收银台,交易数据格式具体参见请求参数说明。

第5步支付请求:支付宝客户端 SDK 将会按照商户客户端提供的请求参数发送支付请求。

第8步接口返回支付结果:商户客户端在第4步中调用的支付接口,会返回最终的支付结果(即同步通知),参见客户端同步返回。

第13步用户在支付宝 APP 或 H5 收银台完成支付后,会根据商户在手机网站支付 API 中传入的前台回跳地址 return_url
自动跳转回商户页面,同时在 URL 请求中附带上支付结果参数。同时,支付宝还会根据原始支付 API 中传入的异步通知地址
notify_url,通过 POST 请求的形式将支付结果作为参数通知到商户系统,详情见支付结果异步通知。

除了正向支付流程外,支付宝也提供交易查询、关闭、退款、退款查询以及对账等配套 API。

特别注意:

构造交易数据并签名必须在商户服务端完成,商户的应用私钥绝对不能保存在商户 APP 客户端中,也不能从服务端下发。

同步返回的数据,只是一个简单的结果通知,商户确定该笔交易付款是否成功需要依赖服务端收到支付宝异步通知的结果进行判断。

商户系统接收到通知以后,必须通过验签(验证通知中的 sign 参数)来确保支付通知是由支付宝发送的。建议使用支付宝提供的 SDK
来完成,详细验签规则参考异步通知验签。

使用SDK快速接入

App支付 API 必须通过支付宝提供的移动端 SDK 来调用。

交易操作
外部商户APP唤起快捷SDK创建订单并支付,具体的接口参数和示例代码可详见alipay.trade.app.pay(app支付接口2.0)

以下大致记录针对alipay.trade.app.pay接口的代码:

服务端
1.APP客户端点击"支付"调用应用服务端,应用服务端通过alipay.trade.app.pay接口传入订单参数

//实例化客户端
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APPID, AlipayConfig.RSA_PRIVATE_KEY, "json", AlipayConfig.CHARSET, AlipayConfig.ALIPAY_PUBLIC_KEY, "RSA2");
//实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
//SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setBody(AlipayConfig.Body); //订单描述
model.setSubject(AlipayConfig.Subject); //订单标题

model.setOutTradeNo(outtradeno); //商家订单号
model.setTimeoutExpress("30m"); 
model.setTotalAmount(r.get("count")); //订单金额
model.setProductCode("QUICK_MSECURITY_PAY"); 
request.setBizModel(model);
request.setNotifyUrl(AlipayConfig.notify_url); //异步通知的路径(不能带参数,支付由支付宝调用,直接跳转[本地调试需要内网穿透])

try {
        //这里和普通的接口调用不同,使用的是sdkExecute(H5wap支付的是pageExecute)
        AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
        System.out.println(response.getBody());//就是orderString 可以直接给客户端请求,无需再做处理。(可以通过"js与app的交互"直接把这个长字符串传到客户端)
        r.getXml().setScript("perform('"+response.getBody()+"')");
    } catch (AlipayApiException e) {
        e.printStackTrace();
}

2.支付宝客户端支付后,支付宝服务端会发起异步通知到应用服务端,服务端进行解签校验,确认支付状态。

<%@page import="com.alipay.api.internal.util.AlipaySignature"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.*"%>
<%@ page import="java.util.Map"%>
<%@ page import="com.alipay.config.*"%>
<%@ page import="com.alipay.api.*"%>
<%
	//获取支付宝POST过来反馈信息
	Map<String,String> params = new HashMap<String,String>();
	Map requestParams = request.getParameterMap();
	for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
		String name = (String) iter.next();
		String[] values = (String[]) requestParams.get(name);
		String valueStr = "";
		for (int i = 0; i < values.length; i++) {
			valueStr = (i == values.length - 1) ? valueStr + values[i]
					: valueStr + values[i] + ",";
		}
		//乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
		//valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
		params.put(name, valueStr);
	}
	//获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以下仅供参考)//
		//商户订单号

		String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8");
		//支付宝交易号

		String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8");

		//交易状态
		String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"),"UTF-8");

		//获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以上仅供参考)//
		//计算得出通知验证结果
		//boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String sign_type)
		boolean verify_result = AlipaySignature.rsaCheckV1(params, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.CHARSET, "RSA2");
		
		if(verify_result){//验证成功
			//
			//请在这里加上商户的业务逻辑程序代码

			//——请根据您的业务逻辑来编写程序(以下代码仅作参考)——
			
			if(trade_status.equals("TRADE_FINISHED")){
				//判断该笔订单是否在商户网站中已经做过处理
					//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
					//请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
					//如果有做过处理,不执行商户的业务程序
					
				//注意:
				//如果签约的是可退款协议,退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知
				//如果没有签约可退款协议,那么付款完成后,支付宝系统发送该交易状态通知。
			} else if (trade_status.equals("TRADE_SUCCESS")){
				//判断该笔订单是否在商户网站中已经做过处理
					//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
					//请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
					//如果有做过处理,不执行商户的业务程序
					
				//注意:
				//如果签约的是可退款协议,那么付款完成后,支付宝系统发送该交易状态通知。
			}

			//——请根据您的业务逻辑来编写程序(以上代码仅作参考)——
			out.clear();
			out.println("success");	//请不要修改或删除

			//
		}else{//验证失败
			out.println("fail");
		}
%>

客户端
Android应用接收到服务端传过来的订单参数(orderStr),用它来调起支付宝收银台


import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import android.view.Gravity;
import android.widget.Toast;

import com.alipay.sdk.app.PayTask;

import net.escjy.gwl.app.common.BasicActivity;
import net.escjy.gwl.app.common.TaskInterface;

import java.util.Map;

/**
 * Created by : vince
 * Created at : 2019/10/11
 * Desc :
 */
public class PayActivity extends BasicActivity implements TaskInterface {

    MessageJs2App mj2app;
    private static final int SDK_PAY_FLAG = 1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        //沙箱
        //EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.pay_main);
        //得到MessageJs2App传过来的订单参数
        Intent intent = getIntent();
        final String orderInfo = intent.getStringExtra("orderStr"); //服务端传过来的订单参数
        //支付
        final Runnable payRunnable = new Runnable() {
            @Override
            public void run() {
                PayTask alipay = new PayTask(PayActivity.this);
                Map<String, String> 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();
    }

    @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<String, String>) msg.obj);
                    /**
                     * 对于支付结果,请商户依赖服务端的异步通知结果。同步通知结果,仅作为支付结束的通知。
                     */
                    String resultInfo = payResult.getResult();// 同步返回需要验证的信息
                    String resultStatus = payResult.getResultStatus();
                    // 判断resultStatus 为9000则代表支付成功
                    if (TextUtils.equals(resultStatus, "9000")) {
                        Toast toast = Toast.makeText(PayActivity.this, "支付成功,正在跳转",
                                Toast.LENGTH_SHORT);
                        toast.setGravity(Gravity.CENTER, 0, 0);
                        toast.show();

                    } else if (TextUtils.equals(resultStatus, "8000")){
                        Toast toast = Toast.makeText(PayActivity.this, "正在处理……",
                                Toast.LENGTH_SHORT);
                        toast.setGravity(Gravity.CENTER, 0, 0);
                        toast.show();
                    }else if (TextUtils.equals(resultStatus, "4000")){
                        Toast toast = Toast.makeText(PayActivity.this, "支付失败",
                                Toast.LENGTH_SHORT);
                        toast.setGravity(Gravity.CENTER, 0, 0);
                        toast.show();
                    }else if (TextUtils.equals(resultStatus, "5000")){
                        Toast toast = Toast.makeText(PayActivity.this, "请勿重复支付",
                                Toast.LENGTH_SHORT);
                        toast.setGravity(Gravity.CENTER, 0, 0);
                        toast.show();
                    }else if (TextUtils.equals(resultStatus, "6001")){
                        Toast toast = Toast.makeText(PayActivity.this, "支付已取消",
                                Toast.LENGTH_SHORT);
                        toast.setGravity(Gravity.CENTER, 0, 0);
                        toast.show();
                    }else if (TextUtils.equals(resultStatus, "6002")){
                        Toast toast = Toast.makeText(PayActivity.this, "网络连接出错,请检查网络",
                                Toast.LENGTH_SHORT);
                        toast.setGravity(Gravity.CENTER, 0, 0);
                        toast.show();
                    }
                    /**
                    * 将支付宝响应的同步通知的部分数据传回服务端并结束当前Activity的生命周期
                    */
                    Intent data = new Intent();
                    if (!TextUtils.isEmpty(resultInfo)){
                        data.putExtra("resultInfo",resultInfo);
//                      data.setAction(resultInfo);
                        setResult(Activity.RESULT_OK, data);
                    }
                    finish();
                    break;

                }

                default:
                    break;
            }
        };
    };

如果按照正常的情况,支付完了之后页面会跳转到先前传入的异步通知的地址,然后再加入自己的业务逻辑,但是我这里死活接收不到,而且就算接收到了按照项目原来的框架也不大好处理,所以我这里用的是客户端传回来的支付宝交易号进行统一收单线下交易查询来进行验证.

下面就是相关的代码:

AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL,AlipayConfig.APPID,AlipayConfig.RSA_PRIVATE_KEY,"json",AlipayConfig.CHARSET,AlipayConfig.ALIPAY_PUBLIC_KEY,"RSA2");
				//alipay.trade.query
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
//				AlipayTradeQueryModel model = new AlipayTradeQueryModel();
//				model.setOutTradeNo(outtradeno);
String tradeNo = r.get("trade_no"); //客户端传回来的支付宝交易号
StringBuilder bizContent = new StringBuilder("{\"trade_no\":\"").append(tradeNo).append("\"}");
request.setBizContent(bizContent.toString());//填充业务参数

log.debug("交易号"+tradeNo);
AlipayTradeQueryResponse response = alipayClient.execute(request);
log.debug("查询响应的相应参数:"+response.getBody());
if(response.isSuccess()){
	String tardeStatus = response.getTradeStatus(); //交易状态
	String out_trade_no = response.getOutTradeNo();//商户订单号
	String trade_no = response.getTradeNo(); //支付宝交易号
	String total_amount = response.getTotalAmount(); //订单金额
	//判断支付宝付款是否成功
	if (tardeStatus.equals("TRADE_SUCCESS")){
		/**
		 * 添加你的业务逻辑
		 */
				
	}
	log.debug("调用成功");
} else {
	log.debug("调用失败");
}

五.调试应用

支付能力直接涉及到交易与资金,为了方便开放者调试支付能力,我们已经准备好沙箱环境,包括沙箱环境账号和沙箱版支付宝钱包,这样就可以在沙箱环境调试了。具体操作步骤见沙箱联调指南

目前 Android 的 APP 支付开发支持沙箱环境而 iOS 版的 APP 支付开发暂不支持沙箱环境。

六.上线应用

商户本身应用上线时候,也要把支付宝开放平台的应用上线
应用开发完成后,请开发者自行进行验收和安全性检查(安全性检查可参考《开放平台第三方应用安全开发指南》),验收检查完成后可申请上线。应用申请上线后,会同时申请此列表的功能,接口即生效,这个状态下的应用能够调用生产环境的接口。

如果想在线上环境调试----‘一分钱大法’,必须得先将应用上线,否则在支付时会报"权限不足"

七.总结
1.如果你什么都看不明白,不妨去把Demo下载下来并且跑通,会有不少收获.
2.碰到技术问题要多问,比如说身边的技术大佬和小蚂哥(机器人解决不了就找人工)
3.如果调试出问题,仔细阅读官方提供的排查文档工具

哈哈,最后成了文档的搬运工

  • 0
    点赞
  • 0
    收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:像素格子 设计师:CSDN官方博客 返回首页
评论 3

打赏作者

Vincent_Wilk

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值