JavaWeb对接支付宝

最近想做JavaWeb的项目,里面用到支付宝。

友情提示:接入支付宝属于交互式网站,需要*局备案,银监会备案等一些操作,非个人可以实现,基本不会通过的,如果私自上线,可能面临罚款,甚至拘役。

开始正题:

一、支付宝测试环境代码测试

1.下载电脑网站的官方demo:

下载地址:https://docs.open.alipay.com/270/106291/

JavaWeb对接支付宝

2.下载解压导入eclipse

JavaWeb对接支付宝

readme.txt请好好看一下。

只有一个Java配置类,其余都是JSP。

3.配置AlipayConfig

(1).注册蚂蚁金服开发者账号(免费,不像苹果会收取费用)

注册地址:https://open.alipay.com ,用你的支付宝账号扫码登录,完善个人信息,选择服务类型(我选的是自研)。

JavaWeb对接支付宝

(2).设置app_id和gatewayUrl

JavaWeb对接支付宝

 

JavaWeb对接支付宝

其中密钥需要自己生成,appID和支付宝网关是已经给好的,网关有dev字样,表明是用于开发测试。

(3).设置密钥

JavaWeb对接支付宝

点击“生成方法”,打开界面如下:

JavaWeb对接支付宝

下周密钥生成工具,解压打开后,选择2048位生成密钥:

JavaWeb对接支付宝

如果没有设置过,此时显示文本是“设置应用公钥”,我这里是已经设置过得。

JavaWeb对接支付宝

 

设置方法,“打开密钥文件路径”:

JavaWeb对接支付宝

JavaWeb对接支付宝

复制应用公钥2048.txt中的内容到点击“设置应用公钥”的弹出框中,保存:

JavaWeb对接支付宝

  • 商户私钥(merchant_private_key)

    复制 应用私钥2048.txt 中的内容到merchant_private_key中。

  • 支付宝公钥(alipay_public_key)

JavaWeb对接支付宝

点击如上图链接,复制弹出框里面的内容到alipay_public_key。

如果这个设置不对,结果是:支付成功,但是验签失败。

如果是正式环境,需要上传到对应的应用中:

JavaWeb对接支付宝

(4).服务器异步通知页面路径(notify_url)

如果没有改名,修改IP和端口号就可以了,我自己的如下:

http://localhost:8080/alipay.trade.page.pay-JAVA-UTF-8/notify_url.jsp

(5).页面跳转同步通知页面路径(return_url)

http://localhost:8080/alipay.trade.page.pay-JAVA-UTF-8/return_url.jsp

4.测试运行

JavaWeb对接支付宝

JavaWeb对接支付宝

测试用的支付宝买家账户可以在“沙箱账号”这个页面可以找到:

JavaWeb对接支付宝

支付成功后,验签结果:

JavaWeb对接支付宝

二:对接至SSM

支付宝的接口流程:

JavaWeb对接支付宝

重要的步骤是里面的 6 7 8步骤,第6步是同步回调,只能说明你接口对接成功,并返回你对接的信息,但是不能表明支付成功,所以这个时候出现了第7步,异步回调,当异步回调成功,就可以说明支付成功。但是,这样不是准确的,异步回调万一回不来,你就不清楚了,所以出现了第8步,主动去查询订单信息。能查到订单信息,说明支付成功了。

好了,描述就到这里,下面看代码:

项目主要添加了两个java类,

      一:是AlipayController,主要涉及前后台数据处理和逻辑访问,例如同步回调异步回调的代码、页面的跳转代码等

      二:是AlipayConfig,这里涉及的是支付宝配置信息的设置。例如:公钥、私钥、网关、回调地址等。。。。

JavaWeb对接支付宝

AlipayController 代码如下:

import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.ischoolbar.programmer.entity.admin.Log;
import com.ischoolbar.programmer.entity.admin.News;
import com.ischoolbar.programmer.entity.admin.Order;
import com.ischoolbar.programmer.entity.admin.User;
import com.ischoolbar.programmer.page.admin.Page;
import com.ischoolbar.programmer.service.admin.LogService;
import com.ischoolbar.programmer.service.admin.NewsCategoryService;
import com.ischoolbar.programmer.service.admin.NewsService;
import com.ischoolbar.programmer.service.admin.OrderService;
import com.ischoolbar.programmer.service.admin.SiteService;
import com.ischoolbar.programmer.util.AlipayConfig;
import com.ischoolbar.programmer.util.GetSystemTime;

