后端应用对接支付宝需要准备
应用的公钥【需要上传到支付宝】
应用的私钥【自己保留】
支付宝的公钥【支付宝提供】
支付宝官方提供了生成公私钥的工具
https://opendocs.alipay.com/open/291/introduce

引入支付宝的jar包依赖【maven中央仓库中提供】
<!-- https://mvnrepository.com/artifact/com.alipay.sdk/alipay-sdk-java --><dependency><groupId>com.alipay.sdk</groupId><artifactId>alipay-sdk-java</artifactId><version>4.10.218.ALL</version></dependency>
编写alipay的配置文件信息
#支付宝配置alipay:#支付成功的跳转页面,支付成功后用户端跳转页面success_return_url: https://home.net#支付宝通知回调接口,支付成功后的通知接口,要求公网可以访问callback_url: https://callback.net
编写AlipayConfig 封装AlipayClient用于后续发起请求【沙箱环境】,不需要交由spring管理,因为本身是单例设计模式编写
需要准备好【相当于指定应用和支付宝之间的交互协议格式】:
支付宝网关地址(官方查询)
商户的appid (申请开发的时候官方给定的
app的私钥(自己用工具生成的,上传到支付宝)
支付宝的公钥(支付宝官方提供)
签名类型
字符的编码格式
请求的返回值类型
public class AlipayConfig {/*** 支付宝网关地址 TODO*/public static final String PAY_GATEWAY="https://openapi.alipaydev.com/gateway.do";/*** 支付宝 APPID TODO*/public static final String APPID="2016092000555936";/*** 应用私钥 TODO*/public static final String APP_PRI_KEY = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDh2PCkJ956QB49e3YK/RBAbAcTolDwKNa9qvmKq1LwZ74It+UDkf5YDkcQXAjsLri5A8B1j910ERIyv0UjIZUUu1gNry57vqA8gLHtvgthyRNh3897kqEXL8AxrEW83R/XjZVcVcqd6f2yOBW6N4lPj60qM90WTo/n2+I1wlXY5moRguB8NFoLC8Q1kAzQvjqILUIOjbqodoxcKRVS9RuAduNfE5XzmYFCDnB9RdtWXs10jfLG1pF37FY9RKOvMRNzlo5sH23HmDizHz29dUk9RRHge21hYgECP0hs8FHJBzPt6tIvbDE2Wu1uwQ3Bhrl4pURZ7d0CWr/cPRGaciEBAgMBAAECggEAL1QtKdmJEAl7zNqgpDDgRP/eg8jSOWH1jo3T+bFpkiPLeTxAa/0eVgm37r+6xvQLlgopPPnHKNmi/KuEq5YQDeYsz8FUdm4+Wi+GGhJnhDiFLU+fxX+27or9NeuqOagFUkDDejQoX+t3VO/X7cxRpDCx01RHErOoCKjVwPpWzzslWmVNdIwZFowFJosiHIj1u4Jt72dJ0AXeYoXUvaoKGEc0pb0TUlLXiosksWjYa/gZI7m4hxuzbUT8k0V/+zMxQ3fIJ3BNUPg0eH3HZRI/rZZnOinwxmX5BMRZDzuvm3TNxRO0Tz0EcQE+b3Fzcgnod9L96gAuJSYRjOJmMN2JIQKBgQDyvy/XxytWzwcHxGDq53VLOWzZr81dVz53AMx5y1qhcTXBC4v8s8Bp8yq3xw9v7I3EHVWuKRwRJn8JuTZKJCQONM6p09kHq8hZX3neDJtJg/+CB6hWKtwWGxJpV5GSzmmEE02HC29UZlz0WVOluVU3ETlR78chsKKE5EZkG0HlBQKBgQDuLY3myRAQt7q0RHFw4D5EjsIFZUCooptQgzWzgo5diN+9/ShdzKaKz/ByujXJ+hRX0V6WPknB4AOmp0i03ZD7bajFexOSj0a7CmL7DpRYrR30pA1N29hHh4b609GDwefjVuo2i2oPlkn1pCLnpmuxkV8XHeAL7jdN0Hw7IuOMzQKBgH7jAw4mlPfmdcVQmFyRqlUs6kILzCxbW1J3P2r2qiQzeiazc3QfPZfkPNMdoWse2qfFsbC82mf4mHUrtD4jEBnA7roE+7Av0iUtbBVuv4k5D0kX2Z1Y//wqIICh/n0fmjYopODPUF1suHAddUTuUKXdQfobfRqWKw2OCWFwggxFAoGBAN7Ig4PMrBdHE/+MSaPwTK4p7jfHxtw2BRshL/jx9KJu5gG7K6fZRipvaBSYMrnzDlY6Q8Q7DRiopiGbNbnfyb40i3n0rpEXLWzEwWLd90qe23c6gCtEqR/3F/3EZaRdmr6eTMOqUhG0XQfEeNW/Z9qXBKAF1My0DMzfZ2SVNM09AoGBAIvnGe74eaN+uXFUfDU8NC6lnZJDARU11SzHc3yuOVyl4Sr/dGEytWo4Y5F6XYEsWcO6b+VDMI5qMzXpVIAO51esiapR6R7grVadiExRP1xcx+0wqM3zjqQxH8NrUuxgTFXROzQaOSYOmcZy6BNEqLpyRO4GlXK4TIbvaw47hF8I";/*** 支付宝公钥 TODO*/public static final String ALIPAY_PUB_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk8OPeu/Bhbxr8chgRXnDSm0FDMZoK3r/qLLzqbv+L2/mYTLxHKw5U3c5XPHXar5SVWRp+IgLsZdTfYeZ+jYFmY+dTYk8mG8GXc1uYwHFADcWQAcR4KA3cbxiaubg7g//ECEhwA5CmaosjJ2p8UAz4j7cDB37TDON7oGMFbkXnSBlGhj5oy6rxdUXS+KeG9YmRPkQU3x3ljK37RRLFhwPPRR218IGDCmCtnI5ddKEq1hMAPnYmKNezerxSGGxxIaggt+sDwN1S3GNNSU7AelLPuvNEza49KaQijsBJpaLKUvqt5KO4IVfvPCmRsIFe7KzgdEmjygIY81qXacPzopJIQIDAQAB";/*** 签名类型*/public static final String SIGN_TYPE="RSA2";/*** 字符编码*/public static final String CHARSET="UTF-8";/*** 返回参数格式*/public static final String FORMAT="json";/*** 构造函数私有化*/private AlipayConfig(){}private volatile static AlipayClient instance = null;/*** 单例模式获取, 双重锁校验* @return*/public static AlipayClient getInstance(){if(instance==null){synchronized (AlipayConfig.class){if(instance == null){instance = new DefaultAlipayClient(PAY_GATEWAY,APPID,APP_PRI_KEY,FORMAT,CHARSET,ALIPAY_PUB_KEY,SIGN_TYPE);}}}return instance;}
读取配置文件中的回调url和页面跳转url,并交由spring管理
在项目开发中,通常会用一个单独的配置类来承载配置文件中的常量信息,方便开发过程中的使用。
@Configuration@Datapublic class PayUrlConfig {/*** 支付成功页面跳转*/@Value("${alipay.success_return_url}")private String alipaySuccessReturnUrl;/*** 支付成功,回调通知*/@Value("${alipay.callback_url}")private String alipayCallbackUrl;}
测试接口
测试接口的流程如下
订单基本信息封装
将订单基本信息和回调接口url,页面跳转url封装为AlipayTradeWapPayRequest(AlipayTradeWapPayRequest这个类是支付宝提供的封装入参的request请求)
通过AlipayClient发起请求
判断响应结果
/*** 测试支付方法*/@GetMapping("test_pay")public void testAlipay(HttpServletResponse response) throws AlipayApiException, IOException {//构造订单信息map集合HashMap<String,String> content = new HashMap<>();//商户订单号,64个字符以内、可包含字母、数字、下划线;需保证在商户端不重复String no = UUID.randomUUID().toString();log.info("订单号:{}",no);content.put("out_trade_no", no);content.put("product_code", "FAST_INSTANT_TRADE_PAY");//订单总金额,单位为元,精确到小数点后两位content.put("total_amount", String.valueOf("111.99"));//商品标题/交易标题/订单标题/订单关键字等。 注意:不可使用特殊字符,如 ,=,& 等。content.put("subject", "杯子");//商品描述,可空content.put("body", "好的杯子");// 该笔订单允许的最晚付款时间,逾期将关闭交易。取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)。 该参数数值不接受小数点, 如 1.5h,可转换为 90m。content.put("timeout_express", "5m");//将订单信息和url封装为alipay的requestAlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();request.setBizContent(JSON.toJSONString(content));request.setNotifyUrl(payUrlConfig.getAlipayCallbackUrl());request.setReturnUrl(payUrlConfig.getAlipaySuccessReturnUrl());//向支付宝网关发起请求并收到返回结果AlipayTradeWapPayResponse alipayResponse = AlipayConfig.getInstance().pageExecute(request);//读取返回值信息并响应给用户if(alipayResponse.isSuccess()){System.out.println("调用成功");//返回值form是html形式的表单格式,可以直接写出String form = alipayResponse.getBody();response.setContentType("text/html;charset=UTF-8");response.getWriter().write(form);response.getWriter().flush();response.getWriter().close();} else {System.out.println("调用失败");}}

支付宝回调接口的开发
应用场景:当用户付款成功以后,支付宝会回调我们商户后台的callback()接口,通知我们用户已经付款成功了。

回调接口CallbackController
回调接口一定是可以在公网访问的一个域名。
@Api("订单回调通知模块")@Controller@RequestMapping("/api/callback/order/v1")@Slf4jpublic class CallbackController {@Autowiredprivate ProductOrderService productOrderService;/*** 支付回调通知 post方式* @param request* @param response* @return*/@PostMapping("alipay")public String alipayCallback(HttpServletRequest request, HttpServletResponse response){//将异步通知中收到的所有参数存储到map中Map<String,String> paramsMap = convertRequestParamsToMap(request);log.info("支付宝回调通知结果:{}",paramsMap);//调用SDK验证签名try {boolean signVerified = AlipaySignature.rsaCheckV1(paramsMap, AlipayConfig.ALIPAY_PUB_KEY, AlipayConfig.CHARSET, AlipayConfig.SIGN_TYPE);if(signVerified){//签名验证通过JsonData jsonData = productOrderService.handlerOrderCallbackMsg(ProductOrderPayTypeEnum.ALIPAY,paramsMap);if(jsonData.getCode() == 0){//通知结果确认成功,不然会一直通知,八次都没返回success就认为交易失败return "success";}}} catch (AlipayApiException e) {log.info("支付宝回调验证签名失败:异常:{},参数:{}",e,paramsMap);}return "failure";}/*** 将request中的参数转换成Map* @param request* @return*/private static Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {Map<String, String> paramsMap = new HashMap<>(16);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 size = values.length;if (size == 1) {paramsMap.put(name, values[0]);} else {paramsMap.put(name, "");}}return paramsMap;}}
service层的修改订单状态的业务逻辑
/**** 支付通知结果更新订单状态* @param alipay* @param paramsMap* @return*/@Overridepublic JsonData handlerOrderCallbackMsg(ProductOrderPayTypeEnum payType, Map<String, String> paramsMap) {if(payType.name().equalsIgnoreCase(ProductOrderPayTypeEnum.ALIPAY.name())){//支付宝支付//获取商户订单号String outTradeNo = paramsMap.get("out_trade_no");//交易的状态String tradeStatus = paramsMap.get("trade_status");if("TRADE_SUCCESS".equalsIgnoreCase(tradeStatus) || "TRADE_FINISHED".equalsIgnoreCase(tradeStatus)){//更新订单状态,采用的是cas操作来完成更新productOrderMapper.updateOrderPayState(outTradeNo,ProductOrderStateEnum.PAY.name(),ProductOrderStateEnum.NEW.name());return JsonData.buildSuccess();}} else if(payType.name().equalsIgnoreCase(ProductOrderPayTypeEnum.WECHAT.name())){//微信支付 TODO}return JsonData.buildResult(BizCodeEnum.PAY_ORDER_CALLBACK_NOT_SUCCESS);}

算法训练营永久班授课,欢迎同学们参加呀~
奔跑的小梁,公众号:梁霖编程工具库算法训练营春节价格通知,2023年2月12日
文章转载自奔跑的小梁,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




