1. 备注
Alipay Easy SDK 简化了服务端调用方式,减少支付的开发流程,对于不以交易为重点的项目是非常合适的。
以下只是简单的调用示例,说明多在代码注释中,更多方法如验签、AES加密、退款、对账等可以查看官方文档了解。
GitHub地址 :https://github.com/alipay/alipay-easysdk/tree/master/java
2. 添加maven依赖
<!-- 支付宝支付(必须)-->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-easysdk</artifactId>
<version>2.2.3</version>
</dependency>
<!-- JSON解析器(非必须)-->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
</dependency>
<!-- ZXing二维码(非必须)-->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.4.1</version>
</dependency>
3. 调用支付方法获得付款二维码
/**
* 支付宝付款
*/
@PostMapping("/alipay")
public R alipay(@RequestBody HashMap<String, Object> map) {
//设置参数
Factory.setOptions(getOptions());
try {
//商家订单号
String outTradeNo = IdUtils.fastSimpleUUID();
//支付金额
double totalMoney = 99.99;
//商品名称
String subject = "千里香混沌";
//发起API调用(以创建当面付收款二维码为例)
AlipayTradePrecreateResponse response = Factory.Payment.FaceToFace()
//.asyncNotify("https://www.test.com/callback")//如在此处设置异步通知地址,其优先级高于全局Config中配置的地址
.preCreate(subject, outTradeNo, String.valueOf(totalMoney));
/*
//当SDK的API声明中的参数不满足个性化需求时,可按如下方式追加可选业务参数:
//第一种
List<Object> goodsDetailList = new ArrayList<>();
Map<String, Object> goodsDetail = new HashMap<>();
goodsDetail.put("goods_id", "apple-01");
goodsDetail.put("goods_name", "Apple iPhone11 128G");
goodsDetail.put("quantity", 1);
goodsDetail.put("price", "5799.00");
goodsDetailList.add(goodsDetail);
AlipayTradePrecreateResponse response = Factory.Payment.FaceToFace()
// 调用optional扩展方法,完成可选业务参数(biz_content下的可选字段)的设置
.optional("seller_id", "2088******225135")
.optional("discountable_amount", "8.88")
.optional("goods_detail", goodsDetailList)
.preCreate("Apple iPhone11 128G", "2234567890", "5799.00");
//第二种
Map<String, Object> optionalArgs = new HashMap<>();
optionalArgs.put("seller_id", "2088******225135");
optionalArgs.put("discountable_amount", "8.88");
optionalArgs.put("goods_detail", goodsDetailList);
AlipayTradePrecreateResponse response = Factory.Payment.FaceToFace()
// 也可以调用batchOptional扩展方法,批量设置可选业务参数(biz_content下的可选字段)
.batchOptional(optionalArgs)
.preCreate("Apple iPhone11 128G", "2234567890", "5799.00");
*/
//处理响应或异常
if (ResponseChecker.success(response)) {
log.info("支付宝付款调用成功");
//支付二维码链接
String qrCode = response.getQrCode();
//二维码链接转图片base64
String qrBase64 = QRCodeGenerator.qrToBase64(qrCode);
//一些自定义的订单保存操作
//map.put("outTradeNo", outTradeNo);
//map.put("totalMoney", totalMoney);
//map.put("subject", subject);
//service.insertPayRecord(map);
//返回数据
return R.ok(qrBase64);
} else {
log.info("支付宝付款调用失败,原因:" + response.msg + "," + response.subMsg);
throw new ServiceException("网络异常!");
}
} catch (Exception e) {
log.info("支付宝付款调用失败,原因:" + e.getMessage());
throw new ServiceException("网络异常!");
}
}
//这些参数可配置在yaml中,这里以公钥模式为例,也可改为证书模式
private Config getOptions() {
Config config = new Config();
config.protocol = "https";//协议
config.gatewayHost = gatewayHost;//支付宝网关,沙箱环境为:openapi-sandbox.dl.alipaydev.com
config.signType = "RSA2";//签名方式
config.appId = appId;//应用id
config.merchantPrivateKey = merchantPrivateKey;//应用私钥
config.alipayPublicKey = alipayPublicKey;//支付宝公钥(公钥模式必填)
//config.merchantCertPath = "/foo/appCertPublicKey_2019051064521003.crt";//应用公钥证书文件路径(证书模式必填)
//config.alipayCertPath = "/foo/alipayCertPublicKey_RSA2.crt";//支付宝公钥证书文件路径(证书模式必填)
//config.alipayRootCertPath = "/foo/alipayRootCert.crt";//支付宝根证书文件路径(证书模式必填)
config.notifyUrl = notifyUrl;//回调地址
//config.encryptKey = "aa4BtZ4tspm2wnXLb1ThQA==";//AES密钥,调用AES加解密相关接口时需要(可选)
return config;
}
4. 接收支付结果异步回调
/**
* 支付结果异步回调
*/
@CrossOrigin
@RequestMapping("/callback")
public void callback(HttpServletRequest request) throws Exception {
try {
Map<String, String> params = getRequestParam(request);
if (!Factory.Payment.Common().verifyNotify(params)) {
log.info("支付结果校验失败");
}
String outTradeNo = params.get("out_trade_no");
String tradeStatus = params.get("trade_status");
if (tradeStatus .equals("TRADE_SUCCESS")) {
log.info("支付成功,订单号:" + outTradeNo );
//更新保存的订单信息
//updatePayRecord1(params, outTradeNo );
} else {
log.info("支付失败,订单号:" + outTradeNo );
//更新保存的订单信息
//updatePayRecord2(params, outTradeNo );
}
} catch (Exception e) {
e.printStackTrace();
}
}
//处理得到的参数
private Map<String, String> getRequestParam(final HttpServletRequest request) {
Map<String, String> res = new HashMap<>();
Enumeration<?> temp = request.getParameterNames();
while (temp.hasMoreElements()) {
String en = (String) temp.nextElement();
String value = request.getParameter(en);
res.put(en, value);
}
return res;
}
5. 查询支付状态
/**
* 付款状态同步查询
* 当需要即时了解支付状态时可调用此接口,如扫码未付款、支付成功、支付失败
* 可使用定时器轮询或改造为webscoket
*/
@GetMapping("/payStatus/{outTradeNo}")
public R payStatus(@PathVariable String outTradeNo ) {
AlipayTradeQueryResponse query = null;
try {
Factory.setOptions(getOptions());
query = Factory.Payment.Common().query(outTradeNo );
} catch (Exception e) {
e.printStackTrace();
}
String httpBody = query.getHttpBody();
JSONObject jsonObject = JSONObject.parseObject(httpBody);
JSONObject query_response = jsonObject.getJSONObject("alipay_trade_query_response");
String trade_status = query_response.getString("trade_status");
if (StringUtils.isBlank(trade_status)) {
trade_status = "NO_SCAN_CODE";
}
return R.ok(trade_status);
}
6. 二维码工具类
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.imageio.ImageIO;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
public class QRCodeGenerator {
//二维码链接转图片base64
public static String qrToBase64(String qrStr) {
int width = 300; // 二维码图像宽度
int height = 300; // 二维码图像高度
String format = "png"; // 二维码图像格式
// 设置二维码参数
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
BitMatrix bitMatrix;
try {
bitMatrix = new MultiFormatWriter().encode(qrStr, BarcodeFormat.QR_CODE, width, height, hints);
} catch (WriterException e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
// 将BitMatrix转换为BufferedImage
BufferedImage image = MatrixToImageWriter.toBufferedImage(bitMatrix);
// 将BufferedImage转换为Base64编码的字符串
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
ImageIO.write(image, format, baos);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
byte[] imageBytes = baos.toByteArray();
return Base64.getEncoder().encodeToString(imageBytes);
}
}
7. 获得应用id和密钥
支付宝开放平台:https://open.alipay.com/develop/sandbox/app