@RequestMapping("/alipay")
@Controller
public class AlipayController {
	@Autowired
	private NewsService newsService;

	@Autowired
	private NewsCategoryService newsCategoryService;

	@Autowired
	private OrderService orderService;

	@Autowired
	private SiteService siteService;

	@RequestMapping(value="/toPay",method=RequestMethod.POST,produces = "text/html;charset=utf-8")
	@ResponseBody
	public String getList(HttpServletRequest request,@RequestParam(name="id",required=true,defaultValue="")Long id,@RequestParam(name="telephone",required=true,defaultValue="")String telephone) throws UnsupportedEncodingException, AlipayApiException{
		//获得初始化的AlipayClient
		AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);
		//设置请求参数
		AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
		alipayRequest.setReturnUrl(AlipayConfig.return_url);
		alipayRequest.setNotifyUrl(AlipayConfig.notify_url);
		
		//付款金额,必填
		String total_amount = new String(news.getMoney().toString());
		//订单名称,名称为产品id+产品名称
		String subject = new String(news.getId()+":"+news.getTitle());
		//商品描述,摘要
		String body = new String(news.getAbstrs());

		alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\"," 
				+ "\"total_amount\":\""+ total_amount +"\"," 
				+ "\"subject\":\""+ subject +"\"," 
				+ "\"body\":\""+ body +"\"," 
				+ "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");

		//若想给BizContent增加其他可选请求参数,以增加自定义超时时间参数timeout_express来举例说明
		//alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\"," 
		//		+ "\"total_amount\":\""+ total_amount +"\"," 
		//		+ "\"subject\":\""+ subject +"\"," 
		//		+ "\"body\":\""+ body +"\"," 
		//		+ "\"timeout_express\":\"10m\"," 
		//		+ "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
		//请求参数可查阅【电脑网站支付的API文档-alipay.trade.page.pay-请求参数】章节

		//请求
		String result = alipayClient.pageExecute(alipayRequest).getBody();
		return result;
	}


	/**
	 * 此功能看付款是否成功,异步实现
	 * @throws UnsupportedEncodingException 
	 * @throws AlipayApiException 
	 * */
	@RequestMapping(value="/notifyInfo",method=RequestMethod.POST)
	@ResponseBody
	public String notifyInfo(HttpServletRequest request) throws UnsupportedEncodingException, AlipayApiException{
		/* *
		 * 功能:支付宝服务器异步通知页面:成功扣款
		 * 日期:2017-03-30
		 * 说明:
		 * 以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
		 * 该代码仅供学习和研究支付宝接口使用,只是提供一个参考。


		 *************************页面功能说明*************************
		 * 创建该页面文件时,请留心该页面文件中无任何HTML代码及空格。
		 * 该页面不能在本机电脑测试,请到服务器上做测试。请确保外部可以访问该页面。
		 * 如果没有收到该页面返回的 success 
		 * 建议该页面只做支付成功的业务逻辑处理,退款的处理请以调用退款查询接口的结果为准。
		 */
		//获取支付宝POST过来反馈信息
		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验证签名
		//——请在这里编写您的程序(以下代码仅作参考)——

		/* 实际验证过程建议商户务必添加以下校验:
		1、需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
		2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
		3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email)
		4、验证app_id是否为该商户本身。
		 */
		if(signVerified) {//验证成功
			//商户订单号
			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");
			//获得初始化的AlipayClient
			AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);
			/**
			 * 再次确认是否扣款成功
			 * */
			//设置请求参数
			AlipayTradeQueryRequest alipayRequest = new AlipayTradeQueryRequest();
			alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","+"\"trade_no\":\""+ trade_no +"\"}");
			String result = alipayClient.execute(alipayRequest).getBody();
			boolean one = result.contains("\"msg\":\"Success\""); 
			boolean two = result.contains("\"sub_msg\":\"交易不存在\"");
			if(one||!two)
			{
				orderService.updateStatus(out_trade_no);
			}
			//			if(trade_status.equals("TRADE_FINISHED")){
			//				//判断该笔订单是否在商户网站中已经做过处理
			//				//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
			//				//如果有做过处理,不执行商户的业务程序
			//
			//				//注意:
			//				//退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知
			//			}else if (trade_status.equals("TRADE_SUCCESS")){
			//				//判断该笔订单是否在商户网站中已经做过处理
			//				//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
			//				//如果有做过处理,不执行商户的业务程序
			//
			//				//注意:
			//				//付款完成后,支付宝系统发送该交易状态通知
			//			}
		}else {//验证失败
			System.out.println("notifyInfo 验签失败");
			//调试用,写文本函数记录程序运行情况是否正常

			//String sWord = AlipaySignature.getSignCheckContentV1(params);
			//AlipayConfig.logResult(sWord);
		}

		//——请在这里编写您的程序(以上代码仅作参考)——
		return "success";
	}


	/**
	 * 是请求成功,非扣款成功,这里涉及页面跳转
	 * @throws UnsupportedEncodingException 
	 * @throws AlipayApiException 
	 * */
	@SuppressWarnings("unused")
	@RequestMapping(value="/returnInfo",method=RequestMethod.GET)
	@ResponseBody
	public ModelAndView returnInfo(HttpServletRequest request) throws UnsupportedEncodingException, AlipayApiException{
		/* *
		 * 功能:支付宝服务器同步通知页面
		 * 日期:2017-03-30
		 * 说明:
		 * 以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
		 * 该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
		 * 调用成功

		 *************************页面功能说明*************************
		 * 该页面仅做页面展示,业务逻辑处理请勿在该页面执行
		 * 
		 * 这里 并非 扣款成功, 这里不能作为标准
		 */

		//同步参数获取
		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验证签名
		ModelAndView mv = new ModelAndView();
		//——请在这里编写您的程序(以下代码仅作参考)——
		if(signVerified) {
			System.out.println("returnInfo 验签成功,且进行页面跳转等准备");
			/**
			 * 验签成功,获取此手机号码下所有以购买的项目
			 * */
			Map<String, Object> queryMap = new HashMap<String, Object>();
			//商户订单号
			String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8");
			String telephone = orderService.searchTelephone(out_trade_no)
		}else {
			System.out.println("returnInfo 验签失败");
		}
		return mv;
	}
}

