一点一滴开始搭建自己的项目框架之支付宝篇 支付宝APP支付

       项目框架: 项目框架

       最近在封装传值方式  准备把json、fromData等传值方式封装成一个体系  可惜封装了半天还是接不到 可能是对HTTP的协议了解的好不够清楚 今天就先把APP支付宝支付的源码放出来吧

       好的首先开始支付宝的文档阅读

      https://docs.open.alipay.com/58/103584/  

      之后就是支付宝的工具包准备了

      

      这个是我的整体项目架构  支付宝工具被我放到了一个包里面 接口和实现类 被我封装到了三层中  接下里上源码

     com.bing.aliPay.pojo.Product.java(这个是支付VO类)

     

package com.bing.aliPay.pojo;

import java.io.Serializable;

/**
* @ClassName: Product
* @Description: TODO(支付宝支付 pojo)
* @date 2018-11-6 
*/
public class Product implements Serializable {
	private static final long serialVersionUID = 1L;
	private String productId;// 商品ID
	private String subject;// 订单名称
	private String body;// 商品描述
	private String totalAmount;// 总金额(单位是元)
	private String outTradeNo;// 订单号(唯一)
	private String spbillCreateIp;// 发起人IP地址
	private String attach;// 附件数据主要用于商户携带订单的自定义数据
	private String notifyUrl;// 支付宝回调地址
	private String return_url;// 页面同步通知地址

	public Product() {
		super();
	}

	public String getProductId() {
		return productId;
	}

	public void setProductId(String productId) {
		this.productId = productId;
	}

	public String getBody() {
		return body;
	}

	public void setBody(String body) {
		this.body = body;
	}

	public String getOutTradeNo() {
		return outTradeNo;
	}

	public void setOutTradeNo(String outTradeNo) {
		this.outTradeNo = outTradeNo;
	}

	public String getSpbillCreateIp() {
		return spbillCreateIp;
	}

	public void setSpbillCreateIp(String spbillCreateIp) {
		this.spbillCreateIp = spbillCreateIp;
	}

	public String getAttach() {
		return attach;
	}

	public void setAttach(String attach) {
		this.attach = attach;
	}
	public String getNotifyUrl() {
		return notifyUrl;
	}

	public void setNotifyUrl(String notifyUrl) {
		this.notifyUrl = notifyUrl;
	}

	public String getSubject() {
		return subject;
	}

	public void setSubject(String subject) {
		this.subject = subject;
	}

	public String getTotalAmount() {
		return totalAmount;
	}

	public void setTotalAmount(String totalAmount) {
		this.totalAmount = totalAmount;
	}

	public String getReturn_url() {
		return return_url;
	}

	public void setReturn_url(String return_url) {
		this.return_url = return_url;
	}

}

      com.bing.aliPay.util.Constants.java(这个是支付宝常量类)

      

package com.bing.aliPay.util;

import org.springframework.util.ClassUtils;

/**
* @ClassName: Constants
* @Description: TODO(常量)
* @date 2018-11-6
*/
public class Constants {
	
	public static final String SF_FILE_SEPARATOR = System.getProperty("file.separator");//文件分隔符
	public static final String SF_LINE_SEPARATOR = System.getProperty("line.separator");//行分隔符
	public static final String SF_PATH_SEPARATOR = System.getProperty("path.separator");//路径分隔符

	public static final String QRCODE_PATH = ClassUtils.getDefaultClassLoader().getResource("static").getPath()+SF_FILE_SEPARATOR+"qrcode"; 
	
	//微信账单 相关字段 用于load文本到数据库
	public static final String WEIXIN_BILL = "tradetime, ghid, mchid, submch, deviceid, wxorder, bzorder, openid, tradetype, tradestatus, bank, currency, totalmoney, redpacketmoney, wxrefund, bzrefund, refundmoney, redpacketrefund, refundtype, refundstatus, productname, bzdatapacket, fee, rate";
	
	public static final String PATH_BASE_INFO_XML = SF_FILE_SEPARATOR+"WEB-INF"+SF_FILE_SEPARATOR+"xmlConfig"+SF_FILE_SEPARATOR;
	
	public static final String CURRENT_USER = "UserInfo";
	
	public static final String SUCCESS = "success";
	
	public static final String FAIL = "fail";
	
}

       com.bing.aliPay.util.AliPayConfig.java(支付宝公共变量)

       

package com.bing.aliPay.util;

import com.bing.model.PayModelVo;
import com.bing.util.PropertiesUtil;

