1.配置沙箱密钥并获取沙箱账号密码
1)打开支付宝开放平台主页https://open.alipay.com/platform/home.htm并登陆
2)打开https://opendocs.alipay.com/open/291/105971下载密钥生成工具
3)生成密钥(应用私钥就是下面alipay.properties中的merchantPrivateKey)
4)打开https://openhome.alipay.com/platform/appDaily.htm?tab=info设置支付宝公钥
下面是我已经设置好的
将应用公钥复制上去生成的支付宝公钥就是alipay.properties中的alipayPublicKey
5)记住沙箱账号密码
准备工作完成,接下来创建java工程
工程目录结构
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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wl.alipay</groupId>
<artifactId>alipay</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-boot-version>2.0.8.RELEASE</spring-boot-version>
<slf4j-api-version>1.7.5</slf4j-api-version>
<commons-lang-version>3.6</commons-lang-version>
<MainClass>com.wl.alipay.PayApplication</MainClass>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/com.alipay.sdk/alipay-easysdk -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-easysdk</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot-version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j-api-version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring-boot-version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot-version}</version>
<configuration>
<mainClass>${MainClass}</mainClass>
<layout>JAR</layout>
</configuration>
<!-- repackage 生成两个 jar.original -->
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- 指定maven 打包java 版本 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
<!-- maven 编译打包resource 和 java 目录下所有文件 maven默认资源路径是resources -->
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
<include>*.*</include>
</includes>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.*</include>
<include>*.*</include>
</includes>
</resource>
</resources>
</build>
</project>
application.properties
server.port=80
alipay.properties
启动类
package com.wl.alipay;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
/**
* Created by Administrator on 2020/12/9.
*/
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class //不使用数据库
},scanBasePackages = "com.wl")
public class PayApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(PayApplication.class);
app.setWebApplicationType(WebApplicationType.SERVLET);
app.run(args);
}
}
config
package com.wl.alipay.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import java.io.IOException;
import java.util.Properties;
/**
* Created by Administrator on 2020/12/5.
*/
@Configuration
public class AliPayConfig {
private static final Logger logger = LoggerFactory.getLogger(AliPayConfig.class);
@Bean
public Properties aliPayProperties() throws IOException{
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/alipay.properties"));
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
}
package com.wl.alipay.config;
/**
* Created by Administrator on 2020/12/5.
*/
public class AliPayConstant {
public static final String appId = "appId";
public static final String alipayPublicKey = "alipayPublicKey";
public static final String merchantCertPath = "merchantCertPath";
public static final String alipayCertPath = "alipayCertPath";
public static final String alipayRootCertPath = "alipayRootCertPath";
public static final String gatewayHost = "gatewayHost";
public static final String protocol = "protocol";
public static final String signType = "signType";
public static final String merchantPrivateKey = "merchantPrivateKey";
public static final String notifyUrl = "notifyUrl";
public static final String encryptKey = "encryptKey";
public static final String timeoutExpress = "timeoutExpress";
public static final String webReturnUrl = "webReturnUrl";
}
关键的服务类
package com.wl.alipay.service;
//import com.alipay.easysdk.payment.app.models.AlipayTradeAppPayResponse;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.payment.app.models.AlipayTradeAppPayResponse;
import com.alipay.easysdk.payment.common.models.*;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import com.alipay.easysdk.payment.wap.models.AlipayTradeWapPayResponse;
import com.wl.alipay.config.AliPayConstant;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Properties;
/**
* Created by Administrator on 2019/8/2.
*/
@Service
public class AliPayService {
private static final Logger logger = LoggerFactory.getLogger(AliPayService.class);
private Properties aliPayProperties;
@Autowired
public AliPayService(Properties aliPayProperties){
this.aliPayProperties =aliPayProperties;
//设置Factory 全局参数
{
Config config = new Config();
config.appId = aliPayProperties.getProperty(AliPayConstant.appId);
if(StringUtils.isNotBlank(aliPayProperties.getProperty(AliPayConstant.alipayPublicKey))) {
config.alipayPublicKey = aliPayProperties.getProperty(AliPayConstant.alipayPublicKey);
}else{
//如果采用非证书模式,则无需赋值下面的三个证书路径,改为赋值如上的支付宝公钥字符串即可
String merchantCertPath = aliPayProperties.getProperty(AliPayConstant.merchantCertPath);
String alipayCertPath = aliPayProperties.getProperty(AliPayConstant.alipayCertPath);
String alipayRootCertPath = aliPayProperties.getProperty(AliPayConstant.alipayRootCertPath);
if(StringUtils.isNotBlank(merchantCertPath) && StringUtils.isNotBlank(alipayCertPath)
&& StringUtils.isNotBlank(alipayRootCertPath)){
config.merchantCertPath = merchantCertPath;
config.alipayCertPath = merchantCertPath;
config.alipayRootCertPath = alipayRootCertPath;
}else {
logger.error("merchantCertPath:{},alipayCertPath:{},alipayRootCertPath:{}"
,merchantCertPath,alipayCertPath,alipayRootCertPath);
}
}
config.gatewayHost = aliPayProperties.getProperty(AliPayConstant.gatewayHost);
config.protocol = aliPayProperties.getProperty(AliPayConstant.protocol);
config.signType = aliPayProperties.getProperty(AliPayConstant.signType);
config.merchantPrivateKey = aliPayProperties.getProperty(AliPayConstant.merchantPrivateKey);
config.notifyUrl = aliPayProperties.getProperty(AliPayConstant.notifyUrl);
//可设置AES密钥,调用AES加解密相关接口时需要(可选)
config.encryptKey = aliPayProperties.getProperty(AliPayConstant.encryptKey);
Factory.setOptions(config);
}
}
/**
* 对应 alipay.trade.app.pay 接口
* 构造交易数据供商户app到支付宝下单
*/
public AlipayTradeAppPayResponse createAppTradeForm(String subject ,String tradeNo,String totalAmount) throws Exception {
return Factory.Payment.App()
//单独设置超时时间 默认15分钟
.optional("timeout_express",aliPayProperties.getProperty(AliPayConstant.timeoutExpress,"15m"))
.pay(subject,tradeNo,totalAmount);
}
/**
* 对应alipay.trade.page.pay 接口
* 构造交易数据供pc端到支付宝下单
*/
public AlipayTradePagePayResponse createWebTradeForm(String subject ,String tradeNo,String totalAmount,String returnUrl) throws Exception{
return Factory.Payment.Page()
//单独设置超时时间 默认15分钟
.optional("timeout_express",aliPayProperties.getProperty(AliPayConstant.timeoutExpress,"15m"))
.pay(subject,tradeNo,totalAmount, returnUrl);
}
/**
* alipay.trade.wap.pay
* 构造交易数据供wap端到支付宝下单
*/
public AlipayTradeWapPayResponse createWapTradeForm(String subject , String tradeNo, String totalAmount,String quitUrl, String returnUrl) throws Exception{
return Factory.Payment.Wap()
//单独设置超时时间 默认15分钟
.optional("timeout_express",aliPayProperties.getProperty(AliPayConstant.timeoutExpress,"15m"))
.pay(subject,tradeNo,totalAmount,quitUrl ,returnUrl);
}
/**
* 对应alipay.trade.query(统一收单线下交易查询)
*/
public AlipayTradeQueryResponse queryTrade(String tradeNo) throws Exception {
return Factory.Payment.Common().query(tradeNo);
}
/**
* alipay.trade.cancel
*/
public AlipayTradeCancelResponse cancelTrade(String tradeNo) throws Exception{
return Factory.Payment.Common().cancel(tradeNo);
}
/**
* alipay.trade.close(统一收单交易关闭接口)
*/
public AlipayTradeCloseResponse closeTrade(String tradeNo) throws Exception{
return Factory.Payment.Common().close(tradeNo);
}
/**
* alipay.trade.refund(统一收单交易退款接口)
*/
public AlipayTradeRefundResponse refundTrade(String tradeNo,String refundAmount) throws Exception{
return Factory.Payment.Common().refund(tradeNo,refundAmount);
}
/**
* alipay.trade.fastpay.refund.query(统一收单交易退款查询)
*/
public AlipayTradeFastpayRefundQueryResponse refundQuery(String tradeNo,String outRequestNo) throws Exception{
return Factory.Payment.Common().queryRefund(tradeNo,outRequestNo);
}
}
注意以上几个接口只带了几个必要的参数,如果想要设置更多的请求参数,可以使用对应Client的batchOptional或optional方法设置(在下面会讲如何设置复杂的参数)
下面仅以pc端支付测试(使用app端支付测试需要与app开发人员联调)
新建AliPayController
package com.wl.alipay.controller;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import com.wl.alipay.service.AliPayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Created by Administrator on 2020/12/9.
*/
@RestController
@RequestMapping("/api/alipay/")
public class AliPayController {
private AliPayService aliPayService;
@Autowired
public AliPayController(AliPayService aliPayService){
this.aliPayService = aliPayService;
}
@RequestMapping("createWebTrade")
public void createWebTrade(HttpServletResponse response) throws Exception{
String tradeNo = "20150320011541010101";
String subject = "iphone11 128G";
String totalAmount = "0.1";
String returnUrl = "http://localhost/api/alipay/webReturnUrl";
AlipayTradePagePayResponse pagePayResponse = aliPayService.createWebTradeForm(tradeNo,subject,totalAmount,returnUrl);
response.setContentType("text/html;charset=utf-8");
response.getWriter().write(pagePayResponse.getBody());// 直接将完整的表单html输出到页面
response.getWriter().flush();
response.getWriter().close();
}
@RequestMapping(value = "webReturnUrl",method = RequestMethod.GET)
public Object webTradeReturnUrl(HttpServletRequest request){
System.out.println(request.getParameterMap());
return request.getParameterMap();
}
}
访问http://localhost/api/alipay/createWebTrade即可跳到支付页面
支付成功后同步返回数据到http://localhost/api/alipay/webReturnUrl(支付宝推荐以异步回调通知为准)
启动项目
访问http://localhost/api/alipay/createWebTrade 如下
使用之前的沙箱帐户名和密码登陆
输入支付密码并确认付款
最后跳转到后台页面返回数据
测试成功
上面AliPayService中的参数都是alipay-easysdk中必须的参数,众所周知alipay的参数少则几个多则数十个,显然上面的接口是没法满足我们个性化需求的。下面介绍如何设置可选的参数
还是以alipay.trade.page.pay接口为例,参考alipay文档 我们将所有参数封装到对象里面去
参考alipay-sdk-java jar包中 com.alipay.api.domain包下的AlipayTradePagePayModel类
新建注解
package com.wl.alipay.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by Administrator on 2020/12/5.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface NameInMap {
/**
* 支付宝请求参数中可选map中的key 例如 totalAmount 对应 total_amount
*/
String value();
/**
* 数据类型(默认String)
*/
Class type() default String.class;
}
一个请求对象里面有十几个小的对象实在是多
这里只贴CreateWebTradeRequest
package com.wl.alipay.domain;
import com.wl.alipay.annotation.NameInMap;
import java.io.Serializable;
import java.util.List;
/**
* Created by Administrator on 2020/12/8.
*/
public class CreateWebTradeRequest implements Serializable{
/**
* 支付成功后同步跳转的页面,是一个http/https开头的字符串(返回同步参数)
*
*/
private String returnUrl;
/**
* 商户订单号
* 必填
*/
private String tradeNo;
/**
* 销售产品码,与支付宝签约的产品码名称。注:目前电脑支付场景下仅支持FAST_INSTANT_TRADE_PAY
* 必填
*/
@NameInMap(value = "product_code")
private String productCode;
/**
* 订单总金额,单位为元,精确到小数点后两位,取值范围为 [0.01,100000000]。金额不能为0
* 必填
*/
private String totalAmount;
/**
* 订单标题
* 必填
*/
private String subject;
/**
* 绝对超时时间,格式为 yyyy-MM-dd HH:mm:ss
* 可选
*/
@NameInMap(value = "time_expire")
private String timeExpire;
/**
* 订单包含的商品列表信息,json数组格式,其它说明详见商品明细说明
* 可选
*/
@NameInMap(value = "goods_detail",type = List.class)
private List<GoodsDetail> goodsDetail;
/**
* 公用回传参数,如果请求时传递了该参数,则返回给商户时会回传该参数。
* 支付宝会在异步通知时将该参数原样返回。本参数必须进行 UrlEncode 之后才可以发送给支付宝。
* 可选
*/
@NameInMap(value = "passback_params")
private String passbackParams;
/**
* 业务扩展参数 json对象
* 可选
*/
@NameInMap(value = "extend_params",type = ExtendParams.class)
private ExtendParams extendParams;
/**
* 商品类型 0-虚拟类商品,1-实物类商品 注:虚拟类商品不支持使用花呗渠道
* 可选
*/
@NameInMap(value = "goods_type")
private String goodsType;
/**
* 优惠参数 注:仅与支付宝协商后可用
* 可选 maxLength 512
*/
@NameInMap(value = "promo_params")
private String promoParams;
/**
* 描述分账信息,json格式,详见分账参数说明
*/
@NameInMap(value = "royalty_info",type = RoyaltyInfo.class)
private RoyaltyInfo royaltyInfo;
/**
* 间连受理商户信息体,当前只对特殊银行机构特定场景下使用此字段
*/
@NameInMap(value = "sub_merchant",type = SubMerchant.class)
private SubMerchant subMerchant;
/**
* 商户原始订单号,最大长度限制32位
*/
@NameInMap(value = "merchant_order_no")
private String merchantOrderNo;
/**
* 可用渠道,用户只能在指定渠道范围内支付,多个渠道以逗号分割
* 注,与disable_pay_channels互斥
* 渠道列表:https://docs.open.alipay.com/common/wifww7
*/
@NameInMap(value = "enable_pay_channels")
private String enablePayChannels;
/**
* 商户门店编号
*/
@NameInMap(value = "store_id")
private String storeId;
/**
* 禁用渠道,用户不可用指定渠道支付,多个渠道以逗号分割
* 注,与enable_pay_channels互斥
* 渠道列表:https://docs.open.alipay.com/common/wifww7
*/
@NameInMap(value = "disable_pay_channels")
private String disablePayChannels;
/**
*PC扫码支付的方式,支持前置模式和
* 跳转模式。
* 前置模式是将二维码前置到商户
* 的订单确认页的模式。需要商户在
* 自己的页面中以 iframe 方式请求
* 支付宝页面。具体分为以下几种:
* 0:订单码-简约前置模式,对应 iframe 宽度不能小于600px,高度不能小于300px;
* 1:订单码-前置模式,对应iframe 宽度不能小于 300px,高度不能小于600px;
* 3:订单码-迷你前置模式,对应 iframe 宽度不能小于 75px,高度不能小于75px;
* 4:订单码-可定义宽度的嵌入式二维码,商户可根据需要设定二维码的大小。
*
* 跳转模式下,用户的扫码界面是由支付宝生成的,不在商户的域名下。
* 2:订单码-跳转模式
*/
@NameInMap(value = "qr_pay_mode")
private String qrPayMode;
/**
* 商户自定义二维码宽度
* 注:qr_pay_mode=4时该参数生效
*/
@NameInMap(value = "qrcode_width")
private String qrcodeWidth;
/**
* 描述结算信息,json格式,详见结算参数说明
*/
@NameInMap(value = "settle_info",type = SettleInfo.class)
private SettleInfo settleInfo;
/**
* 开票信息
*/
@NameInMap(value = "invoice_info",type = InvoiceInfo.class)
private InvoiceInfo invoiceInfo;
/**
* 签约参数,支付后签约场景使用 与app的SingParams 参数大致相同
*/
@NameInMap(value = "agreement_sign_params",type = AgreementSignParams.class)
private AgreementSignParams agreementSignParams;
/**
* 请求后页面的集成方式。
* 取值范围:
* 1. ALIAPP:支付宝钱包内
* 2. PCWEB:PC端访问
* 默认值为PCWEB。
*/
@NameInMap(value = "integration_type")
private String integrationType;
/**
* 请求来源地址。如果使用ALIAPP的集成方式,用户中途取消支付会返回该地址。
*/
@NameInMap(value = "request_from_url")
private String requestFromUrl;
/**
* 商户传入业务信息,具体值要和支付宝约定,应用于安全,营销等参数直传场景,格式为json格式
* https://opendocs.alipay.com/open/204/fx8ebu#%E9%9B%86%E6%88%90%E6%96%B9%E5%BC%8F
*/
@NameInMap(value = "business_params")
private String businessParams;
/**
* 外部指定买家
* 可选
*/
@NameInMap(value = "ext_user_info",type = ExtUserInfo.class)
private ExtUserInfo extUserInfo;
public String getReturnUrl() {
return returnUrl;
}
public void setReturnUrl(String returnUrl) {
this.returnUrl = returnUrl;
}
public String getTradeNo() {
return tradeNo;
}
public void setTradeNo(String tradeNo) {
this.tradeNo = tradeNo;
}
public String getProductCode() {
return productCode;
}
public void setProductCode(String productCode) {
this.productCode = productCode;
}
public String getTotalAmount() {
return totalAmount;
}
public void setTotalAmount(String totalAmount) {
this.totalAmount = totalAmount;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getTimeExpire() {
return timeExpire;
}
public void setTimeExpire(String timeExpire) {
this.timeExpire = timeExpire;
}
public List<GoodsDetail> getGoodsDetail() {
return goodsDetail;
}
public void setGoodsDetail(List<GoodsDetail> goodsDetail) {
this.goodsDetail = goodsDetail;
}
public String getPassbackParams() {
return passbackParams;
}
public void setPassbackParams(String passbackParams) {
this.passbackParams = passbackParams;
}
public ExtendParams getExtendParams() {
return extendParams;
}
public void setExtendParams(ExtendParams extendParams) {
this.extendParams = extendParams;
}
public String getGoodsType() {
return goodsType;
}
public void setGoodsType(String goodsType) {
this.goodsType = goodsType;
}
public String getPromoParams() {
return promoParams;
}
public void setPromoParams(String promoParams) {
this.promoParams = promoParams;
}
public RoyaltyInfo getRoyaltyInfo() {
return royaltyInfo;
}
public void setRoyaltyInfo(RoyaltyInfo royaltyInfo) {
this.royaltyInfo = royaltyInfo;
}
public SubMerchant getSubMerchant() {
return subMerchant;
}
public void setSubMerchant(SubMerchant subMerchant) {
this.subMerchant = subMerchant;
}
public String getMerchantOrderNo() {
return merchantOrderNo;
}
public void setMerchantOrderNo(String merchantOrderNo) {
this.merchantOrderNo = merchantOrderNo;
}
public String getEnablePayChannels() {
return enablePayChannels;
}
public void setEnablePayChannels(String enablePayChannels) {
this.enablePayChannels = enablePayChannels;
}
public String getStoreId() {
return storeId;
}
public void setStoreId(String storeId) {
this.storeId = storeId;
}
public String getDisablePayChannels() {
return disablePayChannels;
}
public void setDisablePayChannels(String disablePayChannels) {
this.disablePayChannels = disablePayChannels;
}
public String getQrPayMode() {
return qrPayMode;
}
public void setQrPayMode(String qrPayMode) {
this.qrPayMode = qrPayMode;
}
public String getQrcodeWidth() {
return qrcodeWidth;
}
public void setQrcodeWidth(String qrcodeWidth) {
this.qrcodeWidth = qrcodeWidth;
}
public SettleInfo getSettleInfo() {
return settleInfo;
}
public void setSettleInfo(SettleInfo settleInfo) {
this.settleInfo = settleInfo;
}
public InvoiceInfo getInvoiceInfo() {
return invoiceInfo;
}
public void setInvoiceInfo(InvoiceInfo invoiceInfo) {
this.invoiceInfo = invoiceInfo;
}
public AgreementSignParams getAgreementSignParams() {
return agreementSignParams;
}
public void setAgreementSignParams(AgreementSignParams agreementSignParams) {
this.agreementSignParams = agreementSignParams;
}
public String getIntegrationType() {
return integrationType;
}
public void setIntegrationType(String integrationType) {
this.integrationType = integrationType;
}
public String getRequestFromUrl() {
return requestFromUrl;
}
public void setRequestFromUrl(String requestFromUrl) {
this.requestFromUrl = requestFromUrl;
}
public String getBusinessParams() {
return businessParams;
}
public void setBusinessParams(String businessParams) {
this.businessParams = businessParams;
}
public ExtUserInfo getExtUserInfo() {
return extUserInfo;
}
public void setExtUserInfo(ExtUserInfo extUserInfo) {
this.extUserInfo = extUserInfo;
}
}
NameInMap中的value就是对应文档的字段
如果是List集合 NameInMap中需要设置type=List.class
如果是复杂对象需要设置type=Class.class
将对象转换为Map<String,Object>的集合的工具类(Map中的key 就是上面NameInMap中value的值)
package com.wl.alipay.util;
import com.wl.alipay.annotation.NameInMap;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created by Administrator on 2020/12/5.
*/
public class AliPayUtil {
public static Map<String,Object> genBatchOptionals(Object request) throws Exception{
Map<String,Object> optionals = new HashMap<>();
Class<?> clazz = request.getClass();
convertMap(optionals,clazz,request);
return optionals;
}
//只有特定类型的clazz 才能调用此方法,这里没有校验类型
private static void convertMap(Map<String,Object> options,Class clazz,Object target) throws Exception{
//todo 缓存field
Field[] fields = clazz.getDeclaredFields();
for(Field field : fields){
NameInMap nameInMap = field.getAnnotation(NameInMap.class);
if(nameInMap != null){
field.setAccessible(true);
String key = nameInMap.value();
Object value = field.get(target);
if(value != null) {
if (nameInMap.type() == String.class) {
options.put(key, value);
} else if (nameInMap.type() == List.class) { //集合
List<Object> values = (List<Object>) value; //
List<Object> list = new ArrayList<>();
options.put(key,list);
for(Object o : values){
if(isBasicType(o)){ //基本数据类型或者String
list.add(o);
}else{
Map<String, Object> innerOptions = new HashMap<>();
list.add(innerOptions);
convertMap(innerOptions, o.getClass(), o);
}
}
} else {
//对象
Map<String, Object> innerOptions = new HashMap<>();
options.put(key, innerOptions);
convertMap(innerOptions, nameInMap.type(), value);
}
}
}
}
}
//String 也算
private static boolean isBasicType(Object o) {
return o instanceof String || o instanceof Character || o instanceof Integer || o instanceof Long || o instanceof Byte
|| o instanceof Double || o instanceof Float || o instanceof Boolean;
}
}
修改上面createWebTradeForm接口如下
/**
* 对应alipay.trade.page.pay 接口
* 构造交易数据供pc端到支付宝下单
*/
public AlipayTradePagePayResponse createWebTradeForm(CreateWebTradeRequest request) throws Exception{
return Factory.Payment.Page().batchOptional(AliPayUtil.genBatchOptionals(request))
//单独设置超时时间 默认15分钟
.optional("timeout_express",aliPayProperties.getProperty(AliPayConstant.timeoutExpress,"15m"))
.pay(request.getSubject(),request.getTradeNo(),request.getTotalAmount(),
"http://localhost/api/alipay/webReturnUrl");
}
batchOptional就是批量设置请求可选参数的方法
测试genBatchOptionals方法
测试数据
package com.wl.alipay;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wl.alipay.domain.*;
import com.wl.alipay.util.AliPayUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Created by Administrator on 2020/12/10.
*/
public class Test {
public static void main(String[] args) throws Exception{
CreateWebTradeRequest request = new CreateWebTradeRequest();
request.setTimeExpire("2016-12-31 10:05");
{
ExtendParams extendParams = new ExtendParams();
extendParams.setSysServiceProviderId("2088511833207846");
extendParams.setHbFqSellerPercent("100");
extendParams.setHbFqNum("3");
extendParams.setCardType("S0JP0000");
request.setExtendParams(extendParams);
}
{
SettleInfo settleInfo = new SettleInfo();
settleInfo.setSettlePeriodTime("7d");
List<SettleDetailInfo> list = new ArrayList<>();
SettleDetailInfo detailInfo = new SettleDetailInfo();
detailInfo.setAmount("0.1");
detailInfo.setSettleEntityId("2088****;st_001");
detailInfo.setTransInType("cardAliasNo");
detailInfo.setTransIn("A0001");
list.add(detailInfo);
settleInfo.setSettleDetailInfos(list);
request.setSettleInfo(settleInfo);
}
request.setSubject("iphone11");
request.setMerchantOrderNo("20161008001");
{
InvoiceInfo invoiceInfo = new InvoiceInfo();
invoiceInfo.setDetails("invoiceInfo");
{
InvoiceKeyInfo keyInfo = new InvoiceKeyInfo();
keyInfo.setInvoiceMerchantName("merchantName");
keyInfo.setTaxNum("10");
keyInfo.setSupportInvoice(true);
invoiceInfo.setKeyInfo(keyInfo);
}
request.setInvoiceInfo(invoiceInfo);
}
{
RoyaltyInfo royaltyInfo = new RoyaltyInfo();
royaltyInfo.setRoyaltyType("ROYALTY");
{
List<RoyaltyDetailInfos> list = new ArrayList<>();
{
RoyaltyDetailInfos infos = new RoyaltyDetailInfos();
infos.setAmount("0.1");
infos.setSerialNo(1L);
infos.setTransIn("wwwwwww");
list.add(infos);
list.add(infos);
}
royaltyInfo.setRoyaltyDetailInfos(list);
}
request.setRoyaltyInfo(royaltyInfo);
}
{
List<GoodsDetail> list = new ArrayList<>();
GoodsDetail goodsDetail = new GoodsDetail();
goodsDetail.setPrice("15.15");
goodsDetail.setGoodsName("iphone11");
list.add(goodsDetail);
list.add(goodsDetail);
request.setGoodsDetail(list);
}
Map<String,Object> map = AliPayUtil.genBatchOptionals(request);
System.out.println(map);
System.out.println(new ObjectMapper().writeValueAsString(map));
}
}
输出数据(转换为json)
{
"royalty_info": {
"royalty_type": "ROYALTY",
"royalty_detail_infos": [{
"amount": "0.1",
"trans_in": "wwwwwww",
"serial_no": 1
}, {
"amount": "0.1",
"trans_in": "wwwwwww",
"serial_no": 1
}]
},
"invoice_info": {
"key_info": {
"tax_num": "10",
"is_support_invoice": true,
"invoice_merchant_name": "merchantName"
},
"details": "invoiceInfo"
},
"time_expire": "2016-12-31 10:05",
"extend_params": {
"sys_service_provider_id": "2088511833207847",
"hb_fq_seller_percent": "100",
"hb_fq_num": "3",
"card_type": "S0JP0000"
},
"settle_info": {
"settle_period_time": "7d",
"settle_detail_info": [{
"amount": "0.1",
"trans_in": "A0001",
"settle_entity_id": "2088****;st_001",
"trans_in_type": "cardAliasNo"
}]
},
"goods_detail": [{
"goods_name": "iphone11",
"price": "15.15"
}, {
"goods_name": "iphone11",
"price": "15.15"
}],
"merchant_order_no": "20161008001"
}
如果嫌使用对象麻烦,可以直接使用封装好的Map