配置文件设置:

package com.ischoolbar.programmer.util;

import java.io.FileWriter;
import java.io.IOException;

/* *
 *类名:AlipayConfig
 *功能:基础配置类
 *详细:设置帐户有关信息及返回路径
 *修改日期:2017-04-05
 *说明:
 *以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
 *该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
 */

public class AlipayConfig {
	
//↓↓↓↓↓↓↓↓↓↓请在这里配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

	// 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
	public static String app_id = "";
	
	
	private static String ip_address = "";
	
	
	// 商户私钥,您的PKCS8格式RSA2私钥
    public static String merchant_private_key = ";
    // 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
    public static String alipay_public_key = "";
    // 服务器异步通知页面路径  需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
    public static String notify_url = "http://"+ip_address+"/alipay/notifyInfo";
	// 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
	public static String return_url = "http://"+ip_address+"/alipay/returnInfo";
	// 签名方式
	public static String sign_type = "RSA2";
	// 字符编码格式
	public static String charset = "utf-8";
	// 支付宝网关
	public static String gatewayUrl = "https://openapi.alipaydev.com/gateway.do";
	// 支付宝网关
	public static String log_path = "/soft/alipay";


//↑↑↑↑↑↑↑↑↑↑请在这里配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

    /** 
     * 写日志,方便测试(看网站需求,也可以改成把记录存入数据库)
     * @param sWord 要写入日志里的文本内容
     */
    public static void logResult(String sWord) {
        FileWriter writer = null;
        try {
            writer = new FileWriter(log_path + "alipay_log_" + System.currentTimeMillis()+".txt");
            writer.write(sWord);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

 

上一篇:MySQL中MyISAM为什么比InnoDB查询快


下一篇:node+vue实现微信支付(沙箱)完整版,亲测可用