import com.alibaba.fastjson.JSONObject;
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.request.AlipayTradeAppPayRequest;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;

/**
 * @ClassName: AliPayConfig
 * @Description: TODO(配置公共参数)
 * @date 2017-11-6
 */
public final class AliPayConfig {

	/**
	 * 私有的默认构造子,保证外界无法直接实例化
	 */
	private AliPayConfig(){};

	// 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
	public static String app_id = "";
	
	// 商户私钥,您的PKCS8格式RSA2私钥
	public static String merchant_private_key = "";
	
	// 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
	public static String alipay_public_key = "";
	
	//异步回调地址
	public static final String NOTIFY_URL = PropertiesUtil.getPropertiesValue("sys","envPrefix")+"";//测试
	
	//PCreturn地址
	public static final String RETURN_URL ="www.baidu.com";//测试
	
	public static final String PRODUCT_CODE = "QUICK_MSECURITY_PAY";
	
	// 签名方式
	public static String sign_type = "RSA2";
	
	// 字符编码格式
	public static String charset = "UTF-8";
	
	// 字符编码格式
	public static String time_out = "30m";

	private static String method = "alipay.trade.refund";

	// 支付宝网关
	public static String gatewayUrl = "https://openapi.alipay.com/gateway.do";

	/**
	 * 参数类型
	 */
	public static String PARAM_TYPE = "json";
	/**
	 * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
	 * 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载
	 */
	private static class SingletonHolder{
		/**
		 * 静态初始化器,由JVM来保证线程安全
		 */
		private static AlipayClient alipayClient = new DefaultAlipayClient(
				gatewayUrl, app_id,
				merchant_private_key, PARAM_TYPE, charset,
				alipay_public_key,sign_type);
	}

	/**
	 * @Title: getAlipayClient
	 * @Description: TODO(支付宝APP请求客户端实例)
	 * @return
	 */
	public static AlipayClient getAlipayClient(){
		return SingletonHolder.alipayClient;
	}

	public static String getResponseBody(PayModelVo payModelVo, String strBody) throws AlipayApiException {
		AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
		
		
		AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
		model.setBody("支付宝支付");
		model.setOutTradeNo(payModelVo.getOutTradeNo()); //商户订单id
		model.setTotalAmount(payModelVo.getMoney());// 订单金额:元
		model.setSubject(payModelVo.getSubject());// 订单标题
		model.setTimeoutExpress("30m");
		model.setProductCode("QUICK_MSECURITY_PAY");
		model.setEnablePayChannels("moneyFund,debitCardExpress");
		request.setBizModel(model);
		request.setNotifyUrl(AliPayConfig.NOTIFY_URL);
		String orderString="";
		try {
	        //这里和普通的接口调用不同,使用的是sdkExecute
	        AlipayTradeAppPayResponse response = AliPayConfig.getAlipayClient().sdkExecute(request);
	        orderString=response.getBody();
	        System.err.println("orderString :  "+orderString);
		} catch (AlipayApiException e) {
			e.printStackTrace();
		}
		return orderString;
	}
	
	public static String aliPayPc(PayModelVo payModelVo) {
		AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
		alipayRequest.setReturnUrl(payModelVo.getReturnUrl());// 前台通知
		alipayRequest.setNotifyUrl(payModelVo.getNotifyUrl());// 后台回调
		JSONObject bizContent = new JSONObject();
		bizContent.put("out_trade_no", payModelVo.getOutTradeNo());
		bizContent.put("total_amount", payModelVo.getMoney());// 订单金额:元
		bizContent.put("subject", payModelVo.getSubject());// 订单标题
		bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");
		bizContent.put("body", payModelVo.getBody());
		bizContent.put("enable_pay_channels", "moneyFund,debitCardExpress");
		String biz = bizContent.toString().replaceAll("\"", "'");
		alipayRequest.setBizContent(biz);
		String form = Constants.FAIL;
		AlipayClient alipayClient = getAlipayClient();
		try {
			form = alipayClient.pageExecute(alipayRequest).getBody();
		} catch (AlipayApiException e) {
		}
		return form;
	}


	
}

在这里回调函数是被抽取出来了  

PropertiesUtil.getPropertiesValue("sys","envPrefix")

这个方法是我的读取xml配置文件的方法  在后面的博客工具类篇中我会提到

好了  这些就是支付宝的配置文件  接下来就是最重要的 预下单与回调函数的编写了

预下单

