pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.17</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.joko</groupId>
<artifactId>H5zfbPay</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>H5zfbPay</name>
<description>H5zfbPay</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.34.0.ALL</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
application.yml
aliconfig: #沙箱环境
APPID: 9021000127602831
#商户私钥(应用私钥),您的PKCS8格式RSA2私钥
PRIVATE_KEY: MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCVhcRpW6bLwxhQV/v34iPIrajQKyOxXqkGlAFdMk3f5mxbWIdvJOx0d+PMByaFtiCOdWSOy2T8EWFqlYKnUzv3p3WovTgfLb2smkdTJbjaWN3MhqlQVzMfCxRK0Ax4mw308HM5YQe44VG7T2xg6mcT2lPhol9caHvVd408iVxZ7QG+fnm+imq+No3InPx8NNKmf0cgcwYmJQ3ovjY8aU7RrhiQpncKUbz7CTTh4oXrDMOP7pDCHAOuWuc8QPLWbKPubw/awX0o3QY0HYWpxZ2963mZRCL84KdTVWwWpG2CYdsEMlv36j4KT1nhoZQ5ONfMDbtPIFtT9x1UEWXqSJe5AgMBAAECggEAYjPX+yaR1vQMrQAMYhLpU60S2Z3rtFuqgcKFXtNiKDKrahcr5EiI8DmpqF/t4hXrLgVWBR769T7pD1qg1AqrsQ0QDsIv8PJWx90QCUEWNnn1OpYa78RFtBbPvg8EkdWshfnCg6YBr2B1EKOqV8C78GMNgnzlSsjoxBvf7Cl/OSOYQAKJ9Aur1gbQ3RGyM0zEviHzsztB06eRAa2896kDggvx6JS7+BVpZIcPAkeGxn6Fb0TJ65A2C0i7qPcC69e4qVwKEzCJHyMO2WpnFct7/W/Pw4icMjfMScQspTcpmETCcMioxcF0yqhQ1+fsZFXoRXas0wdkTaDFmCqjB8oj7QKBgQDeuyMgkYvGQGrltMi2UEdZFCySbVSIv3CBt9Z1VMrLbRM8CzUmkKuxBB+s55zcKk6fNnZ7wUgOHuF7g+aIFB5SpTXm2/CMfVg5lYejBXq1MvCOzZ28JeEMSCHkPLlG+RP35kIrzvRKAKHhZtm20gIynLMRBUAZwxH9UFAK8XGPkwKBgQCr20J/7rJJ6YHv++GNqTsJWRd5rNnEAsvPeuTrLXGE5DNl9DMqTIvQtBza8Z2CCPxai3NsiwGjf9DVBexH+QN2+BtG8JwcubJVcJj8qTzR5qywHveFlvMNpkZUEdOa+AJ5Xc7rk5GuMG/mEVOF8vF1uK1gPM1RMi4ThnN9LgQTAwKBgFQ2d1qaDTohIwnE59qe0cspaIzYj4zArT4ujwR/0clGbW5XYno5WuEsCPnr/I/14xIpmnTCKIdGP8TeoywyRyDCXHX0FKhZwCDNm6IUsUePggrMTWZVABkPsNjF6Nuf01/4jG5Z6l2MjahWpdyM3M3cF4xI6uqSqduYIRtyHJjHAoGASoEX51aYPKrbBitwsXV4fqj9IGbrUarUqPJiZAKjgDVegwjHVcMxqh0rlU/CNSmoHajvNVNeNn+nDXMv6FPwSJdvW+XcCCgJRDk4WfEUyPr0SrZiIKw8WuKHAtNnpXt3tx4QItkrAxVErb09NIkKXTrfDyXM7TKP/y7+yno0kfkCgYA29rZa3Uv5l8JQM3W9ng83K7z60sszeMQ38JkrxRy8I6gNeMSY1U5sAYfGDhNRDLGkm/y1iLr1IwEolPQeRMMTP3PxkgKCAWmxnOPAlXEjIXStACiWzdh3GaPUIB0yzfi7ic5Tm0VbAINdNgcpiirBJHWDgoUW9woldkQwSUbGYw==
#商户公钥(应用公钥)
PUBLIC_KEY: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlYXEaVumy8MYUFf79+IjyK2o0CsjsV6pBpQBXTJN3+ZsW1iHbyTsdHfjzAcmhbYgjnVkjstk/BFhapWCp1M796d1qL04Hy29rJpHUyW42ljdzIapUFczHwsUStAMeJsN9PBzOWEHuOFRu09sYOpnE9pT4aJfXGh71XeNPIlcWe0Bvn55vopqvjaNyJz8fDTSpn9HIHMGJiUN6L42PGlO0a4YkKZ3ClG8+wk04eKF6wzDj+6QwhwDrlrnPEDy1myj7m8P2sF9KN0GNB2FqcWdvet5mUQi/OCnU1VsFqRtgmHbBDJb9+o+Ck9Z4aGUOTjXzA27TyBbU/cdVBFl6kiXuQIDAQAB
#支付宝公钥,对应APPID下的支付宝公钥。
ALIPUBLIC_KEY: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkJkjJYAwwlQyLrbMUaZdct76kE+npfGalua5ux/5WEszMWdDv/LtXC2ey5/7Mr7Ni8A5LRrsZcxfhD09twMpn1HbPM+FKnGVms8U+5gdzWODeVrZTidDaHHayF+GkxSAXlhdmgrTaq6byGc58DCPkczlCArFl4fDb/RUvYPWqiz+BGWZQWxgL/XqBIOIU53vtnL9O19VrMNJa8DXsjMxfWFlyDPolnw+B9G8QVXrp3nnz0qf5upXQVUulBgCzHau45H0alMono3oQFMpHCBuXmjyju3gV2JGzm2ep0kDBOGLu4TjeMC0tHyFsIU0FavyYT66GnIckc4QhljebXtbhwIDAQAB
#沙箱账号:lvthgb5393@sandbox.com 沙箱登录/支付密码:111111
#服务器异步通知页面路径
NOTIFY_URL: http://localhost:8080//notify_payResult
#页面跳转同步通知页面路径
RETURN_YRL:
#用户付款中途退出返回商户网站的地址
QUIT_URL:
#签名方式
SIGN_TYPE: RSA2
#字符编码格式
CHARSET: utf-8
#支付宝网关(注意这是沙箱的网关,正式的网关为:https://openapi.alipay.com/gateway.do)
GATEWAY: https://openapi-sandbox.dl.alipaydev.com/gateway.do
#角色身份
PID: 2088721012173356
#过期时间
TIMEOUT: 60m
H5zfbPayApplication
package com.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class H5zfbPayApplication {
public static void main(String[] args) {
SpringApplication.run(H5zfbPayApplication.class, args);
}
}
config.AlipayProperties
package com.spring.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix="aliconfig")
public class AlipayProperties {
public String APPID;
public String PRIVATE_KEY;
public String PUBLIC_KEY;
public String ALIPUBLIC_KEY;
public String NOTIFY_URL;
public String RETURN_URL;
public String QUIT_URL;
public String SIGN_TYPE;
public String CHARSET;
public String GATEWAY;
public String PID;
public String TIMEOUT;
}
controller.H5payController
package com.spring.controller;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.internal.util.AlipayUtils;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeWapPayRequest;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.api.response.AlipayTradeWapPayResponse;
import com.spring.util.AlipayUtil;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import static com.spring.util.AlipayUtil.staticAliPayProperties;
@Controller
public class H5payController {
//前端传入商品名、支付金额,订单号由系统生成,创建订单
//lvthgb5393@sandbox.com
@GetMapping("/createOrder/{subject}/{amount}")
@ResponseBody
public String createOrder(@PathVariable String amount, @PathVariable String subject) throws AlipayApiException {
AlipayClient alipayClient= AlipayUtil.alipayClient;
AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
//异步接收地址,仅支持http/https,公网可访问
request.setNotifyUrl(staticAliPayProperties.NOTIFY_URL);
//同步跳转地址,仅支持http/https
request.setReturnUrl(staticAliPayProperties.RETURN_URL);
/******************必传参数***************/
JSONObject bizContent = new JSONObject();
//商户订单号,商家自定义,保持唯一性
bizContent.put("out_trade_no",AlipayUtil.getOut_trade_no());
//支付金额,最小值0.01元
bizContent.put("total_amount", amount);
//订单标题,不可使用特殊符号
bizContent.put("subject", subject);
/******可选参数******/
//手机网站支付默认传值FAST_INSTANT_TRADE_PAY
bizContent.put("product_code", "QUICK_WAP_WAY");
//bizContent.put("time_expire", "2022-08-01 22:00:00");
商品明细信息,按需传入
JSONArray goodsDetail = new JSONArray();
JSONObject goods1 = new JSONObject();
goods1.put("goods_id", "1001");
goods1.put("goods_name", "阿萨姆奶茶");
//goods1.put("quantity", 10);
goods1.put("price", 5);
goodsDetail.add(goods1);
bizContent.put("goods_detail", goodsDetail);
扩展信息,按需传入
//JSONObject extendParams = new JSONObject();
//extendParams.put("sys_service_provider_id", "2088511833207846");
//bizContent.put("extend_params", extendParams);
request.setBizContent(bizContent.toString());
AlipayTradeWapPayResponse response = alipayClient.pageExecute(request);
if(response.isSuccess()){
System.out.println("调用成功");
} else {
System.out.println("调用失败");
}
String form = response.getBody();
System.out.println("*********************\n返回结果为:"+form);
return form;
}
//支付结果,异步通知
@RequestMapping(value = "/notify_payResult",method = RequestMethod.POST)
public void notify_payResult(HttpServletResponse response, HttpServletRequest request) throws AlipayApiException, IOException {
//将异步通知中收到的所有参数都存放到map中
Map paramsMap =new HashMap<>();
paramsMap= AlipayUtil.getParameterMap(request);
//调用SDK验证签名
boolean signVerified = AlipaySignature.rsaCheckV1(paramsMap, staticAliPayProperties.ALIPUBLIC_KEY, staticAliPayProperties.CHARSET, staticAliPayProperties.SIGN_TYPE);
if(signVerified){
// TODO 验签成功后,按照支付结果异步通知中的描述,对支付结果中的业务内容进行二次校验,校验成功后在response中返回success并继续商户自身业务处理,校验失败返回failure
response.getWriter().write("success");
response.getWriter().close();
//处理业务逻辑,待续
}else{
// TODO 验签失败则记录异常日志,并在response中返回failure.
response.getWriter().write("success");
response.getWriter().close();
}
}
//通过商家网站唯一订单号 out_trade_no 或支付宝交易号 trade_no 查询对应订单支付情况。
@GetMapping("/queryOrder/{out_trade_no}")
@ResponseBody
public String queryOrder(@PathVariable String out_trade_no) throws AlipayApiException {
AlipayClient alipayClient= AlipayUtil.alipayClient;
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", out_trade_no);
//bizContent.put("trade_no", "2014112611001004680073956707");
request.setBizContent(bizContent.toString());
AlipayTradeQueryResponse response = alipayClient.execute(request);
if(response.isSuccess()){
System.out.println("查询订单--->调用成功");
} else {
System.out.println("查询订单--->调用失败");
}
return response.getBody();
}
//退款,待续
@GetMapping("/createRefund/{out_trade_no}")
@ResponseBody
public String createRefund(@PathVariable String out_trade_no){
return "";
}
}
util.AlipayUtil
package com.spring.util;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.service.schema.util.StringUtil;
import com.spring.config.AlipayProperties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@Component
public class AlipayUtil {
@Autowired
private AlipayProperties aliPayProperties ;
/*
@Autowired 注解不能作用于静态变量,所以借助静态变量 staticAliPayProperties 作为过渡,
并在初始化的方法里面,让 staticAliPayProperties = aliPayProperties;
*/
public static AlipayProperties staticAliPayProperties;
//alipayClient只需要初始化一次,后续调用不同的API都可以使用同一个alipayClient对象。
public static AlipayClient alipayClient;
/**
* 初始化
*/
@PostConstruct
public void init() {
staticAliPayProperties = aliPayProperties;
alipayClient = new DefaultAlipayClient(staticAliPayProperties.getGATEWAY(), staticAliPayProperties.getAPPID(), staticAliPayProperties.getPRIVATE_KEY(), "json", staticAliPayProperties.getCHARSET(), staticAliPayProperties.getALIPUBLIC_KEY(), staticAliPayProperties.getSIGN_TYPE());
}
/**
* 生成商户订单号(25位):时间(精确到毫秒)+3位随机数+5位用户id
*/
public static synchronized String getOut_trade_no() {
//暂时以appid代替
String userId=staticAliPayProperties.APPID;
//时间(精确到毫秒)
DateTimeFormatter ofPattern = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
String localDate = LocalDateTime.now().format(ofPattern);
//3位随机数
String randomNumeric = RandomStringUtils.randomNumeric(3);
//5位用户id
int subStrLength = 5;
int length = userId.length();
String str;
if (length >= subStrLength) {
str = userId.substring(length - subStrLength, length);
} else {
str = String.format("%0" + subStrLength + "d", userId);
}
String orderNum = localDate + randomNumeric + str;
System.out.println("订单号:{"+orderNum+"}");
return orderNum;
}
/**
* 从request中获得参数Map,并返回可读的Map
*
* @param request
* @return
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static Map getParameterMap(HttpServletRequest request) {
// 参数Map
Map properties = request.getParameterMap();
// 返回值Map
Map returnMap = new HashMap();
Iterator entries = properties.entrySet().iterator();
Map.Entry entry;
String name = "";
String value = "";
while (entries.hasNext()) {
entry = (Map.Entry) entries.next();
name = (String) entry.getKey();
Object valueObj = entry.getValue();
if(null == valueObj){
value = "";
}else if(valueObj instanceof String[]){
String[] values = (String[])valueObj;
for(int i=0;i<values.length;i++){
value = values[i] + ",";
}
value = value.substring(0, value.length()-1);
}else{
value = valueObj.toString();
}
returnMap.put(name, value);
}
return returnMap;
}
}