1 支付流程实现
演示案例
主要参考程序员青戈的视频【支付宝沙箱支付快速集成版】 支付宝沙箱支付快速集成版_哔哩哔哩_bilibili
对应的源码在
alipay-demo: 使用支付宝沙箱实现支付功能 - Gitee.com
以下是完整的实现步骤
1.首先导入相关的依赖
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>
<groupId>com.example</groupId>
<artifactId>alipay-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>alipay-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.7.RELEASE</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.22.110.ALL</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.20</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.7.RELEASE</version>
<configuration>
<mainClass>com.example.alipay.AlipayDemoApplication</mainClass>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
如果已有相关依赖,下面的依赖是必须要导入的
XML
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.22.110.ALL</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.20</version>
</dependency>
使用的支付接口,主要进行的是电脑网页支付
官方的接口文档: 统一收单下单并支付页面接口 - 支付宝文档中心
官方的接口内容
java
package com.java.sdk.demo;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.AlipayConfig;
import com.alipay.api.domain.AlipayTradePagePayModel;
import com.alipay.api.domain.ExtUserInfo;
import com.alipay.api.domain.InvoiceKeyInfo;
import com.alipay.api.response.AlipayTradePagePayResponse;
import com.alipay.api.domain.InvoiceInfo;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.domain.ExtendParams;
import com.alipay.api.domain.GoodsDetail;
import com.alipay.api.domain.SubMerchant;
import com.alipay.api.FileItem;
import java.util.Base64;
import java.util.ArrayList;
import java.util.List;
public class AlipayTradePagePay {
public static void main(String[] args) throws AlipayApiException {
// 初始化SDK
AlipayClient alipayClient = new DefaultAlipayClient(getAlipayConfig());
// 构造请求参数以调用接口
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
AlipayTradePagePayModel model = new AlipayTradePagePayModel();
// 设置商户订单号
model.setOutTradeNo("20150320010101001");
// 设置订单总金额
model.setTotalAmount("88.88");
// 设置订单标题
model.setSubject("Iphone6 16G");
// 设置产品码
model.setProductCode("FAST_INSTANT_TRADE_PAY");
// 设置PC扫码支付的方式
model.setQrPayMode("1");
// 设置商户自定义二维码宽度
model.setQrcodeWidth(100L);
// 设置订单包含的商品列表信息
List<GoodsDetail> goodsDetail = new ArrayList<GoodsDetail>();
GoodsDetail goodsDetail0 = new GoodsDetail();
goodsDetail0.setGoodsName("ipad");
goodsDetail0.setAlipayGoodsId("20010001");
goodsDetail0.setQuantity(1L);
goodsDetail0.setPrice("2000");
goodsDetail0.setGoodsId("apple-01");
goodsDetail0.setGoodsCategory("34543238");
goodsDetail0.setCategoriesTree("124868003|126232002|126252004");
goodsDetail0.setShowUrl("http://www.alipay.com/xxx.jpg");
goodsDetail.add(goodsDetail0);
model.setGoodsDetail(goodsDetail);
// 设置订单绝对超时时间
model.setTimeExpire("2016-12-31 10:05:01");
// 设置二级商户信息
SubMerchant subMerchant = new SubMerchant();
subMerchant.setMerchantId("2088000603999128");
subMerchant.setMerchantType("alipay");
model.setSubMerchant(subMerchant);
// 设置业务扩展参数
ExtendParams extendParams = new ExtendParams();
extendParams.setSysServiceProviderId("2088511833207846");
extendParams.setHbFqSellerPercent("100");
extendParams.setHbFqNum("3");
extendParams.setIndustryRefluxInfo("{\"scene_code\":\"metro_tradeorder\",\"channel\":\"xxxx\",\"scene_data\":{\"asset_name\":\"ALIPAY\"}}");
extendParams.setSpecifiedSellerName("XXX的跨境小铺");
extendParams.setRoyaltyFreeze("true");
extendParams.setCardType("S0JP0000");
model.setExtendParams(extendParams);
// 设置商户传入业务信息
model.setBusinessParams("{\"mc_create_trade_ip\":\"127.0.0.1\"}");
// 设置优惠参数
model.setPromoParams("{\"storeIdType\":\"1\"}");
// 设置请求后页面的集成方式
model.setIntegrationType("PCWEB");
// 设置请求来源地址
model.setRequestFromUrl("https://");
// 设置商户门店编号
model.setStoreId("NJ_001");
// 设置商户的原始订单号
model.setMerchantOrderNo("20161008001");
// 设置外部指定买家
ExtUserInfo extUserInfo = new ExtUserInfo();
extUserInfo.setCertType("IDENTITY_CARD");
extUserInfo.setCertNo("362334768769238881");
extUserInfo.setName("李明");
extUserInfo.setMobile("16587658765");
extUserInfo.setMinAge("18");
extUserInfo.setNeedCheckInfo("F");
extUserInfo.setIdentityHash("27bfcd1dee4f22c8fe8a2374af9b660419d1361b1c207e9b41a754a113f38fcc");
model.setExtUserInfo(extUserInfo);
// 设置开票信息
InvoiceInfo invoiceInfo = new InvoiceInfo();
InvoiceKeyInfo keyInfo = new InvoiceKeyInfo();
keyInfo.setTaxNum("1464888883494");
keyInfo.setIsSupportInvoice(true);
keyInfo.setInvoiceMerchantName("ABC|003");
invoiceInfo.setKeyInfo(keyInfo);
invoiceInfo.setDetails("[{\"code\":\"100294400\",\"name\":\"服饰\",\"num\":\"2\",\"sumPrice\":\"200.00\",\"taxRate\":\"6%\"}]");
model.setInvoiceInfo(invoiceInfo);
request.setBizModel(model);
// 第三方代调用模式下请设置app_auth_token
// request.putOtherTextParam("app_auth_token", "<-- 请填写应用授权令牌 -->");
AlipayTradePagePayResponse response = alipayClient.pageExecute(request, "POST");
// 如果需要返回GET请求,请使用
// AlipayTradePagePayResponse response = alipayClient.pageExecute(request, "GET");
String pageRedirectionData = response.getBody();
System.out.println(pageRedirectionData);
if (response.isSuccess()) {
System.out.println("调用成功");
} else {
System.out.println("调用失败");
// sdk版本是"4.38.0.ALL"及以上,可以参考下面的示例获取诊断链接
// String diagnosisUrl = DiagnosisUtils.getDiagnosisUrl(response);
// System.out.println(diagnosisUrl);
}
}
private static AlipayConfig getAlipayConfig() {
String privateKey = "<-- 请填写您的应用私钥,例如:MIIEvQIBADANB ... ... -->";
String alipayPublicKey = "<-- 请填写您的支付宝公钥,例如:MIIBIjANBg... -->";
AlipayConfig alipayConfig = new AlipayConfig();
alipayConfig.setServerUrl("https://openapi.alipay.com/gateway.do");
alipayConfig.setAppId("<-- 请填写您的AppId,例如:2019091767145019 -->");
alipayConfig.setPrivateKey(privateKey);
alipayConfig.setFormat("json");
alipayConfig.setAlipayPublicKey(alipayPublicKey);
alipayConfig.setCharset("UTF-8");
alipayConfig.setSignType("RSA2");
return alipayConfig;
}
}
自身实现的接口
创建一个配置文件
java
alipay.appId=APPID
alipay.appPrivateKey=应用私钥
alipay.alipayPublicKey=支付宝公钥
alipay.notifyUrl=异步发送通知消息的 URL 地址。
alipay.returnUrl=回调地址
相关参数的获取方式
请参考 快速接入 - 支付宝文档中心
具体的沙箱参数 沙箱应用 - 开放平台
APPID
支付宝公钥和应用私钥
notifyUrl
需要借助一个内网穿透的工具
这里使用的是natapp
下载: https://pan.baidu.com/s/1L99Ibawylnck4b4c0NtmXg?pwd=685x
具体的配置可以查看文档
NATAPP1分钟快速新手图文教程 - NATAPP-内网穿透 基于ngrok的国内高速内网映射工具
如何进行内网穿刺
a.配置完免费版的隧道,下载之后,Windows系统进行如下步骤
b.cmd -authtoken= 参数方式运行.
windows ,点击开始->运行->命令行提示符 后进入 natapp.exe的目录
c.运行
java
natapp -authtoken=9ab6b9040a624f40
注意参数输入正确性,不要有多余的空格等!
d.运行成功,都可以得到如下界面:
Tunnel Status Online 代表链接成功
Version 当前客户端版本,如果有新版本,会有提示
Forwarding 当前穿透 网址 或者端口
Web Interface 是本地Web管理界面,可在隧道配置打开或关闭,仅用于web开发测试
Total Connections 总连接数
Avg Conn Time 0.00ms 这里不代表,不代表,不代表 延时,需要注意!
notifyUrl=上图获取的穿透网址+Controller层的接口utl
alipay.returnUrl=回调地址 前端Vue的接口
设置对应的配置类
java
package com.example.alipay.common;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "alipay")
public class AlipayConfig {
private String appId;
private String appPrivateKey;
private String alipayPublicKey;
private String notifyUrl;
private String returnUrl;
}
对应的Controller接口
java
@GetMapping("/pay") // &subject=xxx&traceNo=xxx&totalAmount=xxx
public void pay(AliPay aliPay, HttpServletResponse httpResponse) throws Exception {
//创建交易单
// 1. 创建Client,通用SDK提供的Client,负责调用支付宝的API
AlipayClient alipayClient = new DefaultAlipayClient(GATEWAY_URL, aliPayConfig.getAppId(),
aliPayConfig.getAppPrivateKey(), FORMAT, CHARSET, aliPayConfig.getAlipayPublicKey(), SIGN_TYPE);
// 2. 创建 Request并设置Request参数
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest(); // 发送请求的 Request类
request.setNotifyUrl(aliPayConfig.getNotifyUrl());
JSONObject bizContent = new JSONObject();
bizContent.set("out_trade_no", aliPay.getTraceNo()); // 我们自己生成的订单编号
bizContent.set("total_amount", aliPay.getTotalAmount()); // 订单的总金额
bizContent.set("subject", aliPay.getSubject()); // 支付的名称
bizContent.set("product_code", "FAST_INSTANT_TRADE_PAY"); // 固定配置
request.setBizContent(bizContent.toString());
// 执行请求,拿到响应的结果,返回给浏览器
String form = "";
try {
AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
System.out.println(response); // 在控制台输出响应结果
form = response.getBody(); // 调用SDK生成表单
} catch (AlipayApiException e) {
e.printStackTrace();
}
httpResponse.setContentType("text/html;charset=" + CHARSET);
httpResponse.getWriter().write(form);// 直接将完整的表单html输出到页面
httpResponse.getWriter().flush();
httpResponse.getWriter().close();
}
如何异步通知商家
主要使用的是回调函数
java
@PostMapping("/notify") // 注意这里必须是POST接口
public String payNotify(HttpServletRequest request) throws Exception {
if (request.getParameter("trade_status").equals("TRADE_SUCCESS")) {
System.out.println("=========支付宝异步回调========");
Map<String, String> params = new HashMap<>();
Map<String, String[]> requestParams = request.getParameterMap();
for (String name : requestParams.keySet()) {
params.put(name, request.getParameter(name));
// System.out.println(name + " = " + request.getParameter(name));
}
String outTradeNo = params.get("out_trade_no");
String gmtPayment = params.get("gmt_payment");
String alipayTradeNo = params.get("trade_no");
String sign = params.get("sign");
String content = AlipaySignature.getSignCheckContentV1(params);
boolean checkSignature = AlipaySignature.rsa256CheckContent(content, sign, aliPayConfig.getAlipayPublicKey(), "UTF-8"); // 验证签名
// 支付宝验签
if (checkSignature) {
// 验签通过
System.out.println("交易名称: " + params.get("subject"));
System.out.println("交易状态: " + params.get("trade_status"));
System.out.println("支付宝交易凭证号: " + params.get("trade_no"));
System.out.println("商户订单号: " + params.get("out_trade_no"));
System.out.println("交易金额: " + params.get("total_amount"));
System.out.println("买家在支付宝唯一id: " + params.get("buyer_id"));
System.out.println("买家付款时间: " + params.get("gmt_payment"));
System.out.println("买家付款金额: " + params.get("buyer_pay_amount"));
// 查询订单
/**
* 使用 QueryWrapper 查询订单,根据订单号(outTradeNo)从数据库中查找订单。
* 如果订单存在,更新订单信息,包括支付宝交易号(alipayNo)、支付时间(PayTime)和订单状态(State),将订单状态更新为 已支付。
* 最后返回 success,告诉支付宝服务器回调处理成功。
*/
QueryWrapper<Orders> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("order_id", outTradeNo);
Orders orders = ordersMapper.selectOne(queryWrapper);
if (orders != null) {
orders.setAlipayNo(alipayTradeNo);
orders.setPayTime(new Date());
orders.setState("已支付");
ordersMapper.updateById(orders);
}
}
}
return "success";
}
完整的Controller层代码
java
package com.example.alipay.controller;
import cn.hutool.json.JSONObject;
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.response.AlipayTradePagePayResponse;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.alipay.common.AlipayConfig;
import com.example.alipay.controller.AliPay;
import com.example.alipay.dao.OrdersMapper;
import com.example.alipay.entity.Orders;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
// xjlugv6874@sandbox.com
// 9428521.24 - 30 = 9428491.24 + 30 = 9428521.24
@RestController
@RequestMapping("/alipay")
public class AliPayController {
private static final String GATEWAY_URL = "https://openapi-sandbox.dl.alipaydev.com/gateway.do";
private static final String FORMAT = "JSON";
private static final String CHARSET = "UTF-8";
//签名方式
private static final String SIGN_TYPE = "RSA2";
@Resource
private AlipayConfig aliPayConfig;
@Resource
private OrdersMapper ordersMapper;
@GetMapping("/pay") // &subject=xxx&traceNo=xxx&totalAmount=xxx
public void pay(AliPay aliPay, HttpServletResponse httpResponse) throws Exception {
//创建交易单
// 1. 创建Client,通用SDK提供的Client,负责调用支付宝的API
AlipayClient alipayClient = new DefaultAlipayClient(GATEWAY_URL, aliPayConfig.getAppId(),
aliPayConfig.getAppPrivateKey(), FORMAT, CHARSET, aliPayConfig.getAlipayPublicKey(), SIGN_TYPE);
// 2. 创建 Request并设置Request参数
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest(); // 发送请求的 Request类
request.setNotifyUrl(aliPayConfig.getNotifyUrl());
JSONObject bizContent = new JSONObject();
bizContent.set("out_trade_no", aliPay.getTraceNo()); // 我们自己生成的订单编号
bizContent.set("total_amount", aliPay.getTotalAmount()); // 订单的总金额
bizContent.set("subject", aliPay.getSubject()); // 支付的名称
bizContent.set("product_code", "FAST_INSTANT_TRADE_PAY"); // 固定配置
request.setBizContent(bizContent.toString());
// 执行请求,拿到响应的结果,返回给浏览器
String form = "";
try {
AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
System.out.println(response); // 在控制台输出响应结果
form = response.getBody(); // 调用SDK生成表单
} catch (AlipayApiException e) {
e.printStackTrace();
}
httpResponse.setContentType("text/html;charset=" + CHARSET);
httpResponse.getWriter().write(form);// 直接将完整的表单html输出到页面
httpResponse.getWriter().flush();
httpResponse.getWriter().close();
}
@PostMapping("/notify") // 注意这里必须是POST接口
public String payNotify(HttpServletRequest request) throws Exception {
if (request.getParameter("trade_status").equals("TRADE_SUCCESS")) {
System.out.println("=========支付宝异步回调========");
Map<String, String> params = new HashMap<>();
Map<String, String[]> requestParams = request.getParameterMap();
for (String name : requestParams.keySet()) {
params.put(name, request.getParameter(name));
// System.out.println(name + " = " + request.getParameter(name));
}
String outTradeNo = params.get("out_trade_no");
String gmtPayment = params.get("gmt_payment");
String alipayTradeNo = params.get("trade_no");
String sign = params.get("sign");
String content = AlipaySignature.getSignCheckContentV1(params);
boolean checkSignature = AlipaySignature.rsa256CheckContent(content, sign, aliPayConfig.getAlipayPublicKey(), "UTF-8"); // 验证签名
// 支付宝验签
if (checkSignature) {
// 验签通过
System.out.println("交易名称: " + params.get("subject"));
System.out.println("交易状态: " + params.get("trade_status"));
System.out.println("支付宝交易凭证号: " + params.get("trade_no"));
System.out.println("商户订单号: " + params.get("out_trade_no"));
System.out.println("交易金额: " + params.get("total_amount"));
System.out.println("买家在支付宝唯一id: " + params.get("buyer_id"));
System.out.println("买家付款时间: " + params.get("gmt_payment"));
System.out.println("买家付款金额: " + params.get("buyer_pay_amount"));
// 查询订单
/**
* 使用 QueryWrapper 查询订单,根据订单号(outTradeNo)从数据库中查找订单。
* 如果订单存在,更新订单信息,包括支付宝交易号(alipayNo)、支付时间(PayTime)和订单状态(State),将订单状态更新为 已支付。
* 最后返回 success,告诉支付宝服务器回调处理成功。
*/
QueryWrapper<Orders> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("order_id", outTradeNo);
Orders orders = ordersMapper.selectOne(queryWrapper);
if (orders != null) {
orders.setAlipayNo(alipayTradeNo);
orders.setPayTime(new Date());
orders.setState("已支付");
ordersMapper.updateById(orders);
}
}
}
return "success";
}
}
注意网关地址,需要修改 GATEWAY_URL 为对应沙箱的支付宝网关地址
访问地址
支付之后,会返回相应的支付信息