s
前言
在开发的过程中,不可避免的都需要使用支付功能,本文档主要给大家分享,自己的一次支付宝开发时的经历.支付宝开发,对于个人开发者而言相对于其他支付来说相对比较友好,因为有沙箱环境支持我们在开发阶段测试使用,话不多说,接下来就介绍开发过程
注册成为开发者
- 官网地址: https://open.alipay.com/platform/home.htm
- 使用支付宝登录即可
- 找到沙箱环境
配置沙箱环境
-
官方参考文档:https://opendocs.alipay.com/open/200/105311
-
沙箱环境配置首页:https://openhome.alipay.com/platform/appDaily.htm 需要登陆后方可进入
-
具体配置步骤:
-
设置秘钥
- (参考链接:https://opendocs.alipay.com/open/291/105971)根据文档生成秘钥对
- 将生成的秘钥中的公钥设置到下方的参数中
-
- 这时我们也能看到支付宝的公钥,回头我们要在配置文件中使用
-
获取 APPID
-
下载沙箱app环境
-
登录账号:支付宝为我们提供了商家账号和卖家账号,也可以设置账户余额(突然感觉自己变有钱了)
到此时我们的沙箱以及沙箱App环境已经准备好了,接下来我们将要编写java后台代码
支付代码
准备工作
-
创建springboot项目
-
引入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- 支付宝依赖 https://mvnrepository.com/artifact/com.alipay.sdk/alipay-sdk-java --> <dependency> <groupId>com.alipay.sdk</groupId> <artifactId>alipay-sdk-java</artifactId> <version>4.10.140.ALL</version> </dependency> <!--常用工具类 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency>
-
设定服务端口:我设置的为8089,大家可以随意设定,也可以不进行配置,默认80
-
创建配置类AliPayConfig,配置属性参数
/** * AliPayConfig * * @author cmm * @date 2020/10/29 9:48 */ public class AliPayConfig { //部分参数这里并没有设定,具体开发时大家可以根据需求自己设定 /* //当面付最大查询次数和查询间隔(毫秒) max_query_retry = 5 query_duration = 5000 //当面付最大撤销次数和撤销间隔(毫秒) max_cancel_retry = 3 cancel_duration = 2000 //交易保障线程第一次调度延迟和调度间隔(秒) heartbeat_delay = 5 heartbeat_duration = 900 */ /** * 支付宝网关名 */ //private static String openApiDomain = "https://openapi.alipay.com/gateway.do"; 正式环境 public static String openApiDomain = "https://openapi.alipaydev.com/gateway.do"; //开发环境 /** * appid 沙箱环境处可以找到我们的APPID */ public static String appId = "自己的APPID"; /** * 应用私钥 */ public static String privateKey = "自己配置沙箱环境秘钥时生成的密钥对中的私钥"; /** * 应用公钥 */ public static String publicKey ="自己配置沙箱环境秘钥时生成的密钥对中的公钥"; /** * 支付宝公钥 */ public static String alipayPublicKey = "配置沙箱秘钥时显示的支付宝公钥"; /** * 签名类型 */ public static String signType = "RSA2"; }
网页支付代码
package com.cmm.alipay.controller;
import com.alibaba.fastjson.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.*;
import com.alipay.api.response.*;
import com.cmm.alipay.config.AliPayConfig;
import com.cmm.alipay.config.DateUtils;
import com.cmm.alipay.config.HttpsUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* AliPayController
*
* @author cmm
* @date 2020/10/29 10:01
*/
@RestController
@RequestMapping("/alipay")
public class AliPayController {
/**创建请求客户端*/
private static AlipayClient alipayClient;
static {
//静态初始化请求客户端,补充加载配置文件中的参数
alipayClient = new DefaultAlipayClient(AliPayConfig.openApiDomain,AliPayConfig.appId,
AliPayConfig.privateKey,"JSON","UTF-8",AliPayConfig.alipayPublicKey,AliPayConfig.signType);
}
/**
* 网页支付创建并下单接口
* 参考API文档链接:https://opendocs.alipay.com/apis/api_1/alipay.trade.page.pay
* 本接口商品信息没有填写,具体参数参考api接口根据需求自由补充即可
* @param response
* @throws AlipayApiException
* @throws IOException
*/
@RequestMapping("/pay")
public void aliPay(HttpServletResponse response) throws AlipayApiException, IOException {
//网页支付页面所需要的请求类
AlipayTradePagePayRequest alipayTradePagePayRequest = new AlipayTradePagePayRequest();
//创建请求参数
Map<String, Object> mapParams = new HashMap<>();
//设置异步回调地址(此地址一般为支付成功后调用该地址处理服务逻辑)
alipayTradePagePayRequest.setNotifyUrl("http://alipay.free.idcfengye.com/alipay/notify");//自己服务的接口地址,要求外网可以访问
//设置同步回调地址,这里我跳转了页面
alipayTradePagePayRequest.setReturnUrl("http://alipay.free.idcfengye.com/paySuccess.html");//自己服务的接口地址,要求外网可以访问
//设置订单编号(商家端,即后台要保证订单编号唯一)
mapParams.put("out_trade_no",System.currentTimeMillis());
//销售产品码
mapParams.put("product_code","FAST_INSTANT_TRADE_PAY");
//设置商品标题
mapParams.put("subject","Iphone111");
//设置商品金额(必须保证小数点后两位,例如88应为88.00)
mapParams.put("total_amount",18+".88");
//设置跳转模式
/**
* 跳转模式。
* 前置模式是将二维码前置到商户
* 的订单确认页的模式。需要商户在
* 自己的页面中以 iframe 方式请求
* 支付宝页面。具体分为以下几种:
* 0:订单码-简约前置模式,对应 iframe 宽度不能小于600px,高度不能小于300px;
* 1:订单码-前置模式,对应iframe 宽度不能小于 300px,高度不能小于600px;
* 3:订单码-迷你前置模式,对应 iframe 宽度不能小于 75px,高度不能小于75px;
* 4:订单码-可定义宽度的嵌入式二维码,商户可根据需要设定二维码的大小。
*
* 跳转模式下,用户的扫码界面是由支付宝生成的,不在商户的域名下。
* 2:订单码-跳转模式
*/
mapParams.put("qr_pay_mode","4");
//设置超时时间
String dateStr = DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss", DateUtils.addMinutes(DateUtils.getNowDate(), 3));
mapParams.put("time_expire",dateStr);
//额外参数,等其他参数参考该接口的api文档根据需求设计即可
//将参数转为json串
String s = JSONObject.toJSONString(mapParams);
//设置请求参数
alipayTradePagePayRequest.setBizContent(s);
//发送请求获取页面信息
AlipayTradePagePayResponse alipayTradePagePayResponse = alipayClient.pageExecute(alipayTradePagePayRequest);
//获取body中的信息
String body = alipayTradePagePayResponse.getBody();
System.out.println(body);
//以页面的形式响应信息
response.setContentType("text/html;charset=utf-8");
response.getWriter().write(body);
response.getWriter().flush();
response.getWriter().close();
}
@RequestMapping("/notify")
public String notify(HttpServletRequest request) {
Map<String, String> paramsMap = convertRequestParamsToMap(request);
System.out.println("支付完成进入异步通知");
String out_trade_no= paramsMap.get("out_trade_no");
String trade_status= paramsMap.get("trade_status");
try {
boolean signVerified = AlipaySignature.rsaCheckV1(paramsMap, AliPayConfig.alipayPublicKey, "UTF-8", AliPayConfig.signType);
//无论同步异步都要验证签名
if(signVerified){
if(trade_status.equals("TRADE_FINISHED") || trade_status.equals("TRADE_SUCCESS")){
//处理自己系统的业务逻辑,如:将支付记录状态改为成功,需要返回一个字符串success告知支付宝服务器
return "异步 success";
} else {
//支付失败不处理业务逻辑
return "failure";
}
}else {
//签名验证失败不处理业务逻辑
return "failure";
}
} catch (AlipayApiException e) {
e.printStackTrace();
return "failure";
}
}
// 将request中的参数转换成Map
private static Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {
Map<String, String> retMap = new HashMap<>();
Set<Map.Entry<String, String[]>> entrySet = request.getParameterMap().entrySet();
for (Map.Entry<String, String[]> entry : entrySet) {
String name = entry.getKey();
String[] values = entry.getValue();
int valLen = values.length;
if (valLen == 1) {
retMap.put(name, values[0]);
} else if (valLen > 1) {
StringBuilder sb = new StringBuilder();
for (String val : values) {
sb.append(",").append(val);
}
retMap.put(name, sb.toString().substring(1));
} else {
retMap.put(name, "");
}
}
return retMap;
}
@RequestMapping("/query")
public void query(HttpServletResponse response) throws AlipayApiException, IOException {
AlipayTradeQueryRequest alipayTradeQueryRequest = new AlipayTradeQueryRequest();
Map<String, Object> mapParams = new HashMap<>();
//设置订单编号
mapParams.put("trade_no","2020102922001475080500830170");
String s = JSONObject.toJSONString(mapParams);
alipayTradeQueryRequest.setBizContent(s);
AlipayTradeQueryResponse alipayTradeQueryResponse = alipayClient.pageExecute(alipayTradeQueryRequest);
String body = alipayTradeQueryResponse.getBody();
System.out.println(body);
response.setContentType("text/html;charset=utf-8");
response.getWriter().write(body);
response.getWriter().flush();
response.getWriter().close();
//2088102180871897
//
}
/**
* 退款接口
* @param response
* @throws AlipayApiException
* @throws IOException
*/
@RequestMapping("/refund")
public void refund(HttpServletResponse response) throws Exception {
AlipayTradeRefundRequest alipayTradeRefundRequest = new AlipayTradeRefundRequest();
Map<String, Object> mapParams = new HashMap<>();
//设置订单编号
mapParams.put("trade_no","2020102922001475080500830170");
mapParams.put("out_request_no","ABCDEF");
mapParams.put("refund_amount","20.00");
mapParams.put("refund_reason","正常退款");
String s = JSONObject.toJSONString(mapParams);
alipayTradeRefundRequest.setBizContent(s);
AlipayTradeRefundResponse alipayTradeRefundResponse = alipayClient.pageExecute(alipayTradeRefundRequest, "GET");
boolean success = alipayTradeRefundResponse.isSuccess();
if(success){
String body = alipayTradeRefundResponse.getBody();
System.out.println(body);
String s1 = HttpsUtils.get(body, null, null);
System.out.println(s1);
}
/**/
//2088102180871897
//
}
}
app支付
- 下载app支付测试工具:https://openclub.alipay.com/club/history/read/7695
- 参考api地址:https://opendocs.alipay.com/apis/api_1/alipay.trade.app.pay
-
-
服务端代码
package com.cmm.alipay.controller; import com.alibaba.fastjson.JSONObject; import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayClient; import com.alipay.api.DefaultAlipayClient; import com.alipay.api.domain.AlipayTradeAppPayModel; import com.alipay.api.domain.AlipayTradePayModel; import com.alipay.api.internal.util.AlipaySignature; import com.alipay.api.request.*; import com.alipay.api.response.*; import com.cmm.alipay.config.AliPayConfig; import com.cmm.alipay.config.DateUtils; import com.cmm.alipay.config.HttpsUtils; import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHintType; import com.google.zxing.MultiFormatWriter; import com.google.zxing.client.j2se.MatrixToImageWriter; import com.google.zxing.common.BitMatrix; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.text.SimpleDateFormat; import java.util.*; /** * AliPayController * * @author cmm * @date 2020/10/29 10:01 */ @RestController @RequestMapping("/aliapp") public class AliPayAppController { private static AlipayClient alipayClient; static { alipayClient = new DefaultAlipayClient(AliPayConfig.openApiDomain,AliPayConfig.appId, AliPayConfig.privateKey,"JSON","UTF-8",AliPayConfig.alipayPublicKey,AliPayConfig.signType); } @RequestMapping("/pay") public String aliPay(HttpServletResponse response) throws AlipayApiException, IOException { AlipayTradeAppPayRequest alipayTradeAppPayRequest = new AlipayTradeAppPayRequest(); Map<String, Object> mapParams = new HashMap<>(); alipayTradeAppPayRequest.setNotifyUrl("http://alipay.free.idcfengye.com/aliapp/notify"); alipayTradeAppPayRequest.setReturnUrl("http://alipay.free.idcfengye.com/paySuccess.html"); //设置订单编号 mapParams.put("out_trade_no",System.currentTimeMillis()); mapParams.put("timeout_express","30m"); //设置支付类型 mapParams.put("subject","app商品"); mapParams.put("total_amount",18+".88"); //设置超时时间 String dateStr = DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss", DateUtils.addMinutes(DateUtils.getNowDate(), 3)); mapParams.put("time_expire",dateStr); String s = JSONObject.toJSONString(mapParams); alipayTradeAppPayRequest.setBizContent(s); AlipayTradeAppPayResponse alipayTradeAppPayResponse = alipayClient.sdkExecute(alipayTradeAppPayRequest); String body = alipayTradeAppPayResponse.getBody(); System.out.println(body); return body; } }
-
调用接口响应如下:
alipay_sdk=alipay-sdk-java-dynamicVersionNo&app_id=2016102400750511&biz_content=%7B%22time_expire%22%3A%222020-11-04+23%3A04%3A19%22%2C%22out_trade_no%22%3A1604502079233%2C%22total_amount%22%3A%2218.88%22%2C%22subject%22%3A%22app%E5%95%86%E5%93%81%22%2C%22timeout_express%22%3A%2230m%22%7D&charset=UTF-8&format=JSON&method=alipay.trade.app.pay¬ify_url=http%3A%2F%2Falipay.free.idcfengye.com%2Faliapp%2Fnotify&return_url=http%3A%2F%2Falipay.free.idcfengye.com%2FpaySuccess.html&sign=khruIxseoHq0XT72mcASv16Mp80BaIL5npE%2FkPAjueZN16FKT8Xzw4cTRfMezGtX2zsGKNI1dcGNdkbxfPbyC0WNu55J0JYhRrAtXAuy9OCcFYFzQF1QJNoYCfm7AnowfxiDDDfb4lwfpI3NCJIoCMce%2BwG3SAVnw5956Dm7cvakcfyDwo3WdGU9KhD1QZIm1JOTtdNQ25cuGsnhPPuf7xRs%2FTHxbLpXEwv1jlF0r0KgrCmXn8Q9rC8gCvmsykjjX0qQ%2F4YfmobChAC7zwi0UUMN9ClBZ11oBCu4MLpkxef8qxskncbfNT0DUnypGVi5cu2PSvdATIPxlf0Bt%2BnonA%3D%3D&sign_type=RSA2×tamp=2020-11-04+23%3A01%3A19&version=1.0
-
见上面响应的字符串拷贝到测试客户端中
至此,简单的支付宝支付功能已经完成,关于退款,查询及异步通知等功能,大家可以参考官方
[api文档]: https://opendocs.alipay.com/apis/api_1
如有其它问题也可留言询问!