先说支付宝的吧。
第一步:去支付宝新建沙箱应用并申请开通相应权限,也就是测试环境,完成后去https://auth.alipay.com/login/ant_sso_index.htm?goto=https%3A%2F%2Fopenhome.alipay.com%2Fplatform%2FappDaily.htm%3Ftab%3Dinfo查看自己的应用,在这里面会有APPID,支付宝网关等参数,密钥和支付宝公钥按提示生成即可,这些参数在之后代码中都会用到。
第二步:自己服务器的代码部分,在写代码之前,先导入马爸爸为我们准备的SDK,Maven依赖如下,
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>3.4.27.ALL</version>
</dependency>复制代码
然后我们得先知道整个支付流程是怎么样的。按照一般逻辑,前端拿着订单ID请求后台的统一下单接口,此接口整理数据返回信息给前端,前端拿此信息请求支付宝开始支付,支付完成后根据return_url跳转前端页面同步通知,根据notify_url调用服务器后台接口异步通知。
下面进入正题:
统一下单接口:从这里开始才是支付流程的第一步,整理数据给前端用以发起支付请求,这个接口里面需要的参数可以参考官方文档https://docs.open.alipay.com/api(这个是所有API的文档,找自己需要的接口看)。这里重点提一下,return_url和notify_url,return_url:同步跳转路径,是支付完成后前端跳转页面的路径,通常为某个html页面的路径,这个跳转只表示支付完成,意思是整个支付流程完成,并不代表支付成功或者失败。而notify_url为异步通知,一般为后端controller的requestMapping,这个才是真正通知支付成功或者失败的接口,后端要写一个接受支付宝返回信息的接口,下面再讲。这个统一下单接口会根据不同的请求方式会返回不同的信息,GET请求返回的是json或xml,POST则是直接返回一个带订单信息的form表单。前端拿到统一下单接口返回的信息后请求支付即可。
public void alipay(UserEntity userEntity, HttpServletResponse response, Long id) {
try {
//根据订单ID从数据库获取订单信息用于请求支付宝接口的数据封装(这里最好是把当前登录的用户一起传入sql查询,防止查出非本人的订单)
BookingOrderEntity bookingOrderEntity = userDao.getOrderDetail(userEntity.getId(), id);
//封装公共参数
AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig.getGatewayUrl(), alipayConfig.getAppId(),
alipayConfig.getMerchantPrivateKey(), "json", AlipayConfig.charset, alipayConfig.getAlipayPublicKey(),
AlipayConfig.signType);
//创建API对应的request(手机网页支付,APP支付均不同,此处根据自己需求更改)
AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest();
//在公共参数中设置前端同步回跳页面和后台异步通知路径
alipayRequest.setReturnUrl(alipayConfig.getReturnUrl().replace("ID", bookingOrderEntity.getId().toString()));
alipayRequest.setNotifyUrl(alipayConfig.getNotifyUrl());
//填充业务参数,即订单信息
String subject = bookingOrderEntity.getSnapshotHotelName() + "-" + bookingOrderEntity.getBookingNo();
alipayRequest.setBizContent("{" +
" \"out_trade_no\":" + bookingOrderEntity.getBookingNo() + "," + " \"total_amount\":" + bookingOrderEntity.getPayPrice() + "," + " \"subject\":\"" + subject + "\"," + " \"product_code\":\"QUICK_WAP_WAY\"" + " }"); //调用SDK生成表单 String form = alipayClient.pageExecute(alipayRequest).getBody(); response.setContentType("text/html;charset=utf-8"); //直接将完整的表单html输出到页面 response.getWriter().write(form); } catch (AlipayApiException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }复制代码
支付宝参数实体类:我是写成了去配置文件拿信息的,各位也可以直接写成普通类放参数就行。
@Component
@ConfigurationProperties(prefix = "alipay")
public class AlipayConfig {
/**
* 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
*/
@Value("${alipay.appId}")
private String appId;
/**
* 商户私钥,您的PKCS8格式RSA2私钥
*/
@Value("${alipay.merchantPrivateKey}") private String merchantPrivateKey; /** * 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。 */ @Value("${alipay.alipayPublicKey}") private String alipayPublicKey; /** * 服务器异步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 */ @Value("${alipay.notifyUrl}") private String notifyUrl; /** * 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 */ @Value("${alipay.return_url}") private String returnUrl; /** * 支付宝网关 */ @Value("${alipay.gatewayUrl}") private String gatewayUrl; /** * 签名方式 */ public static String signType = "RSA2"; /** * 字符编码格式 */ public static String charset = "utf-8"; public String getAppId() { return appId; } public String getMerchantPrivateKey() { return merchantPrivateKey; } public String getAlipayPublicKey() { return alipayPublicKey; } public String getNotifyUrl() { return notifyUrl; } public String getReturnUrl() { return returnUrl; } public String getGatewayUrl() { return gatewayUrl; } public void setAppId(String appId) { this.appId = appId; } public void setMerchantPrivateKey(String merchantPrivateKey) { this.merchantPrivateKey = merchantPrivateKey; } public void setAlipayPublicKey(String alipayPublicKey) { this.alipayPublicKey = alipayPublicKey; } public void setNotifyUrl(String notifyUrl) { this.notifyUrl = notifyUrl; } public void setReturnUrl(String returnUrl) { this.returnUrl = returnUrl; } public void setGatewayUrl(String gatewayUrl) { this.gatewayUrl = gatewayUrl; } }复制代码
POST返回的信息:是一个自动提交的form表单,前端直接加载即可。
钉钉内网穿透:return_url和notify_url都必须是外网能够访问到的,如果在本地想要测试,可以下载一个钉钉内网穿透工具https://open-doc.dingtalk.com/microapp/debug/ucof2g,这里面以Mac为例讲了怎么使用,windows操作:cd至git克隆下来的目录,有mac和windows两个版本,进入windows的,不需要chmode命令,这个命令是用来改权限的,启动命令改为ding -config=ding.cfg -subdomain=a 8080即可。
打开穿透工具:如下图,a为域名前缀,可自定义,8081为你自己本地后台服务器的端口。
开启成功:如下图,这里好像只能使用http不能用https的,开启成功后只需将notify_url改为: http://a.vaiwan.com:8081/xxx/xxx即可。