失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 开发笔记 | Springboot整合多平台支付(微信/支付宝)

开发笔记 | Springboot整合多平台支付(微信/支付宝)

时间:2019-09-01 06:31:34

相关推荐

开发笔记 | Springboot整合多平台支付(微信/支付宝)

目录

微信支付

开发前准备

支付程序编写

(1)创建订单

(2)微信的支付回调

(3)查询订单状态

(4)退款

(5)退款查询与关闭订单

(5)退款回调

alipay-sdk沙箱模拟支付宝支付

官方参考文档

开发前准备

开发

支付测试

整合alipay-easysdk支付宝支付

开发流程

yml配置文件

导入依赖

支付配置类

支付程序编写

网页支付(扫码支付)

App支付

移动端网站支付

支付回调

支付查询

退款

交易取消

其他辅助工具类

简易生成订单号

沙箱模拟时利用natapp内网穿透工具进行异步/同步回调

本篇笔记包含:微信支付,支付宝沙箱环境支付模拟,支付宝简单支付版(easysdk)实现

后续有其他平台会继续更新

微信支付

开发前准备

以小程序支付为例

接入前准备参考官方文档微信支付-开发者文档https://pay./wiki/doc/apiv3/apis/chapter3_5_1.shtml

场景:通过小程序调起支付-》支付完成-》微信支付成功通知

业务流程:

用户创建商户订单-》结合商户订单号,价格等调起微信支付-》完成支付更新商户订单支付1.

1.导入依赖

<!-- 微信支付 SDK--><dependency><groupId>com.github.binarywang</groupId><artifactId>weixin-java-pay</artifactId><version>4.0.0</version></dependency>

2.yml配置基本参数

wxpayconfig:

appId: xxxxxxxx (自己的appid)

mch-id: xxxxxxxx (自己的商户id)

mchKey: xxxxxxxx(自己的api密钥,非appSecret)

appSecret:xxxxxxxx

keyPath: classpath:test/apiclient_cert.p12 (微信商户下载的安全证书存放位置)

notifyUrl: xxxxxxxx (微信支付回调的接口)

refundNotifyUrl:xxxxxxxx

officialAppId: xxxxxxxx (服务号appid)

officialAppSecret: xxxxxxxx (服务号appSecret)

3.config配置类

