app支付宝支付

 首先简单说一下流程: 

       1. android或ios通过java或其他后台服务端语言获取一个签名字符串orderinfo(支付宝提供了jar和demo)

       2.android或ios获取到orderinfo后通过支付宝提供的SDK直接就可以调起支付宝支付界面

       3.通过支付宝支付页面付款完成后,支付宝服务会异步通知到我们自己的服务端,并返回必要参数;这个接口需要我们后台
 

 

 

服务器异步通知页面特性

必须保证服务器异步通知页面(notify_url)上无任何字符,如空格、HTML标签、开发系统自带抛出的异常提示信息等;
支付宝是用POST方式发送通知信息,因此该页面中获取参数的方式,如:request.Form(“out_trade_no”)、$_POST[‘out_trade_no’];
支付宝主动发起通知,该方式才会被启用;
只有在支付宝的交易管理中存在该笔交易,且发生了交易状态的改变,支付宝才会通过该方式发起服务器通知(即时到账交易状态为“等待买家付款”的状态默认是不会发送通知的);
服务器间的交互,不像页面跳转同步通知可以在页面上显示出来,这种交互方式是不可见的;
第一次交易状态改变(即时到账中此时交易状态是交易完成)时,不仅会返回同步处理结果,而且服务器异步通知页面也会收到支付宝发来的处理结果通知;
程序执行完后必须打印输出“success”(不包含引号)。如果商户反馈给支付宝的字符不是success这7个字符,支付宝服务器会不断重发通知,直到超过24小时22分钟。一般情况下,25小时以内完成8次通知(通知的间隔频率一般是:4m,10m,10m,1h,2h,6h,15h);
程序执行完成后,该页面不能执行页面跳转。如果执行页面跳转,支付宝会收不到success字符,会被支付宝服务器判定为该页面程序运行出现异常,而重发处理结果通知;
cookies、session等在此页面会失效,即无法获取这些数据;
该方式的调试与运行必须在服务器上,即互联网上能访问;
该方式的作用主要防止订单丢失,即页面跳转同步通知没有处理订单更新,它则去处理;
当商户收到服务器异步通知并打印出success时,服务器异步通知参数notify_id才会失效。也就是说在支付宝发送同一条异步通知时(包含商户并未成功打印出success导致支付宝重发数次通知),服务器异步通知参数notify_id是不变的。

 回调通知参照:https://blog.csdn.net/thc1987/article/details/80269181

 

首先在pom中配置支付宝封装的jar包

在 https://search.maven.org 中搜索com.alipay.sdk

获得

<dependency>
         <groupId>com.alipay.sdk</groupId>
        <artifactId>alipay-sdk-java</artifactId>
        <version>3.4.49.ALL</version>
   </dependency>

代码及参数官方文档参考地址

https://docs.open.alipay.com/54/106370/   DEMO&SDK
https://docs.open.alipay.com/api_1/alipay.trade.app.pay/   调起支付 获取orderString 的参数说明https://docs.open.alipay.com/204/105301/ 异常通知参数

 

调起支付的方法

package com.qike.webapp.controller;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ExecutorService;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.qike.utils.pay.AlipayNotifyParam;
import com.qike.utils.pay.AppAlipayConfig;
import com.qike.webapp.response.ResponseData;
import com.thinkgem.jeesite.common.utils.IdGen;

@Controller
@RequestMapping("/app/alipay")
public class AppAlipayController {

	// log
	private static Logger logger = LoggerFactory.getLogger(AppAlipayController.class);
	@Autowired
	private ExecutorService executorService;

	/**
	 * 调起支付 获取orderString
	 * @author liuqiyu
	 * @date 2018年12月5日
	 */
	@ResponseBody
	@RequestMapping("payment") 
	public ResponseData payment() {  // 需要的参数再次从前端传入,补充model中的值

		ResponseData responseData = new ResponseData();
		// 实例化客户端
		AlipayClient alipayClient = new DefaultAlipayClient(AppAlipayConfig.GATWAY, AppAlipayConfig.APP_ID,
				AppAlipayConfig.PRIVATE_KEY, AppAlipayConfig.FORMAT, AppAlipayConfig.CHARSET,
				AppAlipayConfig.ALIPAY_PUBLIC_KEY, AppAlipayConfig.SIGN_TYPE);
		// 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
		AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
		// SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
		AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
		model.setBody("我是测试数据");
		model.setSubject("App支付测试Java");
		model.setOutTradeNo(IdGen.uuid());
		model.setTimeoutExpress("30m");
		model.setTotalAmount("0.01");
		model.setProductCode("QUICK_MSECURITY_PAY");
		request.setBizModel(model);
		request.setNotifyUrl("商户外网可以访问的异步地址");
		try {
			// 这里和普通的接口调用不同,使用的是sdkExecute
			AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
			// response.getBody()就是orderString 可以直接给客户端请求,无需再做处理。
			String responseBody = response.getBody();
			responseData.setCode(200);
			responseData.setMsg("预支付会话创建成功");
			responseData.setData(responseBody);
			return responseData;
		} catch (Exception e) {
			e.printStackTrace();
			responseData.setCode(300);
			responseData.setMsg("服务器异常");
			return responseData;
		}
	}

