具体流程可以看支付宝支付文档,之前做过微信支付整个流程,所以大体还是比较类似的,支付宝支付更容易一些。支付宝网页支付返回的是一个form表单,将表单承接,并点击提交即可进入支付宝提供的web支付页面。
一、前端方面
1、通过接口请求支付宝支付 - 返回的是一个 form表单
async getAliPay() { let _params = { orderId: this.apply.id, amount: this.apply.price, name: this.apply.examName } let { data } = await paymentAlipayApi(_params) this.alipayData = data }
2、定义一个div用于承接支付宝返回的 form 表单内容
<div v-html="alipayData" id="alipay"></div>
3、给一个按钮点击支付
<a-button v-if="apply.status === 'apply'" type="danger" size="large" class="w100" @click="aliPay">去支付</a-button> aliPay () { document.querySelector('form[name="punchout_form"]').submit() },
二、后端方面
1、导入依赖
<!--aliPay--> <dependency> <groupId>com.alipay.sdk</groupId> <artifactId>alipay-sdk-java</artifactId> <version>3.3.49.ALL</version> </dependency>
2、controller层声明接口及业务处理
@ApiOperation("支付宝发起订单") @GetMapping(value = "/aliPay", produces = "text/html; charset=utf-8") public String aliPay(@RequestParam Integer orderId, @RequestParam BigDecimal amount, @RequestParam String name) { try { return alipayService.pay(orderId, amount, name, ""); } catch (AlipayApiException e) { log.error(LogUtil.getStack(e)); return null; } }
AlipayService
package com.opengauss.exam.common.payment; import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayClient; import com.alipay.api.DefaultAlipayClient; import com.alipay.api.request.AlipayTradePagePayRequest; import org.springframework.stereotype.Service; import java.math.BigDecimal; @Service public class AlipayService { public String pay(Integer orderId, BigDecimal amount, String service, String desc) throws AlipayApiException { AlipayClient alipayClient = new DefaultAlipayClient( AlipayConfig.gateway_url, AlipayConfig.app_id, AlipayConfig.merchant_private_key, AlipayConfig.format, AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type); //设置请求参数 AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest(); alipayRequest.setReturnUrl(AlipayConfig.return_url + orderId); alipayRequest.setNotifyUrl(AlipayConfig.notify_url); /* out_trade_no 商户订单号,商户网站订单系统中唯一订单号,必填 total_amount 付款金额,必填 subject 订单名称,必填 body 商品描述,可空 */ alipayRequest.setBizContent("{\"out_trade_no\":\""+ orderId +"\"," + "\"total_amount\":\""+ amount +"\"," + "\"subject\":\""+ service +"\"," + "\"body\":\""+ desc +"\"," + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}"); //请求 String result = alipayClient.pageExecute(alipayRequest).getBody(); int index = result.indexOf("<form"); result = "<form target=\"_blank\" " + result.substring(index + 6); return result; } }
AlipayConfig 就是一些密钥之类的配置,具体不一样,这里面主要的就是记录上面需要的配置,具体可看文档,比如:
// 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号 public static String app_id = "***"; // 网页支付 // 商户私钥,您的PKCS8格式RSA2私钥 //1测试 public static String merchant_private_key1 = "***";// 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。 //1测试 public static String alipay_public_key1 = "***";// 服务器异步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 public static String notify_url = "https://***/api/alipayNotify"; // 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 public static String return_url = "https://***/signup"; // 签名方式 public static String sign_type = "RSA2"; // 字符编码格式 public static String charset = "utf-8"; // 字符串格式 public static String format = "json"; // 支付宝网关 public static String gateway_url = "https://openapi.alipay.com/gateway.do";
其中 notify_url 就是支付成功之后,支付宝会给你这个接口发送通知,你可以在这里面去做一些支付成功之后的校验和一些业务处理。
需要注意的是这个接口需要在 SpringSecurity 里允许公开访问。
其中 return_url 就是支付成功之后会跳转到你给的这个路由。
3、异步回调接口处理
@ApiOperation("支付宝回调") @PostMapping("/alipayNotify") public Map alipayNotify(@RequestParam Map<String, String> params) { Map<String, Object> result = new HashMap<>(); result.put("success", false); try { //调用SDK验证签名 boolean signVerified = AlipaySignature.rsaCheckV1( params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type); /* 实际验证过程建议商户务必添加以下校验: 1、需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号, 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额), 3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email) 4、验证app_id是否为该商户本身。 */ if (signVerified) {//验证成功 //商户订单号 String out_trade_no = params.get("out_trade_no"); //支付宝交易号 String trade_no = params.get("trade_no"); //支付宝交易时间 Date gmt_payment = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(params.get("gmt_payment")); //订单金额 BigDecimal total_amount = BigDecimal.valueOf(Double.valueOf(params.get("total_amount"))); //交易状态 String trade_status = params.get("trade_status") == null ? "" : params.get("trade_status"); //if (trade_status.equals("TRADE_FINISHED")) { //判断该笔订单是否在商户网站中已经做过处理 //如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序 //如果有做过处理,不执行商户的业务程序 //注意: //退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知 if (trade_status.equals("TRADE_SUCCESS")) { //判断该笔订单是否在商户网站中已经做过处理 //如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序 //如果有做过处理,不执行商户的业务程序 //注意: //付款完成后,支付宝系统发送该交易状态通知 ExamApply order = examRoomService.getExamApplyById(Integer.parseInt(out_trade_no)); //订单存在并且付款状态未完成 if (order != null) { if (order.getStatus().equals(ExamApplyStatus.APPLY)) { //校验订单金额是否等于支付金额 if (order.getPrice().compareTo(total_amount) == 0) { order.setTradeNo(trade_no); order.setGmtPaymentTime(gmt_payment); order.setStatus(ExamApplyStatus.PAYED); userService.paymentFinished(order); result.put("success", true); } else { log.error("Amount not equals, Order Amount: " + order.getPrice() + ", Alipay Amount: " + total_amount); } } } else { log.error("Order not found: " + params); } } else { log.error("Alipay trade fail: " + trade_status); } } else {//验证失败 //调试用,写文本函数记录程序运行情况是否正常 //String sWord = AlipaySignature.getSignCheckContentV1(params); //AlipayConfig.logResult(sWord); log.error("Alipay signature check fail: " + params); } } catch (Exception e) { log.error(LogUtil.getStack(e)); } return result; }
这个异步回调通知就是支付成功之后支付宝给你设置的回调地址发送的。从里面可以拿到支付的订单的信息,然后通过签名校验,如果校验成功,就可以去做一些我们自己的业务处理。
其中该接口需要返回上述代码 reslult 格式的给支付宝,支付宝收到之后就不会再发生通知,如果没收到或格式不对,就是会继续发通知的。
支付宝支付大体这样,总体流程比微信支付简单一些。