@Configuration@ConditionalOnClass(WxPayService.class)public class MyWxPayConfig {@Bean@ConditionalOnMissingBeanpublic WxPayService wxService() {WxPayConfig payConfig = new WxPayConfig();payConfig.setAppId("xxxxxxxxx");payConfig.setMchId("xxxxxxxxx");payConfig.setMchKey("xxxxxxxxx");payConfig.setKeyPath("xxxxxxxxx");payConfig.setNotifyUrl("xxxxxxxx"); //微信支付后回调的接口payConfig.setRefundNotifyUrl("xxxxxxxx"); //微信退款后回调的接口payConfig.setTradeType(WxPayConstants.TradeType.JSAPI);payConfig.setUseSandboxEnv(false); //是否使用沙箱支付环境WxPayService wxPayService = new WxPayServiceImpl();wxPayService.setConfig(payConfig);return wxPayService;} }

支付程序编写

公共部分

@RestController@RequestMapping("/wxpay")@Api(value = "微信支付")public class WXPayController{@Autowiredprivate WxPayService wxPayService;......}

(1)创建订单

@ApiOperation(value = "确认支付")@GetMapping(value = "/confirm")public ResponseEntity<WxPayMpOrderResult> confirm(String orderId, HttpServletRequest request) throws Exception {/***orderId 业务系统创建的订单表id*校验业务系统是否存在订单 且订单状态为待支付,不能为已取消,已支付*一些针对业务系统的业务处理 业务订单实体 bizOrder orderNum业务创建的订单号*BizAppUser appUser = appUserService.getById(bizOrder.getAuthId()); //获取当前登录小 *程序用户**/try{//通过redis 杜绝 订单重复提交if(Boolean.toString(true).equals(redisUtils.get("PAY_" + bizOrder.getOrderNum()))) {return ResultUtil.error("订单已提交");}//将订单号存入redis 设置状态为 true表示已提交 5s后过期redisUtils.set("PAY_" + bizOrder.getOrderNum(), "true", 5);WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest = WxPayUnifiedOrderRequest.newBuilder().outTradeNo(bizOrder.getOrderNum()) //订单号.body(bizOrder.getOrderNum()) .totalFee(bizOrder.getAmount())) //支付金额.openid(appUser.getOpenid()) //用户小程序id.spbillCreateIp(getIpAddress(request)).build();//将参数传给微信 进行订单处理WxPayMpOrderResult result = this.wxPayService.createOrder(wxPayUnifiedOrderRequest);//更新支付操作至业务订单表 支付结果通过支付回调更新 业务订单中的支付状态bizOrder.setPayTime(LocalDateTime.now());orderService.updateById(order);}catch(Exception e){log.error("支付失败");return ResultUtil.error("支付失败");}finally{//最后删除 次订单的重复提交限制 标识redisUtils.delete("PAY_" + bizOrder.getOrderNum());}}

(2)微信的支付回调

@ApiOperation(value = "微信支付回调")@PostMapping(value = "/callback")public String wxParentNotifyPage(@RequestBody String xmlData) throws Exception {final WxPayOrderNotifyResult notifyResult = this.wxPayService.parseOrderNotifyResult(xmlData);//获取支付结果后 更新 业务订单表 更新订单支付状态 等业务操作if (WxPayConstants.ResultCode.SUCCESS.equals(notifyResult.getReturnCode()) && WxPayConstants.WxpayTradeStatus.SUCCESS.equals(notifyResult.getReturnCode())) {//更新支付状态BizOrder bizOrder = new BizOrder();bizOrder.setPayStatus("PAID");......Wrappers.lambdaUpdate(BizOrder.class).eq(BizOrder::getOrderNum, notifyResult.getOutTradeNo()));}return WxPayNotifyResponse.success("成功");}

(3)查询订单状态

//查询订单是否支付成功@ApiOperation(value = "查询支付状态")@GetMapping(value = "/queryOrder")public WxPayOrderQueryResult queryOrder(String orderId) throws Exception {BizOrder order = orderService.getById(orderId);Assert.notNull(order, "未查询到订单信息");//第一个参数为流水号 第二个参数为订单号return this.wxPayService.queryOrder(null,order.getOrderNum());}

(4)退款

@ApiOperation(value = "退款")@PostMapping("/refund")public WxPayRefundResult refund(@RequestBody WxPayRefundRequest request) throws WxPayException {return this.wxPayService.refund(request);}

(5)退款查询与关闭订单

//根据微信订单号/商户订单号/商户退款单号/微信退款单号查询退款@GetMapping("/refund/query")public WxPayRefundQueryResult refundQuery(@RequestParam(required = false) String transactionId,@RequestParam(required = false) String outTradeNo,@RequestParam(required = false) String outRefundNo,@RequestParam(required = false) String refundId) throws Exception {return this.wxPayService.refundQuery(transactionId, outTradeNo, outRefundNo, refundId);}//关闭订单@GetMapping("/close")public WxPayOrderCloseResult closeOrder(@RequestParam(required = false) String outTradeNo) throws Exception {return this.wxPayService.closeOrder(outTradeNo);}

(5)退款回调

@ApiOperation(value = "退款回调")@PostMapping("/notify/refund")public String parseRefundNotifyResult(@RequestBody String xmlData) throws WxPayException{final WxPayRefundNotifyResult result = this.wxPayService.parseRefundNotifyResult(xmlData);if(WxPayConstants.RefundStatus.SUCCESS.equals(result.getReqInfo().getRefundStatus())){//更新业务订单表支付状态等业务操作......}return WxPayNotifyResponse.success("成功");}

alipay-sdk沙箱模拟支付宝支付

官方参考文档

alipay.trade.page.pay(统一收单下单并支付页面接口) | API支付宝文档中心/apis/api_1/alipay.trade.page.pay

开发前准备

商户平台

登录 - 支付宝

创建流程​​​​​​创建应用 | 网页&移动应用

【沙箱测试环境流程】本文以沙箱环境模拟

1.用支付宝账号登录【开放控制平台】创建应用获取appid

2.选择沙箱模拟环境

3.沙箱应用-》获取appid(一个appid绑定一个收款支付宝账户)

4.利用开发助手工具生成RSA2密钥

生成密钥 | 开放平台支付宝文档中心/common/02kipl生成,一对RSA 2密钥【应用公钥、应用私钥】以及公钥证书申请 CSR 文件

【公钥】传给支付宝平台

【私钥】配置代码中,签名用

一个密钥与一个应用绑定

5.生成密钥后,进行配置

返回平台-》开发信息-》自定义密钥-》设置并启用(加签)-》应用公钥

保存后生成支付宝公钥(需要配置到项目中)

【注意代码中需要配置应用私钥,支付宝公钥(非应用公钥)】

应用公钥,支付宝公钥个人理解

应用公钥配置到支付宝平台,应用私钥配置代码中,我方发起请求,支付方通过应用公钥验证。

支付宝公钥配置代码中,当支付方回调我方接口,我方进行校验

两者完成相互校验提高安全性

6.支付宝网关(配置代码中)

/gateway.do

至此前期配置准备完成

开发

导入依赖

<dependency><groupId>com.alipay.sdk</groupId><artifactId>alipay-sdk-java</artifactId><version>4.9.9</version></dependency><!-- thymeleaf 依赖 用于渲染html页面 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency>

配置文件

alipay:appId: 收款账号对应的应用idprivateKey: 应用私钥publicKey: 支付宝公钥returnUrl: 127.0.0.1:9999/order/returnnotifyUrl: 127.0.0.1:9999/order/notify-url(异步回调地址,http/https开头必须外网能访问)refundNotifyUrl: /qq_37630282(同步回调地址,需要外网能够访问)gatewayUrl: /gateway.do(沙箱官网与正式网关不同,此处为沙箱网关)charset: utf-8signType: RSA2server:port: 9999spring:application:name: alipay-demothymeleaf:prefix: classpath:templates/suffix: .html

配置类

@Data@Component@ConfigurationProperties(prefix = "alipay")public class AlipayConfig {private String appId;private String privateKey;private String publicKey;private String returnUrl;private String notifyUrl;private String refundNotifyUrl;private String gatewayUrl;private String charset;private String signType;}

请求实体

此处不能用驼峰,不然请求参数会错误支付宝接口参数接收规范

@Datapublic class AlipayDTO {//商户订单号private String out_trade_no;//订单名称private String subject;//金额private String total_amount;//商品描述private String body;//超时时间参数private String timeout_express = "50m";//产品编号private String product_code = "FAST_INSTANT_TRADE_PAY";}

模拟支付界面html

<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>支付模拟页面</title></head><body><form action="/order/pay" method="post">订单号:<input type="text" name="outTradeNo" required><br/>订单名称:<input type="text" name="subject" required><br/>支付金额:<input type="text" name="totalAmount" required><br/>商品描述:<input type="text" name="body" ><br/><input type="submit" value="支付"> <input type="reset" value="重置"></form></body></html>

支付完跳转的商户界面

<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>支付成功</title></head><body>支付成功</body></html>

Controller

@Controller@RequestMapping("/order")public class PayController {@Resourceprivate PayService payService;@RequestMapping("/index")public String payPage(){//模拟支付界面return "index";}@PostMapping("/pay")@ResponseBodypublic String pay(@RequestBody AlipayDTO dto) throws AlipayApiException{//支付 整合实际业务创建或者更新订单return this.payService.pay(dto);}@RequestMapping("/notify-url")@ResponseBodypublic String afterPay(){//支付后回调 整合实际业务更新订单数据return "支付完成";}@RequestMapping(value = "/return")public String returnPage(){//模拟支付界面return "success";}}

servie

@Servicepublic class PayService {@Resourceprivate AlipayConfig alipayConfig;public String pay(AlipayDTO dto) throws AlipayApiException {AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig.getGatewayUrl(),alipayConfig.getAppId(),alipayConfig.getPrivateKey(),"json",alipayConfig.getCharset(),alipayConfig.getPublicKey(),alipayConfig.getSignType());AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();//回调地址 结合业务更新我方数据库request.setNotifyUrl(alipayConfig.getNotifyUrl());//支付完 支付界面跳转的界面request.setReturnUrl(alipayConfig.getReturnUrl());request.setBizContent(JSON.toJSONString(dto));String result = alipayClient.pageExecute(request).getBody();return result;}}

代码编写完成

支付测试

1.可能存在的问题

除了上方参数格式错误报错【订单信息无法识别,建议联系卖家】外

还可能存在支付成功后,返回界面【支付存在钓鱼风险!防钓鱼网站的方法】问题

解决方法:

1.关闭支付宝界面,清除浏览器缓存 2.换个未开过支付宝沙页面的箱浏览器

2.解决完后,出现支付宝登录界面

3.输入沙箱提供的买家账号,登录支付

支付成功

支付完-》同步回调return_url,异步回调notify_url

支付完稍等一会儿会跳转配置return_url对应的接口页面(此处设置return_url为博客地址)

[不跳转的情况排查]

1.return_url必须外网能访问,且http/https开头

2.考虑延迟,或者跟换浏览器尝试或者为本地服务地址等情况。

*以上支付宝沙箱支付流程整合完毕,以下为进一步整合内容

整合alipay-easysdk支付宝支付

开发流程

相关密钥配置同上

yml配置文件

alipay:appId: 应用idprivateKey: 应用私钥publicKey: 支付宝公钥(非应用公钥)serverUrl: 应用地址domain: returnUrl: notifyUrl: 回调地址 需外网可调用refundNotifyUrl:

导入依赖

<dependency><groupId>com.alipay.sdk</groupId><artifactId>alipay-easysdk</artifactId><version>2.2.0</version></dependency>

支付配置类

@Data@Component@ConfigurationProperties(prefix = "alipay")public class AliPayPropertiesConfig {//应用idprivate String appId;private String privateKey;private String publicKey;private String appCertPath;private String aliPayCertPath;private String aliPayRootCertPath;private String serverUrl;private String domain;private String returnUrl;//支付回调的接口private String notifyUrl;private String refundNotifyUrl;}

支付程序编写

网页支付(扫码支付)

@Resourceprivate AliPayPropertiesConfig aliPayConfig;......AlipayTradePagePayResponse response = Factory.Payment.Page().pay("支付标题", "订单号", "50", aliPayConfig.getReturnUrl());String form = response.getBody();return form;

App支付

AlipayTradePagePayResponse alipayTradePagePayResponse = Factory.Payment.Page().pay(subject, outTradeNo, totalAmount, returnUrl);

移动端网站支付

AlipayTradeWapPayResponse alipayTradeWapPayResponse = Factory.Payment.Wap().pay(subject, outTradeNo, totalAmount, quitUrl, returnUrl);

支付回调

@ApiOperation("阿里支付异步回调")@PostMapping("notify")public String notifyUrl(HttpServletRequest request) throws Exception{Map<String, String> params = request.getParameterMap();boolean flag = mon().verifyNotify(params);String out_trade_no = params.get("out_trade_no");String trade_no = params.get("trade_no");String total_amount = params.get("total_amount");if(flag){if(params.get("trade_status").equals("TRADE_SUCCESS")){// todo 业务}}return null;}

支付查询

AlipayTradeQueryResponse alipayTradeQueryResponse = mon().query(outTradeNo);Assert.isTrue(ResponseChecker.success(alipayTradeQueryResponse),"查询异常");return alipayTradeQueryResponse;

退款

AlipayTradeRefundResponse alipayTradeRefundResponse = mon().refund(outTradeNo, refundAmount);Assert.isTrue(ResponseChecker.success(alipayTradeRefundResponse), "退款异常");return alipayTradeRefundResponse;

交易取消

AlipayTradeCloseResponse alipayTradeCloseResponse = mon().close(outTradeNo);Assert.isTrue(ResponseChecker.success(alipayTradeCloseResponse),"交易取消失败");return alipayTradeCloseResponse;

其他辅助工具类

简易生成订单号

public class OrderNumerUtils {//生成yyyyMMddHHmmss+随机数的订单号public static String getOrderNumer(){Date date = new Date();SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");String strDate = formatter.format(date);String strRandom = RandomStringUtils.randomNumeric(8);return strDate + strRandom;}}

沙箱模拟时利用natapp内网穿透工具进行异步/同步回调

利用natapp注册本地ip即可生成web隧道,外网即可临时调用本地回调接口,具体搜索natapp教程

natapp_百度百科natapp 基于ngrok的反向代理软件,通过在公网和本地运行的 Web 服务器之间建立一个安全的通道。natapp 可捕获和分析所有通道上的流量,便于后期分析和重放./item/natapp/19762535?fr=aladdin

如果觉得《开发笔记 | Springboot整合多平台支付(微信/支付宝)》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。