/**
	 * @Title: trans
	 * @Description: (支付宝交易)
	 * @param request
	 * @return
	 */
	@RequestMapping(value = "trans", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
	@ResponseBody
	public String trans(HttpServletRequest request, final PayModelVo payModelVo, final String addressId, final String cartIds, String mergeId, final String isDispatch, final String isMerge, final String logisticsId, String ProductList) {
		log.debug("开始支付宝调用");
		ResultModel resultModel = new ResultModel();
		try {
			String token = request.getHeader("token");
			String money = payModelVo.getMoney(); /// 变动金额
			String type = payModelVo.getType(); // 消费类型 用户充值 用户提现 商品出售  会员购买会员升级  询价单购买  待支付订单的支付(此类支付不需要预订单了)
			String out_trade_no = payModelVo.getOutTradeNo();
			//回调地址
			payModelVo.setToken(token);
			payModelVo.setNotifyUrl(AliPayConfig.NOTIFY_URL);
			payModelVo.setBody("支付用途");
			payModelVo.setMoney(money);
			//将payModelVo中所有信息存入redis
			RedisTool.set("notify_"+out_trade_no,JSON.toJSONString(payModelVo));
			//返回支付宝请求
			Product product = new Product();
			String subject = "支付宝支付";
			product.setOutTradeNo(payModelVo.getOutTradeNo());
			product.setSubject(subject);
			product.setTotalAmount(payModelVo.getMoney());
			product.setNotifyUrl(AliPayConfig.NOTIFY_URL);
			String orderString = aliPayService.aliPayMobile(product);
			resultModel = new ResultModel(200,"10000","执行成功");
			Map<String,String> map = new HashMap<>();
			map.put("orderString",orderString);
			resultModel.setResult(map);
		} catch (Exception e) {
			log.error("支付宝支付异常", e);
			e.printStackTrace();
			resultModel.setError(500);
			resultModel.setErrorCode("10008");
			resultModel.setMsg("预下单异常");
		}
		log.info("支付宝支付app:" + JsonUtil.toJson(resultModel));
		return JsonUtil.toJson(resultModel);
	}

支付宝支付实现类

aliPayService.aliPayMobile
public String aliPayMobile(Product product) {
		//实例化客户端
		//实例化具体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.setOutTradeNo(product.getOutTradeNo()); //商户订单id
		model.setTotalAmount(product.getTotalAmount());// 订单金额:元
		model.setSubject(product.getSubject());// 订单标题
		model.setTimeoutExpress("30m");
		model.setProductCode("QUICK_MSECURITY_PAY");
		model.setEnablePayChannels("moneyFund,debitCardExpress");
		request.setBizModel(model);
		request.setNotifyUrl(product.getNotifyUrl());
		String orderString="";
		try {
		        //这里和普通的接口调用不同,使用的是sdkExecute
		        AlipayTradeAppPayResponse response = AliPayConfig.getAlipayClient().sdkExecute(request);
		        orderString=response.getBody();
		        logger.info("orderString", orderString);//就是orderString 可以直接给客户端请求,无需再做处理。
		    } catch (AlipayApiException e) {
		    	logger.error("支付宝构造表单失败", e);
		}
		return orderString;
	}

APP调用预下单之后可以直接拿到字符串去进行支付,支付完成之后 支付宝会进行支付成功的回调  一般我们的业务都是在成功的回调中去进行编写的

支付回调 ,当然  如果要是想测试回调 在本机的情况下 你需要外网的映射 这些东西网上不少 但是一般都是需要花钱的 一般也不贵 我用的net123 ,30块钱用一年了都

支付回调

/**
	 * @Title: notify
	 * @Description: (充值支付宝支付回调)
	 * @param request
	 * @param response
	 * @throws Exception
	 */
	@Transactional(readOnly = false)
	@RequestMapping(value = "alipay_notify", produces = "application/json;charset=UTF-8")
	public void alipay_notify(HttpServletRequest request, HttpServletResponse response) throws Exception {
		log.info("支付宝支付回调!");
		String message = "failure";
		String outtradeno = "";
		//如果失败,应存入redis,便于出问题时候查看原因(默认保存两天)
		try {
			Map<String, String> params = new HashMap<String, String>();
			Map<String, String[]> requestParams = request.getParameterMap();
			for (Iterator<String> 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] + ",";
				}
				// 乱码解决,这段代码在出现乱码时使用
				//valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
				params.put(name, valueStr);
			}
			boolean signVerified = AlipaySignature.rsaCheckV1(params, AliPayConfig.alipay_public_key,
					AliPayConfig.charset, AliPayConfig.sign_type); // 调用SDK验证签名
			outtradeno = params.get("out_trade_no");
			//将支付数据存入redis中 使用out_tran_no与支付前缀来进行区分
			PayModelVo paymodel = (PayModelVo) new Gson().fromJson(RedisTool.get("notify_" + outtradeno), PayModelVo.class);
			if (signVerified) {//支付宝验证签名成功
				// 若参数中的appid和填入的appid不相同,则为异常通知
				if (!AliPayConfig.app_id.equals(params.get("app_id"))) {
					log.info("与付款时的appid不同,此为异常通知,应忽略!");
					RedisTool.setex("notify_" + outtradeno, DateTools.getDatelinetoInt(),"与付款时的appid不同");
				} else {
					String receiptAmount = params.get("receipt_amount");
					String status = params.get("trade_status");
					if (status.equals("TRADE_SUCCESS") || status.equals("TRADE_FINISHED")) { // 如果状态是已经支付成功
						/**
						 * 需要判断redis中是否有out_trade_no
						 * 如果没有,说明有两种情况:一种是数据出错(包括等待时间过长,redis自动清除),一种是已经回调过一次(默认成功完成后删除redis),需要去数据库查询单子状态,进一步确认
						 * 如果有,那就正常执行业务
						 */
						//redis中没有out_trade_no
						if (!RedisTool.exists("notify_" + outtradeno)) {
						}else{

							RedisTool.del("notify_" + outtradeno);
							// 校验金额
							if(!receiptAmount.equals(paymodel.getMoney())){
								//金额数目不对
								return;
							}
							ResultModel resultModel = new ResultModel();
							/**
							 *  TODO 业务处理
							 */
//							payService.business(paymodel,resultModel);
							if(resultModel.getError()==200){
								message="success";
								if(paymodel.getPaytype().equals("7")){//通联微信购买,需要向pc推送
									try {
//										WxPayController.goeasy(paymodel,"true");//GoEasy通知前端
									} catch (Exception e) {
										e.printStackTrace();
									}
								}

							}else{
								log.info("notify_" + outtradeno+":业务处理失败!");
								RedisTool.setex("notify_" + outtradeno, DateTools.getDatelinetoInt(),"业务处理失败");
							}
						}
					} else{
						log.info("notify_" + outtradeno+":支付宝回调failure");
						RedisTool.setex("notify_" + outtradeno, DateTools.getDatelinetoInt(),"支付宝处理出错");
					}
				}
			}else { // 如果验证签名没有通过
				log.info("notify_" + outtradeno+":验证签名失败!");
				RedisTool.setex("notify_" + outtradeno,DateTools.getDatelinetoInt(),"验证签名失败");
			}
		} catch (Exception e) { 
			log.error("notify_" + outtradeno+"alipay_notify出现异常", e);
			RedisTool.setex("notify_" + outtradeno, 1,"其他异常");
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
		} finally {
			writeMessageToResponse(response, message);
		}
	}

