支付宝电脑网站支付接口用沙箱简单测试

2020-02-13 16:02:09来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

支付宝电脑网站支付接口用沙箱简单测试

我使用springboot+jsp做的简单测试

官方电脑网站支付文档https://docs.open.alipay.com/270

首先加入支付宝接口的依赖、配置文件和工具类

<!-- 支付宝支付接口依赖 -->
<dependency>
    <groupId>com.alipay.sdk</groupId>
    <artifactId>alipay-sdk-java</artifactId>
    <version>4.9.13.ALL</version>
</dependency>

添加请求时要用的参数配置文件alipay.properties

#应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
alipay.app_id=
#商户私钥,您的PKCS8格式RSA2私钥
alipay.merchant_private_key=
#支付宝公钥
alipay.alipay_public_key=
#服务器异步通知页面路径
alipay.notify_url=
#页面跳转同步通知页面路径
alipay.return_url=
#签名方式
alipay.sign_type=RSA2
#字符编码格式
alipay.charset=utf-8
#支付宝网关
alipay.gatewayUrl=https://openapi.alipaydev.com/gateway.do
alipay.log_path=C:\

请到支付宝沙箱中找相关参数填上(异步通知和同步通知可以先不填,后面慢慢讲)

开发者中心-控制台-开发服务-研发服务-沙箱

在启动类加载配置文件

@SpringBootApplication
@PropertySource({"classpath:alipay.properties"})
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

添加AlipayConfig类

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

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

/**
 * 类名:AlipayConfig 功能:基础配置类 详细:设置帐户有关信息及返回路径
 */
@Configuration
public class 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;

	// 服务器异步通知页面路径  需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
	public static String notify_url;


	// 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
	public static String return_url;

	// 签名方式
	public static String sign_type;

	// 字符编码格式
	public static String charset;

	// 支付宝网关
	public static String gatewayUrl;

	// 支付宝网关
	public static String log_path;


	public String getApp_id() {
		return app_id;
	}

	@Value("${alipay.app_id}")
	public void setApp_id(String app_id) {
		AlipayConfig.app_id = app_id;
	}

	public String getMerchant_private_key() {
		return merchant_private_key;
	}

	@Value("${alipay.merchant_private_key}")
	public void setMerchant_private_key(String merchant_private_key) {
		AlipayConfig.merchant_private_key = merchant_private_key;
	}

	public String getAlipay_public_key() {
		return alipay_public_key;
	}

	@Value("${alipay.alipay_public_key}")
	public void setAlipay_public_key(String alipay_public_key) {
		AlipayConfig.alipay_public_key = alipay_public_key;
	}

	public String getNotify_url() {
		return notify_url;
	}

	@Value("${alipay.notify_url}")
	public void setNotify_url(String notify_url) {
		AlipayConfig.notify_url = notify_url;
	}

	public String getReturn_url() {
		return return_url;
	}

	@Value("${alipay.return_url}")
	public void setReturn_url(String return_url) {
		AlipayConfig.return_url = return_url;
	}

	public String getSign_type() {
		return sign_type;
	}

	@Value("${alipay.sign_type}")
	public void setSign_type(String sign_type) {
		AlipayConfig.sign_type = sign_type;
	}

	public String getCharset() {
		return charset;
	}

	@Value("${alipay.charset}")
	public void setCharset(String charset) {
		AlipayConfig.charset = charset;
	}

	public String getGatewayUrl() {
		return gatewayUrl;
	}

	@Value("${alipay.gatewayUrl}")
	public void setGatewayUrl(String gatewayUrl) {
		this.gatewayUrl = gatewayUrl;
	}

	public String getLog_path() {
		return log_path;
	}

	@Value("${alipay.log_path}")
	public void setLog_path(String log_path) {
		AlipayConfig.log_path = log_path;
	}

	/**
	 * 写日志,方便测试(看网站需求,也可以改成把记录存入数据库)
	 *
	 * @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();
				}
			}
		}
	}

}

添加AlipayUtil工具类

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.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradePagePayResponse;
import com.example.config.AlipayConfig;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.*;


/**
 * 支付宝接口工具类
 *
 * @author mowen
 *
 */
