支付宝电脑网站支付接口用沙箱简单测试
2020-02-13 16:02:09来源:博客园 阅读 ()
支付宝电脑网站支付接口用沙箱简单测试
我使用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
输入框内输入金额点击付款就会进入后台控制器的toPay方法,然后请求支付宝接口,把请求得到的body内容放到request作用域,
转跳到pay.jsp,这个页面其实就是把得到的内容放到一个div里,然后会自动跳转到付款页面,因为内容就是一个会自动提交的from表单,restful模式的话,把它传给前端,前端找个div放进去就会自动跳转。
使用沙箱版的支付宝就可付款,上线环境一样的道理。
同步通知
官方文档: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_id | String(32) | 支付宝分配给开发者的应用 ID | 2014072300007148 |
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的)
开启通道后:官网教程http://www.ngrok.cc/_book/
异步通知地址就可填写:
#服务器异步通知页面路径
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 {// 验证失败 } }
用户付款后会打印:params集合里有着这次订单的所有资料,具体有哪些参数请看官方文档。
原文链接:https://www.cnblogs.com/mowen120/p/12303660.html
如有疑问请与原作者联系
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- Java高级实战Maven+JSP+SSM+Mysql实现的音乐网站,70%人不会 2020-06-04
- Maven+JSP+SSM+Mysql实现的音乐网站 2020-06-03
- 17张图揭密支付宝系统架构 2020-06-03
- 从聚合支付业务的设计来聊聊策略模式 2020-06-03
- Java开发必知道的国外10大网站 2020-05-20
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash