1. 导入依赖
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.16.2.ALL</version>
</dependency>
2. 将支付做成一个组件,放入 Spring 容器中
@ConfigurationProperties(prefix = "alipay")
@Component
@Data
public class AlipayTemplate {
//在支付宝创建的应用的id
private String app_id = "2021000117681592";
// 商户私钥,您的PKCS8格式RSA2私钥
private String merchant_private_key = "支付的私钥";
// 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
private String alipay_public_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApbu0Od9GxEh0h4Wd+sKwJDmGl0oNV44UZB/C0qVSOEdLBB8fExM2eNyfXJsh0JxVQNwquxmpCDkarhRP9lDJJvPfFh+u7cUvWVHXp+Ql3jdEX7ojMs3UfNpfKNoD2X7YSbySVPNLI3grGObZgKy3/6uUIBCT2HdMRzp0vtBfBms608wcG0PrV4BNxYsR/ncgLuYE3w98Bco/MKxmDFX+1Knpmyb2jLGZpsVro37lo9YQdqBNXDypwiu7NLrwgNUIgjhsHpwFx2auThUYd2HIBU/i6dj1GjHdjpbuxGOjMyg9AxE/69ya3yQRjSJVWIgk4wkgnufE1eNz+DBZJN93hQIDAQAB";
// 服务器[异步通知]页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
// 支付宝会悄悄的给我们发送一个请求,告诉我们支付成功的信息
// 测试时,可以用内容穿透技术,让支付宝可以回调到我们内网的接口,根据自己实际情况修改
private String notify_url = "http://acd64030bea5.ngrok.io/payed/notify";
// 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
//同步通知,支付成功,一般跳转到成功页
private String return_url = "http://www.member.mall.com/memberOrder.html";
// 签名方式
private String sign_type = "RSA2";
// 字符编码格式
private String charset = "utf-8";
// 自动关单时间
private String timeout = "1m";
// 支付宝网关; https://openapi.alipaydev.com/gateway.do
private String gatewayUrl = "https://openapi.alipaydev.com/gateway.do";
public String pay(PayVo vo) throws AlipayApiException {
//AlipayClient alipayClient = new DefaultAlipayClient(AlipayTemplate.gatewayUrl, AlipayTemplate.app_id, AlipayTemplate.merchant_private_key, "json", AlipayTemplate.charset, AlipayTemplate.alipay_public_key, AlipayTemplate.sign_type);
//1、根据支付宝的配置生成一个支付客户端
AlipayClient alipayClient = new DefaultAlipayClient(gatewayUrl,
app_id, merchant_private_key, "json",
charset, alipay_public_key, sign_type);
//2、创建一个支付请求 //设置请求参数
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
alipayRequest.setReturnUrl(return_url);
alipayRequest.setNotifyUrl(notify_url);
//商户订单号,商户网站订单系统中唯一订单号,必填
String out_trade_no = vo.getOut_trade_no();
//付款金额,必填
String total_amount = vo.getTotal_amount();
//订单名称,必填
String subject = vo.getSubject();
//商品描述,可空
String body = vo.getBody();
// 30分钟内不付款就会自动关单
alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
+ "\"total_amount\":\""+ total_amount +"\","
+ "\"subject\":\""+ subject +"\","
+ "\"body\":\""+ body +"\","
+ "\"timeout_express\":\"" + timeout + "\"," // 这里设置超时时间,主要是要跟订单不支付过期后,支付页面要相应关闭 支付宝的自动收单功能
+ "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
String result = alipayClient.pageExecute(alipayRequest).getBody();
//会收到支付宝的响应,响应的是一个页面,只要浏览器显示这个页面,就会自动来到支付宝的收银台页面
return result;
}
}
PayVo 代码:
@Data
public class PayVo {
/**
* 商户订单号 必填
*/
private String out_trade_no;
/**
* 订单名称 必填
*/
private String subject;
/**
* 付款金额 必填
*/
private String total_amount;
/**
* 商品描述 可空
*/
private String body;
}
处理支付回调处理
- 支付宝支付成功后,同步回调。
通过配置该 url,可以在支付宝支付成功后,跳转到我们需要看的结果页面。但是这种方法可能会有问题,比如,在跳转的过程中,浏览器崩溃,一些支付宝返回的结果数据,如签名数据,返回的订单数据,我们就没办法拿到。此时,系统中,比如订单的状态,我们就无法修改了。支付宝推荐我们使用另一种方式。private String return_url = "http://www.member.mall.com/memberOrder.html";
- 支付成功后,异步回调。
首先,必须保证该url,必须时外网可以访问的地址。而且支付宝会一直尽最大努力通知,也就是每隔一段时间会回调一次,直到告诉支付宝,收到了支付成功的消息。private String notify_url = "http://acd64030bea5.ngrok.io/payed/notify";
参考异步通知说明 封装PayAsyncVo
写Controller 用于处理异步回调请求@Data public class PayAsyncVo { private Date gmt_create; private String charset; private String gmt_payment; private Date notify_time; private String subject; private String sign; private String buyer_id;//支付者的id private String body;//订单的信息 private String invoice_amount;//支付金额 private String version; private String notify_id;//通知id private String fund_bill_list; private String notify_type;//通知类型; trade_status_sync private String out_trade_no;//订单号 private String total_amount;//支付的总额 private String trade_status;//交易状态 TRADE_SUCCESS private String trade_no;//流水号 private String auth_app_id; private String receipt_amount;//商家收到的款 private String point_amount;// private String app_id;//应用id private String buyer_pay_amount;//最终支付的金额 private String sign_type;//签名类型 private String seller_id;//商家的id @Override public String toString() { return "gmt_create --> '" + gmt_create + '\'' + "\ncharset --> '" + charset + '\'' + "\ngmt_payment --> '" + gmt_payment + '\'' + "\nnotify_time --> '" + notify_time + '\'' + "\nsubject --> '" + subject + '\'' + "\nsign --> '" + sign + '\'' + "\nbuyer_id --> '" + buyer_id + '\'' + "\nbody --> '" + body + '\'' + "\ninvoice_amount --> '" + invoice_amount + '\'' + "\nversion --> '" + version + '\'' + "\nnotify_id --> '" + notify_id + '\'' + "\nfund_bill_list --> '" + fund_bill_list + '\'' + "\nnotify_type --> '" + notify_type + '\'' + "\nout_trade_no --> '" + out_trade_no + '\'' + "\ntotal_amount --> '" + total_amount + '\'' + "\ntrade_status --> '" + trade_status + '\'' + "\ntrade_no --> '" + trade_no + '\'' + "\nauth_app_id --> '" + auth_app_id + '\'' + "\nreceipt_amount --> '" + receipt_amount + '\'' + "\npoint_amount --> '" + point_amount + '\'' + "\napp_id --> '" + app_id + '\'' + "\nbuyer_pay_amount --> '" + buyer_pay_amount + '\'' + "\nsign_type --> '" + sign_type + '\'' + "\nseller_id --> '" + seller_id + '\''; } }
@RestController public class OrderPayedListener { @Autowired AlipayTemplate alipayTemplate; @Autowired OrderService orderService; @PostMapping("/payed/notify") public String handleAlipayed(PayAsyncVo payAsyncVo, HttpServletRequest request) throws AlipayApiException, UnsupportedEncodingException { // 只要我们收到了支付宝给我们的异步通知,告诉我们订单支付成功。我们返回success,支付包就不再会通知我们了 // Map<String, String[]> map = request.getParameterMap(); // for (String s : map.keySet()) { // String parameter = request.getParameter(s); // System.out.println("参数名:" + s + "==> 参数值:" + parameter); // } // System.out.println("支付宝通知到位了....数据:" + map); /** * 修改处理之前,要验证签名 */ Map<String,String> params = new HashMap<String,String>(); Map<String,String[]> requestParams = request.getParameterMap(); for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) { String name = (String) iter.next(); String[] values = (String[]) requestParams.get(name); String valueStr = ""; for (int i = 0; i < values.length; i++) { valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ","; } // 乱码解决,这段代码在出现乱码时使用 // valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8"); params.put(name, valueStr); } boolean signVerified = AlipaySignature.rsaCheckV1(params, alipayTemplate.getAlipay_public_key(), alipayTemplate.getCharset(), alipayTemplate.getSign_type()); //调用SDK验证签名 if (signVerified) { System.out.println("签名验证成功"); // 修改订单支付状态 保存交易流水号 String result = orderService.handlePayResult(payAsyncVo); return result; } else { return "error"; } } }
- 收单的处理
在构造请求的过程中,可以设置超时时间。假如付款页面不设置超时时间,相当于任何时候都可以付款。而我们的订单是又时间限制的。如果在30min后,订单关闭了。并且锁定的库存也释放了。此时客户再来付款的时候,付款成功,逻辑上就错误了。因此,在订单自动取消的时间限制内,如果没有付款成功,那么再去付款的时候,设置超时时间,就不能付款了。// 30分钟内不付款就会自动关单 alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\"," + "\"total_amount\":\""+ total_amount +"\"," + "\"subject\":\""+ subject +"\"," + "\"body\":\""+ body +"\"," + "\"timeout_express\":\"" + timeout + "\"," // 这里设置超时时间,主要是要跟订单不支付过期后,支付页面要相应关闭 支付宝的自动收单功能 + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");