public class AlipayUtil {
	/**
	 * 发起支付
	 *
	 * @param amount          金额
	 * @param subject         主题
	 * @param notice          内容
	 * @param passback_params 自定义回调参数
	 * @return
	 * @throws AlipayApiException
	 * @throws Exception
	 */
	public static String sponsorPay(String amount, String subject, String notice, String passback_params) throws AlipayApiException {
		// 获得初始化的AlipayClient
		DefaultAlipayClient 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);

		// 商户订单号,商户网站订单系统中唯一订单号,必填
		SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyyMMddHHmmssSSS");
		String out_trade_no = simpleDateFormat.format(new Date());
		// 付款金额,必填
		String total_amount = amount;

		String body = notice;

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

		// 请求
		AlipayTradePagePayResponse response = alipayClient.pageExecute(alipayRequest);

		String result = "";
		if (response.isSuccess()) {

			result = response.getBody();
		}

		return result;

	}

	/**
	 * 支付宝退款接口
	 *
	 * @param outTradeNo     商户订单号
	 * @param tradeNo        支付宝交易号
	 * @param refundAmount   退款的金额
	 * @param refundReason   退款的原因说明
	 * @param out_request_no 标识一次退款请求,同一笔交易多次退款需要保证唯一,如需部分退款,则此参数必传
	 * @return
	 */
	public static String aliRefund(String outTradeNo, String tradeNo, String refundAmount, String refundReason, String out_request_no) {
		// 获得初始化的AlipayClient
		AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);

		// 设置请求参数
		AlipayTradeRefundRequest alipayRequest = new AlipayTradeRefundRequest();
		alipayRequest.setReturnUrl(AlipayConfig.return_url);
		alipayRequest.setNotifyUrl(AlipayConfig.notify_url);
		try {
			alipayRequest.setBizContent("{\"out_trade_no\":\"" + outTradeNo + "\"," + "\"trade_no\":\"" + tradeNo + "\"," + "\"refund_amount\":\"" + refundAmount + "\"," + "\"refund_reason\":\"" + refundReason + "\"," + "\"out_request_no\":\"" + out_request_no + "\"}");
			// 请求
			String result;
			result = alipayClient.execute(alipayRequest).getBody();
			return result;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 支付宝的验签方法
	 *
	 * @param req
	 * @return
	 */
	public static boolean checkSign(HttpServletRequest req) {
		Map<String, String[]> requestMap = req.getParameterMap();
		Map<String, String> paramsMap = new HashMap<>();
		requestMap.forEach((key, values) -> {
			String strs = "";
			for (String value : values) {
				strs = strs + value;
			}
			System.out.println(("key值为" + key + "value为:" + strs));
			paramsMap.put(key, strs);
		});

		// 调用SDK验证签名
		try {
			return AlipaySignature.rsaCheckV1(paramsMap, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type);
		} catch (AlipayApiException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.out.println("*********************验签失败********************");
			return false;
		}
	}

}

请求时执行步骤:

请求时执行步骤

跳转到支付页面

新建index.jsp和pay.jsp

index.jsp:

<%@ page language="java" contentType="text/html; charset=utf-8"%>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form method="post" action="http://127.0.0.1:8080/demo/toPay">
        <label>金额</label><input type="text" name="money" >
        <input type="submit" value="付款">
    </form>
</body>
</html>

pay.jsp:

<%@ page language="java" contentType="text/html; charset=utf-8"%>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div>${requestScope.result}</div>
</body>
</html>

新建一个controller类

@Controller
public class index {

    @RequestMapping
    public String index() {
        return "index";
    }

    @RequestMapping(value = "/toPay",method = RequestMethod.POST)
    public String toPay(String money, ModelMap map) {
        try {
            String result= AlipayUtil.sponsorPay(money,"我买了啥?","不知道啊",null);
            map.addAttribute("result",result);
        } catch (AlipayApiException e) {
            e.printStackTrace();
            System.out.println("请求支付宝接口失败");
        }
        return "pay";
    }
}

项目开启后访问进入index.jsp

image

输入框内输入金额点击付款就会进入后台控制器的toPay方法,然后请求支付宝接口,把请求得到的body内容放到request作用域,