	/**
	 * <pre>
	 * 第一步:验证签名,签名通过后进行第二步
	 * 第二步:按一下步骤进行验证
	     * 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
	     * 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
	     * 3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email),
	     * 4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过,则表明本次通知是异常通知,务必忽略。
	     * 在上述验证通过后商户必须根据支付宝不同类型的业务通知,正确的进行不同的业务处理,并且过滤重复的通知结果数据。
	     * 在支付宝的业务通知中,只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时,支付宝才会认定为买家付款成功。
	 * </pre>
	 * 支付回调
	 * @param params
	 * @return
	 */
	@RequestMapping("alipay_callback")
	@ResponseBody
	public String callback(HttpServletRequest request) {

		// 将异步通知中收到的待验证所有参数都存放到map中
		Map<String, String> params = convertRequestParamsToMap(request);
		String paramsJson = JSON.toJSONString(params);
		logger.info("支付宝回调,{}", paramsJson);
		try {
			// 调用SDK验证签名
			boolean signVerified = AlipaySignature.rsaCheckV1(params, AppAlipayConfig.ALIPAY_PUBLIC_KEY,
					AppAlipayConfig.CHARSET, "RSA2");
			// 判断延签是否成功
			if (signVerified) {
				logger.info("支付宝回调签名认证成功");
				// 按照支付结果异步通知中的描述,对支付结果中的业务内容进行1\2\3\4二次校验,校验成功后在response中返回success,校验失败返回failure
				this.check(params);
				// 另起线程处理业务
				executorService.execute(new Runnable() {
					@Override
					public void run() {
						AlipayNotifyParam param = buildAlipayNotifyParam(params);
						String trade_status = param.getTradeStatus();
						// 支付成功
						if (trade_status.equals("TRADE_SUCCESS")) { // 只对支付成功的做处理
							// 处理支付成功逻辑
							try {
								/*
								 * // 处理业务逻辑。。。 myService.process(param);
								 */

							} catch (Exception e) {
								logger.error("支付宝回调业务处理报错,params:" + paramsJson, e);
							}
						} else {
							logger.error("没有处理支付宝回调业务,支付宝交易状态:{},params:{}", trade_status, paramsJson);
						}
					}
				});
				// 如果签名验证正确,立即返回success,后续业务另起线程单独处理
				// 业务处理失败,可查看日志进行补偿,跟支付宝已经没多大关系。
				return "success";
			} else {
				logger.info("支付宝回调签名认证失败,signVerified=false, paramsJson:{}", paramsJson);
				return "failure";
			}
		} catch (AlipayApiException e) {
			logger.error("支付宝回调签名认证失败,paramsJson:{},errorMsg:{}", paramsJson, e.getMessage());
			return "failure";
		}
	}

	/**
	 * 将request中的参数转换成Map
	 * 
	 * @param request
	 * @return
	 */
	private static Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {
		Map<String, String> retMap = new HashMap<String, String>();

		Set<Entry<String, String[]>> entrySet = request.getParameterMap().entrySet();

		for (Entry<String, String[]> entry : entrySet) {
			String name = entry.getKey();
			String[] values = entry.getValue();
			int valLen = values.length;

			if (valLen == 1) {
				retMap.put(name, values[0]);
			} else if (valLen > 1) {
				StringBuilder sb = new StringBuilder();
				for (String val : values) {
					sb.append(",").append(val);
				}
				retMap.put(name, sb.toString().substring(1));
			} else {
				retMap.put(name, "");
			}
		}

		return retMap;
	}

	/***
	 * 将map中的数据先转换为json,在转换为AlipayNotifyParam参数对象
	 * 
	 * @param params
	 * @return
	 */
	private AlipayNotifyParam buildAlipayNotifyParam(Map<String, String> params) {
		String json = JSON.toJSONString(params);
		return JSON.parseObject(json, AlipayNotifyParam.class);
	}

	/**
	 * 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
	 * 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
	 * 3、校验通知中的seller_id(或者seller_email)是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email),
	 * 4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过,则表明本次通知是异常通知,务必忽略。
	 * 在上述验证通过后商户必须根据支付宝不同类型的业务通知,正确的进行不同的业务处理,并且过滤重复的通知结果数据。
	 * 在支付宝的业务通知中,只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时,支付宝才会认定为买家付款成功。
	 * 
	 * @param params
	 * @throws AlipayApiException
	 */
	private void check(Map<String, String> params) throws AlipayApiException {
		String outTradeNo = params.get("out_trade_no");

		// 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
		Order order = getOrderByOutTradeNo(outTradeNo); // 这个方法自己实现
		if (order == null) {
			throw new AlipayApiException("out_trade_no错误");
		}

		// 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
		long total_amount = new BigDecimal(params.get("total_amount")).multiply(new BigDecimal(100)).longValue();
		if (total_amount != order.getPayPrice().longValue()) {
			throw new AlipayApiException("error total_amount");
		}

		// 3、校验通知中的seller_id(或者seller_email)是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email),
		// 第三步可根据实际情况省略

		// 4、验证app_id是否为该商户本身。
		if (!params.get("app_id").equals(alipayConfig.getAppid())) {
			throw new AlipayApiException("app_id不一致");
		}
	}

}

支付宝返回结果对应的参数类:AlipayNotifyParam

public class AlipayNotifyParam implements Serializable {

    private String appId;   
    private String tradeNo; // 支付宝交易凭证号 
    private String outTradeNo; // 原支付请求的商户订单号   
    private String outBizNo; // 商户业务ID,主要是退款通知中返回退款申请的流水号   
    private String buyerId; // 买家支付宝账号对应的支付宝唯一用户号。以2088开头的纯16位数字    
    private String buyerLogonId; // 买家支付宝账号 
    private String sellerId; // 卖家支付宝用户号    
    private String sellerEmail; // 卖家支付宝账号  
    private String tradeStatus; // 交易目前所处的状态,见交易状态说明    
    private BigDecimal totalAmount; // 本次交易支付的订单金额  
    private BigDecimal receiptAmount; // 商家在交易中实际收到的款项  
    private BigDecimal buyerPayAmount; // 用户在交易中支付的金额   
    private BigDecimal refundFee; // 退款通知中,返回总退款金额,单位为元,支持两位小数  
    private String subject; // 商品的标题/交易标题/订单标题/订单关键字等   
    private String body; // 该订单的备注、描述、明细等。对应请求时的body参数,原样通知回来   
    private Date gmtCreate; // 该笔交易创建的时间。格式为yyyy-MM-dd HH:mm:ss 
    private Date gmtPayment; // 该笔交易的买家付款时间。格式为yyyy-MM-dd HH:mm:ss  
    private Date gmtRefund; // 该笔交易的退款时间。格式为yyyy-MM-dd HH:mm:ss.S   
    private Date gmtClose; // 该笔交易结束时间。格式为yyyy-MM-dd HH:mm:ss   
    private String fundBillList; // 支付成功的各个渠道金额信息,array 
    private String passbackParams; // 公共回传参数,如果请求时传递了该参数,则返回给商户时会在异步通知时将该参数原样返回。    

    // 省略get set
}
 

 

支付配置

package com.qike.utils.pay;

public class AppAlipayConfig {

    // 默认网关
    public static final String GATWAY = "https://openapi.alipay.com/gateway.do";
    // 支付宝分配给开发者的应用ID
    public static final String APP_ID = "";
    // 应用私钥(私钥加密请求参数第三方公钥解密)
    public static final String PRIVATE_KEY = "";
    // 仅支持JSON
    public static final String FORMAT = "json";
    // 编码格式
    public static final String CHARSET = "utf-8";
    // 支付宝公钥
    public static final String ALIPAY_PUBLIC_KEY = "";
    // 签名类型
    public static final String SIGN_TYPE = "RSA2";
    // app支付回调地址(支付宝)
    public static final String APP_NOTIFY_URL = "";

}
 

文章转载与https://blog.csdn.net/thc1987/article/details/80269181

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值