返回给支付宝是否成功的信息

/**
	 * @Title: writeMessageToResponse
	 * @Description: (向支付宝发起支付结果通知)
	 * @param response
	 * @param message
	 */
	protected void writeMessageToResponse(HttpServletResponse response, String message) {
		PrintWriter writer = null;
		try {
			response.setCharacterEncoding(StandardCharsets.UTF_8.name());
			writer = response.getWriter();
			writer.print(message);
			writer.flush();
		} catch (IOException e) {
			log.error("io出现异常", e);
		} finally {
			if (writer != null)
				writer.close();
		}
	}

支付宝的回调 最关键的就是验证 其实你看那么多代码  几乎全部都是验证用的  首先取出参数 进行签名 验证支付宝的签名的合法性 这个是用来保证支付的安全性  之后就是验证 是否是二次回调  当你的业务没有处理成功的时候 你没有给支付宝返回正确的返回值 支付宝会进行多次的回调的  如果是二次回调 你需要直接略过 最后是验证你的支付金额和卖出产品的金额是否一致  这个很重要  比如前台直接将加个修改成0.01那直接支付你不去验证 那业务也是可以成立的 回调依然后进行

好的 支付宝APP支付的业务就只有这些了  下一篇是支付宝的

PC支付 :PC支付

有什么不对的地方请直接评论 谢谢

猜你喜欢

转载自blog.csdn.net/qq_38689769/article/details/86362490
今日推荐