手机网站支付常用于HTML5运用,常见于微信"大众年夜众号上的运用。手机网站支付文档
手机网站支付的流程图:
用户点击H5运用中的支付按钮点击支付按钮会要求后台接口,后台接口要求支付宝的支付接口,支付接口会返回一段html代码个中包括一个form表单和一段js代码用于自动提交表单,表单提交后就会自动跳转到支付宝的支付页面(如果手机中装了支付App就去打开APP,如果没有就在网页版支付支付成功后会调用支付时设置的同步url, 然后跳转到商户的后台系统,一样平常情形下商户系统会展示一下支付成功,以及购买的商品信息等视图流程图
二:集成步骤0. 创建运用、配置密钥
集成前须要先创建运用、配置密钥、回调地址等,详细操作请查看Spring Boot入门教程(三十五):支付宝集成-准备事情
1. dependency<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><!-- alipay begin --><dependency> <groupId>com.alipay</groupId> <artifactId>alipay-sdk-java</artifactId> <version>20170725114550</version></dependency><dependency> <groupId>com.alipay</groupId> <artifactId>alipay-trade-sdk</artifactId> <version>20161215</version></dependency><dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId></dependency><dependency> <groupId>commons-configuration</groupId> <artifactId>commons-configuration</artifactId> <version>1.10</version></dependency><dependency> <groupId>com.google.zxing</groupId> <artifactId>core</artifactId> <version>3.2.1</version></dependency><dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId></dependency><!-- alipay end -->
2. application.yml
手机网站支付须要支付成功后同步的地址returnUrl,支付成功后跳转到的页面,这个值既可以配在这里也可以在Java代码调用支付宝接口时也可以设置为别的值
# 沙箱账号pay: alipay: gatewayUrl: https://openapi.alipaydev.com/gateway.do appid: 2016091400508498 appPrivateKey: MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCtXKWs+trRSuCxEUvlsEeSAuLWs3B/Dh74R8223BnfzoA29aGeoycAqfKlBMcbzU2G1KayESvZKGpwBLeejSbecRYjgZsQDyEaDimQQJtGFvZVV6u4XNnvIJ72eQzEaEIQfuiorjBTLm6DQuds4R0GxftqON6QFoIZkWB9ZrZKd02cuy16dW+UqtLVGGAHcCIAkB63zUiKSNfweMddneZ7MVs3lvu3xhMnD+5us/+n2Vp4qhfmpYLcdqIW6InU4GypeoOpyUTrfUGpgdR0l924vHy/GQJZEKFaRcK3cYK+ECyKpSIoqaJJFLHbkqsliuPpMUG+rM3jiqeIAH4psAznAgMBAAECggEBAJ5jyEbbxrJzrAh7GhHX1fwUQPYSadTbrPYAfHX2cHlnrQMJtsk+nTLhEv0r+VJwZ8WpYkfMong8kcqYtL7ajcmsHqMAFhE9EWxBxj2ymWsXLabZe93sj30IG9Rq0nxcGQgDO0RqKWLGSFgK93Al2KRInKT3InkY53K+vR61ihVLmGf7+GwPtIZteBy+tuAyvcj2RlkYvjiFi3ySyZ1wA3sJIlgrGiixd6fj20XFGNE3CnAwu0BJgXXbP/S9J4C0RRa3ZXj8fX7oONhVxz2xKxn7AT4e8OWjt7J41H2LRct8Fgl9pqgz2FJYv/WfbkG8x9fGiKYYvPXoxjjrk/tkewkCgYEA8f9Lcu5JPrE9rpw9zlwhm5cOO81xLxdwL5R5/1bRP48BZGIYuqlCbVvjJVqtO8eTnLhUwH7fG8B7cmoeO9bGr9GQrtfyCqz6FtVymTBieJlfgZDVhtzyv2qKOBMIFE8jsbSBK/NHHMvykJ+XdQ1riwCeQDdXICRuYTTFwGk2OsUCgYEAt2SoN95tVmVrvKG6ATLNEtge/ozeVywA4GjltrSw/G9vqp+DkkT2pY19uROuzMazoTzKWpPho2q/qzNlv/ANbOFM2GEmKamQ7CO88JgRxMsPTvc/HxCLU/ClMJU8LcOf9LfP2KYZpPwuheKJoF4vDGj8NsbFmccJyYSdpkNEk7sCgYBJlL2FMaz1sgC2Ue19DIhvfaunRV1P20mSPgwmNmijccETm7w3LXX0OIdFeV/JGHLqqSWj7i+6iXk/ncKZoUGCfi8G6sQ+uL/GJ5qTt6GJV+ExTS+PtSjeSO/EAw1m13Vb+C16hpstx1l23f+4aJ81gbecgPct38XsKpaiXZtOnQKBgQCMsN7QRYYxwoq9YoDUzIlAzKYyeBVWYL6najHYUZR5hG/xQIBqZRem9/4cTvpJxKInrwA6LrrqaEl0aHDFp75U6h7O3PCvA5PXZK9dD/yJsZIj7U/yX/nTQokn1UUegrYiwiTkusBvrrtuINWePsLvTVc4GpObHnPmsiNTWsWwYwKBgENaeTNOCHV2km/ysXQSEIhKbtlAMQPsgWHCt/bzHlF9m18izb1LrJyjzcSsd+Zy78R+pv4G50Q27c3e/DFPz/wYxN/yHWRbyLBA8ipJbCtMtPEdS9krpmN6cChIdLGbz4CVUqOPSRzNb9lhhgPCcCNRq6DG3HBceb1Se9VnO3zk alipayPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApFQKccMq+wPoWh93DcX3XYrnT7FJ3gntJA/jEwgk6Jd3iEVS2CyUCCgFVcQn8xjXT81YbZHYvoC50IBuu+A+Ey+J8VIgsBm5g9uwbOLRf66GrZjuKOlalHm5gHXjcL2gZRMBJEStOxstcO2YQriqhQzdL3EKp+HQc9u14IOVFpZdR8qq1l7CzKHn1vQo/1fUPfUrTLQqSuQTU9Ospv/QHFqOJA7DPetUQ+jnaZ082f3clr4ITw4EE8A6IWqTXcETTx5j/udCGP84g2Y4j+8i9DqYGyD5ePVgt4G0ICBX1bi1qNlylfxRg8Y3c1DFrRGyr0NpKQxSVXkYaVNvrCoudQIDAQAB returnUrl: http://yxep7y.natappfree.cc/alipay/return notifyUrl: http://yxep7y.natappfree.cc/alipay/notifyspring: thymeleaf: prefix: classpath:/templates/ suffix: .html mode: HTML5 encoding: UTF-8
3.AlipayProperties
@Data@Slf4j@ConfigurationProperties(prefix = 34;pay.alipay")public class AlipayProperties { / 支付宝gatewayUrl / private String gatewayUrl; / 商户运用id / private String appid; / RSA私钥,用于对商户要求报文加签 / private String appPrivateKey; / 支付宝RSA公钥,用于验签支付宝应答 / private String alipayPublicKey; / 署名类型 / private String signType = "RSA2"; / 格式 / private String formate = "json"; / 编码 / private String charset = "UTF-8"; / 同步地址 / private String returnUrl; / 异步地址 / private String notifyUrl; / 最大查询次数 / private static int maxQueryRetry = 5; / 查询间隔(毫秒) / private static long queryDuration = 5000; / 最大撤销次数 / private static int maxCancelRetry = 3; / 撤销间隔(毫秒) / private static long cancelDuration = 3000; private AlipayProperties() {} / PostContruct是spring框架的表明,在方法上加该表明会在项目启动的时候实行该方法,也可以理解为在spring容器初始化的时候实行该方法。 / @PostConstruct public void init() { log.info(description()); } public String description() { StringBuilder sb = new StringBuilder("\nConfigs{"); sb.append("支付宝网关: ").append(gatewayUrl).append("\n"); sb.append(", appid: ").append(appid).append("\n"); sb.append(", 商户RSA私钥: ").append(getKeyDescription(appPrivateKey)).append("\n"); sb.append(", 支付宝RSA公钥: ").append(getKeyDescription(alipayPublicKey)).append("\n"); sb.append(", 署名类型: ").append(signType).append("\n"); sb.append(", 查询重试次数: ").append(maxQueryRetry).append("\n"); sb.append(", 查询间隔(毫秒): ").append(queryDuration).append("\n"); sb.append(", 撤销考试测验次数: ").append(maxCancelRetry).append("\n"); sb.append(", 撤销重试间隔(毫秒): ").append(cancelDuration).append("\n"); sb.append("}"); return sb.toString(); } private String getKeyDescription(String key) { int showLength = 6; if (StringUtils.isNotEmpty(key) && key.length() > showLength) { return new StringBuilder(key.substring(0, showLength)).append("") .append(key.substring(key.length() - showLength)).toString(); } return null; }}
4.AlipayConfiguration
@Configuration@EnableConfigurationProperties(AlipayProperties.class)public class AlipayConfiguration { @Autowired private AlipayProperties properties; @Bean public AlipayTradeService alipayTradeService() { return new AlipayTradeServiceImpl.ClientBuilder() .setGatewayUrl(properties.getGatewayUrl()) .setAppid(properties.getAppid()) .setPrivateKey(properties.getAppPrivateKey()) .setAlipayPublicKey(properties.getAlipayPublicKey()) .setSignType(properties.getSignType()) .build(); } @Bean public AlipayClient alipayClient(){ return new DefaultAlipayClient(properties.getGatewayUrl(), properties.getAppid(), properties.getAppPrivateKey(), properties.getFormate(), properties.getCharset(), properties.getAlipayPublicKey(), properties.getSignType()); }}
5. AlipayWAPPayController
/ 支付宝-手机网站支付. <p> 手机网站支付 @author Mengday Zhang @version 1.0 @since 2018/6/11 /@Slf4j@Controller@RequestMapping("/alipay/wap")public class AlipayWAPPayController { @Autowired private AlipayProperties alipayProperties; @Autowired private AlipayClient alipayClient; / 去支付 支付宝返回一个form表单,并自动提交,跳转到支付宝页面 @param response @throws Exception / @PostMapping("/alipage") public void gotoPayPage(HttpServletResponse response) throws AlipayApiException, IOException { // 订单模型 String productCode="QUICK_WAP_WAY"; AlipayTradeWapPayModel model = new AlipayTradeWapPayModel(); model.setOutTradeNo(UUID.randomUUID().toString()); model.setSubject("支付测试"); model.setTotalAmount("0.01"); model.setBody("支付测试,共0.01元"); model.setTimeoutExpress("2m"); model.setProductCode(productCode); AlipayTradeWapPayRequest wapPayRequest =new AlipayTradeWapPayRequest(); wapPayRequest.setReturnUrl("http://yxep7y.natappfree.cc/alipay/wap/returnUrl"); wapPayRequest.setNotifyUrl(alipayProperties.getNotifyUrl()); wapPayRequest.setBizModel(model); // 调用SDK天生表单, 并直接将完全的表单html输出到页面 String form = alipayClient.pageExecute(wapPayRequest).getBody(); System.out.println(form); response.setContentType("text/html;charset=" + alipayProperties.getCharset()); response.getWriter().write(form); response.getWriter().flush(); response.getWriter().close(); } / 支付宝页面跳转同步关照页面 @param request @return @throws UnsupportedEncodingException @throws AlipayApiException / @RequestMapping("/returnUrl") public String returnUrl(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException, AlipayApiException { response.setContentType("text/html;charset=" + alipayProperties.getCharset()); //获取支付宝GET过来反馈信息 Map<String,String> params = new HashMap<>(); Map requestParams = request.getParameterMap(); for (Iterator 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] + ","; } //乱码办理,这段代码在涌现乱码时利用。如果mysign和sign不相等也可以利用这段代码转化 valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8"); params.put(name, valueStr); } boolean verifyResult = AlipaySignature.rsaCheckV1(params, alipayProperties.getAlipayPublicKey(), alipayProperties.getCharset(), "RSA2"); if(verifyResult){ //验证成功 //请在这里加上商户的业务逻辑程序代码,如保存支付宝交易号 //商户订单号 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"); return "wapPaySuccess"; }else{ return "wapPayFail"; } } / 退款 @param orderNo 商户订单号 @return / @PostMapping("/refund") @ResponseBody public String refund(String orderNo) throws AlipayApiException { AlipayTradeRefundRequest alipayRequest = new AlipayTradeRefundRequest(); AlipayTradeRefundModel model=new AlipayTradeRefundModel(); // 商户订单号 model.setOutTradeNo(orderNo); // 退款金额 model.setRefundAmount("0.01"); // 退款缘故原由 model.setRefundReason("无情由退货"); // 退款订单号(同一个订单可以分多次部分退款,当分多次时必传)// model.setOutRequestNo(UUID.randomUUID().toString()); alipayRequest.setBizModel(model); AlipayTradeRefundResponse alipayResponse = alipayClient.execute(alipayRequest); System.out.println(alipayResponse.getBody()); return alipayResponse.getBody(); } / 退款查询 @param orderNo 商户订单号 @param refundOrderNo 要求退延接口时,传入的退款要求号,如果在退款要求时未传入,则该值为创建交易时的外部订单号 @return @throws AlipayApiException / @GetMapping("/refundQuery") @ResponseBody public String refundQuery(String orderNo, String refundOrderNo) throws AlipayApiException { AlipayTradeFastpayRefundQueryRequest alipayRequest = new AlipayTradeFastpayRefundQueryRequest(); AlipayTradeFastpayRefundQueryModel model=new AlipayTradeFastpayRefundQueryModel(); model.setOutTradeNo(orderNo); model.setOutRequestNo(refundOrderNo); alipayRequest.setBizModel(model); AlipayTradeFastpayRefundQueryResponse alipayResponse = alipayClient.execute(alipayRequest); System.out.println(alipayResponse.getBody()); return alipayResponse.getBody(); } / 关闭交易 @param orderNo @return @throws AlipayApiException / @PostMapping("/close") @ResponseBody public String close(String orderNo) throws AlipayApiException { AlipayTradeCloseRequest alipayRequest = new AlipayTradeCloseRequest(); AlipayTradeCloseModel model =new AlipayTradeCloseModel(); model.setOutTradeNo(orderNo); alipayRequest.setBizModel(model); AlipayTradeCloseResponse alipayResponse= alipayClient.execute(alipayRequest); System.out.println(alipayResponse.getBody()); return alipayResponse.getBody(); }}
6. AlipayController
/ 支付宝通用接口. <p> detailed description @author Mengday Zhang @version 1.0 @since 2018/6/13 /@Slf4j@RestController@RequestMapping("/alipay")public class AlipayController { @Autowired private AlipayProperties aliPayProperties; @Autowired private AlipayTradeService alipayTradeService; / 支付异步关照 https://docs.open.alipay.com/194/103296 / @RequestMapping("/notify") public String notify(HttpServletRequest request) throws AlipayApiException, UnsupportedEncodingException { // 一定要验签,防止黑客修改参数 Map<String, String[]> parameterMap = request.getParameterMap(); StringBuilder notifyBuild = new StringBuilder("/ alipay notify /\n"); parameterMap.forEach((key, value) -> notifyBuild.append(key + "=" + value[0] + "\n") ); log.info(notifyBuild.toString()); // https://docs.open.alipay.com/54/106370 // 获取支付宝POST过来反馈信息 Map<String,String> params = new HashMap<>(); Map requestParams = request.getParameterMap(); for (Iterator 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] + ","; } params.put(name, valueStr); } boolean flag = AlipaySignature.rsaCheckV1(params, aliPayProperties.getAlipayPublicKey(), aliPayProperties.getCharset(), aliPayProperties.getSignType()); if (flag) { / TODO 须要严格按照如下描述校验关照数据的精确性 商户须要验证该关照数据中的out_trade_no是否为商户系统中创建的订单号, 并判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额), 同时须要校验关照中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email), 上述有任何一个验证不通过,则表明本次关照是非常关照,务必忽略。 在上述验证通过后商户必须根据支付宝不同类型的业务关照,精确的进行不同的业务处理,并且过滤重复的关照结果数据。 在支付宝的业务关照中,只有交易关照状态为TRADE_SUCCESS或TRADE_FINISHED时,支付宝才会认定为买家付款成功。 / //交易状态 String tradeStatus = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"),"UTF-8"); // TRADE_FINISHED(表示交易已经成功结束,并不能再对该交易做后续操作); // TRADE_SUCCESS(表示交易已经成功结束,可以对该交易做后续操作,如:分润、退款等); if(tradeStatus.equals("TRADE_FINISHED")){ //判断该笔订单是否在商户网站中已经做过处理 //如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细, // 并判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),并实行商户的业务程序 //请务必判断要求时的total_fee、seller_id与关照时获取的total_fee、seller_id为同等的 //如果有做过处理,不实行商户的业务程序 //把稳: //如果签约的是可退款协议,退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态关照 //如果没有签约可退款协议,那么付款完成后,支付宝系统发送该交易状态关照。 } else if (tradeStatus.equals("TRADE_SUCCESS")){ //判断该笔订单是否在商户网站中已经做过处理 //如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细, // 并判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),并实行商户的业务程序 //请务必判断要求时的total_fee、seller_id与关照时获取的total_fee、seller_id为同等的 //如果有做过处理,不实行商户的业务程序 //把稳: //如果签约的是可退款协议,那么付款完成后,支付宝系统发送该交易状态关照。 } return "success"; } return "fail"; } / 订单查询(最紧张用于查询订单的支付状态) @param orderNo 商户订单号 @return / @GetMapping("/query") public String query(String orderNo){ AlipayTradeQueryRequestBuilder builder = new AlipayTradeQueryRequestBuilder() .setOutTradeNo(orderNo); AlipayF2FQueryResult result = alipayTradeService.queryTradeResult(builder); switch (result.getTradeStatus()) { case SUCCESS: log.info("查询返回该订单支付成功: )"); AlipayTradeQueryResponse resp = result.getResponse(); log.info(resp.getTradeStatus());// log.info(resp.getFundBillList()); break; case FAILED: log.error("查询返回该订单支付失落败!!!"); break; case UNKNOWN: log.error("系统非常,订单支付状态未知!!!"); break; default: log.error("不支持的交易状态,交易返回非常!!!"); break; } return result.getResponse().getBody(); }}
7. WebMvcConfiguration
通过访问http://localhost:8080/toPay来跳转到toPay.html页面
@Configurationpublic class WebMvcConfiguration extends WebMvcConfigurationSupport { @Override protected void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/toPay").setViewName("toPay"); super.addViewControllers(registry); }}
8. templates
toPay.html
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body style="font-size: 30px"><form method="post" action="/alipay/wap/alipage"> <h3>购买商品:越南新娘</h3> <h3>价格:20000</h3> <h3>数量:2个</h3> <button style="width: 100%; height: 60px; alignment: center; background: blue" type="submit">支付</button></form></body></html>
wapPaySuccess.html
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body><h1>WAP 支付成功,请及时享用!
欢迎下次再来</h1></body></html>
wapPayFail.html
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body><h2>WAP 支付失落败,请重新支付</h2></body></html>
三:运行结果
首先访问去支付页面:http://localhost:8080/toPay
获取源码关注并私信“支付宝手机网站支付”获取源代码。