转跳到pay.jsp,这个页面其实就是把得到的内容放到一个div里,然后会自动跳转到付款页面,因为内容就是一个会自动提交的from表单,restful模式的话,把它传给前端,前端找个div放进去就会自动跳转。

image

使用沙箱版的支付宝就可付款,上线环境一样的道理。

同步通知

官方文档:https://docs.open.alipay.com/59/103665

支付宝对商户的请求数据处理完成后,会将处理的结果数据直接通知给商户。这些处理结果数据就是同步通知参数。比如我填:

#页面跳转同步通知页面路径
alipay.return_url=http://127.0.0.1:8080/demo/toReturnURL

后台控制器添加方法:

    @RequestMapping(value = "/toReturnURL")
    public String toReturnURL() {
        return "returnURL";
    }

添加returnURL.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <span>同步页面</span>
</body>
</html>


到达付款页面付款几秒后会根据你填的同步通知地址请求,我后端又跳到jsp页面,也可以直接写一个htnl页面的路径地址,参数有以下这些,也可以用参数验证完后再跳转页面


参数 参数名称 类型 描述 范例
out_trade_no 商户订单号 String(64) 原支付请求的商户订单号 6823789339978248
total_amount
订单金额 Number(9,2) 本次交易支付的订单金额,单位为人民币(元),精确到小数点后2位 20.00
sign 签名 String(256) 601510b7970e52cc63db0f44997cf70e
sign_type签名类型String(10)

签名算法类型,目前支持RSA2和RSA,推荐使用RSA2

RSA2
trade_no 支付宝交易号 String(64)

支付宝交易凭证号

2013112011001004330000121536
auth_app_id 授权方的app_id String(32) 授权方的appid,由于本接口暂不开放第三方应用授权,因此auth_app_id=app_id 2014072300007148
app_id开发者的app_idString(32)支付宝分配给开发者的应用 ID2014072300007148
seller_id卖家支付宝用户号String(30)卖家支付宝用户号2088101106499364

timestamp


异步通知

当收银台调用预下单请求 API 生成二维码展示给用户后,用户通过手机扫描二维码进行支付,支付宝会将该笔订单的变更信息,沿着商户调用预下单请求时所传入的异步通知地址 notify_url,通过 POST 请求的形式将支付结果作为参数通知到商户系统。

异步回调地址状态码(http状态码) 为 200 时表示异步通知成功,返回码为 404 或 500 时则表示服务器内部错误,需要商家自行排查。

商家如果因为其他原因没有收到支付宝服务端返回的异步通知,开发者可以在开发者社区查看自查自查方案:

官方文档:https://docs.open.alipay.com/194/103296


异步通知和同步一样的地方在于,付款后带一些参数返回到你服务器,让你可以进行一下验证,

异步通知只有扫码才会有。

异步通知最重要的是要外网能访问到你。

所以连接路由器的话,需要用内网穿透,让外网能访问到你;

我使用的是http://www.ngrok.cc/

测试的加开个免费的通道即可(关于开启通道失败问题,我家是移动宽带,免费的服务器连接不了,出现连接失败可以用手机开个网试试,不然直接买vip的)

image

开启通道后:官网教程http://www.ngrok.cc/_book/

image

异步通知地址就可填写:

#服务器异步通知页面路径
alipay.notify_url=http://paydemo.free.idcfengye.com/demo/notifyURL

后台控制器添加方法:

    @RequestMapping(value = "/notifyURL",method = RequestMethod.POST)
    public void notifyURL(HttpServletRequest request) throws UnsupportedEncodingException, AlipayApiException {

        // 获取支付宝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);
        }

        // 调用SDK验证签名
        boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type);

        if (signVerified) {// 验证成功

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

            if (trade_status.equals("TRADE_FINISHED")) {
                System.out.println("交易结束,不可退款");
            } else if (trade_status.equals("TRADE_SUCCESS")) {
                System.out.println("交易支付成功");
            }

        } else {// 验证失败

        }
    }

用户付款后会打印:

image

params集合里有着这次订单的所有资料,具体有哪些参数请看官方文档。



原文链接:https://www.cnblogs.com/mowen120/p/12303660.html
如有疑问请与原作者联系

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:Java连载82-Set、Collection、List、Map的UML演示

下一篇:什么是队列?