最近在做一个网站,后端采用了SpringBoot,需要集成支付宝进行线上支付,在这个过程中研究了大量支付宝的集成资料,也走了一些弯路,现在总结出来,相信你读完也能轻松集成支付宝支付。
(资料图)
在开始集成支付宝支付之前,我们需要准备一个支付宝商家账户,如果是个人开发者,可以通过注册公司或者让有公司资质的单位进行授权,后续在集成相关API的时候需要提供这些信息。
下面我以电脑网页端在线支付为例,介绍整个从集成、测试到上线的具体流程。
1. 预期效果展示在开始之前我们先看下我们要达到的最后效果,具体如下:
前端点击支付跳转到支付宝界面支付宝界面展示付款二维码用户手机端支付完成支付,支付宝回调开发者指定的url。2. 开发流程2.1 沙盒调试支付宝为我们准备了完善的沙盒开发环境,我们可以先在沙盒环境调试好程序,后续新建好应用并成功上线后,把程序中对应的参数替换为线上参数即可。
1. 创建沙盒应用直接进入 https://open.alipay.com/develop/sandbox/app 创建沙盒应用即可,
这里因为是测试环境,我们就选择系统默认密钥就行了,下面选择公钥模式,另外应用网关地址就是用户完成支付之后,支付宝会回调的url。在开发环境中,我们可以采用内网穿透的方式,将我们本机的端口暴露在某个公网地址上,这里推荐 https://natapp.cn/ ,可以免费注册使用。
2. SpringBoot代码实现在创建好沙盒应用,获取到密钥,APPID,商家账户PID等信息之后,就可以在测试环境开发集成对应的API了。这里我以电脑端支付API为例,介绍如何进行集成。
步骤1, 添加alipay sdk对应的Maven依赖。关于电脑网站支付的详细产品介绍和API接入文档可以参考:https://opendocs.alipay.com/open/repo-0038oa?ref=api 和 https://opendocs.alipay.com/open/270/01didh?ref=api
com.alipay.sdk alipay-sdk-java 4.35.132.ALL
步骤2,添加支付宝下单、支付成功后同步调用和异步调用的接口。这里需要注意,同步接口是用户完成支付后会自动跳转的地址,因此需要是Get请求。异步接口,是用户完成支付之后,支付宝会回调来通知支付结果的地址,所以是POST请求。
@RestController @RequestMapping("/alipay") public class AliPayController { @Autowired AliPayService aliPayService; @PostMapping("/order") public GenericResponse
步骤3,实现Service层代码这里针对上面controller中的三个接口,分别完成service层对应的方法。下面是整个支付的核心流程,其中有些地方需要根据你自己的实际情况进行保存订单到DB或者检查订单状态的操作,这个可以根据实际业务需求进行设计。
public class AliPayService { @Autowired AliPayHelper aliPayHelper; @Resource AlipayConfig alipayConfig; @Transactional(rollbackFor = Exception.class) public GenericResponse
步骤4,实现alipayHelper类。这个类里面对支付宝的接口进行封装。public class AliPayHelper { @Resource private AlipayConfig alipayConfig; //返回数据格式 private static final String FORMAT = "json"; //编码类型 private static final String CHART_TYPE = "utf-8"; //签名类型 private static final String SIGN_TYPE = "RSA2"; /*支付销售产品码,目前支付宝只支持FAST_INSTANT_TRADE_PAY*/ public static final String PRODUCT_CODE = "FAST_INSTANT_TRADE_PAY"; private static AlipayClient alipayClient = null; private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS"); private static final Random random = new Random(); @PostConstruct public void init(){ alipayClient = new DefaultAlipayClient( alipayConfig.getGateway(), alipayConfig.getAppid(), alipayConfig.getPrivateKey(), FORMAT, CHART_TYPE, alipayConfig.getPublicKey(), SIGN_TYPE); }; /*================PC网页支付====================*/ /** * 统一下单并调用支付页面接口 * @param outTradeNo * @param totalAmount * @param subject * @return */ public Map placeOrderAndPayForPCWeb(String outTradeNo, float totalAmount, String subject){ AlipayTradePagePayRequest request = new AlipayTradePagePayRequest(); request.setNotifyUrl(alipayConfig.getNotifyUrl()); request.setReturnUrl(alipayConfig.getReturnUrl()); JSONObject bizContent = new JSONObject(); bizContent.put("out_trade_no", outTradeNo); bizContent.put("total_amount", totalAmount); bizContent.put("subject", subject); bizContent.put("product_code", PRODUCT_CODE); request.setBizContent(bizContent.toString()); AlipayTradePagePayResponse response = null; try { response = alipayClient.pageExecute(request); } catch (AlipayApiException e) { e.printStackTrace(); } Map resultMap = new HashMap<>(); resultMap.put("isSuccess", response.isSuccess()); if(response.isSuccess()){ log.info("调用成功"); log.info(JSON.toJSONString(response)); resultMap.put("body", response.getBody()); } else { log.error("调用失败"); log.error(response.getSubMsg()); resultMap.put("subMsg", response.getSubMsg()); } return resultMap; } /** * 交易订单查询 * @param out_trade_no * @return */ public Map tradeQueryForPCWeb(String out_trade_no){ AlipayTradeQueryRequest request = new AlipayTradeQueryRequest(); JSONObject bizContent = new JSONObject(); bizContent.put("trade_no", out_trade_no); request.setBizContent(bizContent.toString()); AlipayTradeQueryResponse response = null; try { response = alipayClient.execute(request); } catch (AlipayApiException e) { e.printStackTrace(); } Map resultMap = new HashMap<>(); resultMap.put("isSuccess", response.isSuccess()); if(response.isSuccess()){ System.out.println("调用成功"); System.out.println(JSON.toJSONString(response)); resultMap.put("status", response.getTradeStatus()); } else { System.out.println("调用失败"); System.out.println(response.getSubMsg()); resultMap.put("subMsg", response.getSubMsg()); } return resultMap; } /** * 验证签名是否正确 * @param sign * @param content * @return */ public boolean CheckSignIn(String sign, String content){ try { return AlipaySignature.rsaCheck(content, sign, alipayConfig.getPublicKey(), CHART_TYPE, SIGN_TYPE); } catch (AlipayApiException e) { e.printStackTrace(); } return false; } /** * 将异步通知的参数转化为Map * @return */ public Map paramstoMap(HttpServletRequest request) throws UnsupportedEncodingException { Map params = new HashMap(); Map requestParams = request.getParameterMap(); for (Iterator 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); } return params; } }
步骤5,封装config类,用于存放所有的配置属性。@Data @Component @ConfigurationProperties(prefix = "alipay") public class AlipayConfig { private String gateway; private String appid; private String pid; private String privateKey; private String publicKey; private String returnUrl; private String notifyUrl; }
另外需要在application.properties中,准备好上述对应的属性。
# alipay config alipay.gateway=https://openapi.alipaydev.com/gateway.do alipay.appid=your_appidalipay.pid=your_pid alipay.privatekey=your_private_keyalipay.publickey=your_public_keyalipay.returnurl=完成支付后的同步跳转地址 alipay.notifyurl=完成支付后,支付宝会异步回调的地址
3. 前端代码实现前端代码只需要完成两个功能,
根据用户的请求向后端发起支付请求。直接提交返回数据完成跳转。下面的例子中,我用typescript实现了用户点击支付之后的功能,
async function onPositiveClick() { paymentLoading.value = true const { data } = await placeAlipayOrder({ //你的一些请求参数,例如金额等等 }) const div = document.createElement("divform") div.innerHTML = data document.body.appendChild(div) document.forms[0].setAttribute("target", "_blank") document.forms[0].submit() showModal.value = false paymentLoading.value = false }
2.2 创建并上线APP完成沙盒调试没问题之后,我们需要创建对应的支付宝网页应用并上线。
登录 https://open.alipay.com/develop/manage 并选择创建网页应用,
填写应用相关信息:
创建好应用之后,首先在开发设置中,设置好接口加签方式以及应用网关。
注意密钥选择RSA2,其他按照上面的操作指南一步步走即可,注意保管好自己的私钥和公钥。
之后在产品绑定页,绑定对应的API,比如我们这里是PC网页端支付,找到对应的API绑定就可以了。如果第一次绑定,可能需要填写相关的信息进行审核,按需填写即可,一般审核一天就通过了。
最后如果一切就绪,我们就可以把APP提交上线了,上线成功之后,我们需要把下面SpringBoot中的properties替换为线上APP的信息,然后就可以在生产环境调用支付宝的接口进行支付了。
# alipay config alipay.gateway=https://openapi.alipaydev.com/gateway.do alipay.appid=your_appidalipay.pid=your_pid alipay.privatekey=your_private_keyalipay.publickey=your_public_keyalipay.returnurl=完成支付后的同步跳转地址 alipay.notifyurl=完成支付后,支付宝会异步回调的地址
参考:
https://blog.csdn.net/xqnode/article/details/124457790https://blog.51cto.com/u_15754099/5585676https://zhuanlan.zhihu.com/p/596771147https://segmentfault.com/a/1190000041974184欢迎关注公众号【码老思】,只讲最通俗易懂的原创技术干货。