百度网站前面的图片,潮阳建设局网站,特别炫酷的网站,小米路由可以做网站吗文章目录 1、支付方式选择2、交互流程3、对接准备1#xff09;加密解密 签名验签2#xff09;沙箱环境3#xff09;内网穿透 4、二维码5、下单6、异步通知回调7、查询支付结果8、退款9、通用版SDK 需求#xff1a;系统A对接支付宝#xff0c;实现支持用户扫码支付
1、支… 文章目录 1、支付方式选择2、交互流程3、对接准备1加密解密 签名验签2沙箱环境3内网穿透 4、二维码5、下单6、异步通知回调7、查询支付结果8、退款9、通用版SDK 需求系统A对接支付宝实现支持用户扫码支付
1、支付方式选择 对接的API文档https://open.alipay.com/api 可选的支付方式有
扫码付出示付款码或者用户扫码付款APP支付在APP中唤起支付宝手机网站支付在移动端网页中唤起支付宝 App 或支付宝网页电脑网站支付在PC端唤起支付宝App或者网页登录支付宝账户刷脸付需硬件支持商家扣款类似每月会员扣款预授权支付冻结对应额度交易完成后给商家JSAPI支付小程序
这里选择扫码付的方式点击下单后返回支付二维码用户扫码支付。
2、交互流程
画个下单流程的时序图 大致流程
用户下单系统A组装信息后订单信息、回调地址、签名调用支付宝预下单接口返回二维码链接系统A将二维码链接转二维码图片用户扫码唤醒本地支付宝完成支付支付宝返回支付成功信息给用户支付宝异步通知系统A支付成功的消息回调地址如果用户支付成功支付宝就调用回调地址的API回调接口中自然是系统A收到用户支付成功消息后的动作上一步如果通知失败比如网络异常或支付宝调用异步通知接口时系统A正好挂了 ⇒ 可主动调支付宝提供的查询支付结果接口或者加定时任务轮询来查询交易状态如3s-5s还可以考虑在第一步请求支付宝接口时加上二维码的有效时间过期就重新发起
查询支付结果流程 退款流程同上查询支付结果。PS注意下单、退款过程中相关订单的业务数据落库到系统A。
3、对接准备
1加密解密 签名验签
支付信息不能在网络上明文传输以防被篡改。系统A到支付宝的方向采用
支付宝公钥加密 系统A的私钥签名系统A做的事支付宝私钥解密 系统A的公钥验签收到信息后支付宝做的事
同理支付宝返回支付结果时就是在支付宝中用系统A的公钥加密支付宝的私钥签名传输到系统A后则是先用支付宝的公钥验签再用系统A的私钥解密支付结果
2沙箱环境
调试过程中可采用支付宝提供的沙箱环境点击右上角控制台登录后选择沙箱 这里有一套可调试的APPID、系统A的公钥、密钥、支付宝的公钥、支付宝的网关地址以及商家账户和用户账户用于后续登录沙箱版本支付宝APP完成支付 点击【沙箱工具】侧边栏下载沙箱版支付宝APP等于上面的买家账户。
3内网穿透
前面提到用户支付成功后支付宝需要回调系统A接口来通知系统A但我的开发环境在内网支付宝访问不到考虑做内网穿透让支付宝通知到一个中转地址再由中转地址到我的内网。穿透工具选择cpolar下载地址 https://dashboard.cpolar.com/get-started下载后解压并安装msi包 双击exe文件执行认证
cpolar authtoken xxxx创建隧道建立链接
cpolar http 9527//返回结果
Forwarding http://maggie.cpolar.io - localhost:9527
Forwarding https://maggie.cpolar.io - localhost:9527转发成功。此时给支付宝访问forward的地址即可比如系统A的异步通知接口
localhost:9527/notify那就是
http://maggie.cpolar.io/notify4、二维码
二维码是消息的载体。平时玩可直接在草料二维码UI页面这里需要给系统A的订单服务用代码生成二维码。二维码中的信息自然是支付宝预下单返回的url。Java生成二维码可集成zxing库但这样得自己两层for填充方格子【SpringBoot整合ZXing创建二维码和条形码】 这里选择hutool工具类库对zxing的二次封装引入依赖
dependencygroupIdcn.hutool/groupIdartifactIdhutool-all/artifactIdversion5.7.22/version
/dependencydependencygroupIdcom.google.zxing/groupIdartifactIdcore/artifactIdversion3.4.1/version
/dependency调用方式
//生成直到url对应的二维码宽高均300像素可到路径也可到Http响应
QrCodeUtil.generate(https://url/path, 300, 300, png, httpServletResponse.getOutPutStream());也可引入QrConfig对象设置其他属性
QrConfig config new QrConfig(300, 300);
//纠错级别
config.setErrorCorrection(ErrorCorrectionLevel.H);
//二维码颜色
config.setBackColor(Color.BLUE);
QrCodeUtil.generate(https://www.baidu.com, config, new File(D:\\code.png));5、下单
支付宝提供的SDK 中已经对加签验签逻辑做了封装使用 SDK 时传入支付宝公钥等内容可直接通过 SDK 自动进行加验签。 SDK文档地址https://opendocs.alipay.com/open/54/103419?pathHashd6bc7c2b 。支付宝提供了两种SDK
通用版SDK简易版SDK
官网有通用版的API代码示例这里走简易版的。引入简易版SDK的依赖
!-- https://mvnrepository.com/artifact/com.alipay.sdk/alipay-easysdk --
dependencygroupIdcom.alipay.sdk/groupIdartifactIdalipay-easysdk/artifactIdversion2.2.0/version
/dependency
在application.yml配置文件中统一写密钥、通知地址等生产环境不要将私钥信息配置在源码中例如配置为常量或储存在配置文件中这样源码一丢这些保密信息都泄漏了放安全区域或服务器运行时读取即可
alipay:easy:protocol: httpsgatewayHost: openapi-sandbox.dl.alipaydev.comsignType: RSA2appId: 9021000133624745merchantPrivateKey: MIIEvQIBADANBgkqhkiG9w0BalipayPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCnotifyUrl: http://maggie.cpolar.io/notify
server:port: 9527ConfigurationProperties注解统一读到
Configuration
Data
ConfigurationProperties(prefix alipay.easy)
public class AliPayConfigInfo {/*** 请求协议*/private String protocol;/*** 请求网关*/private String gatewayHost;/*** 签名类型*/private String signType;/*** 应用ID来自支付宝申请*/private String appId;/*** 应用秘钥*/private String merchantPrivateKey;/*** 支付宝公钥*/private String alipayPublicKey;/*** 支付结果异步通知的地址*/private String notifyUrl;/*** 设施AES秘钥*/private String encryptKey;
}
将配置处理成Config类型的Bean方便后面传入Config对象
Configuration
public class AliPayConfig {Beanpublic Config config(AliPayConfigInfo configInfo){Config config new Config();config.protocol configInfo.getProtocol();config.gatewayHost configInfo.getGatewayHost();config.signType configInfo.getSignType();config.appId configInfo.getAppId();config.merchantPrivateKey configInfo.getMerchantPrivateKey();config.alipayPublicKey configInfo.getAlipayPublicKey();config.notifyUrl configInfo.getNotifyUrl();config.encryptKey ;return config;}
}
写下单接口响应一个二维码给前端这里业务数据、订单编号直接写死只做示意
RestController
Slf4j
public class PayController {Resourceprivate Config config;/*** 收银台点击结账* 发起下单请求*/GetMapping(/pay)public void pay(HttpServletRequest request, HttpServletResponse response) throws Exception {Factory.setOptions(config);//调用支付宝的接口AlipayTradePrecreateResponse payResponse Factory.Payment.FaceToFace().preCreate(订单主题Mac笔记本, LS123qwe123, 19999);//参照官方文档响应示例解析返回结果String httpBodyStr payResponse.getHttpBody();JSONObject jsonObject JSONObject.parseObject(httpBodyStr);String qrUrl jsonObject.getJSONObject(alipay_trade_precreate_response).get(qr_code).toString();QrCodeUtil.generate(qrUrl, 300, 300, png, response.getOutputStream());}
}6、异步通知回调
异步回调参考文档https://opendocs.alipay.com/open/194/103296?pathHashe43f422erefapi实现先全放Controller层了
RestController
Slf4j
public class PayController {Resourceprivate Config config;/*** 给支付宝的回调接口*/PostMapping(/notify)public void notify(HttpServletRequest request, HttpServletResponse response) throws Exception {MapString, String params new HashMap();//获取支付宝POST过来反馈信息将异步通知中收到的待验证所有参数都存放到map中MapString, String[] parameterMap request.getParameterMap();for (String name : parameterMap.keySet()) {String[] values parameterMap.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 signResult Factory.Payment.Common().verifyNotify(params);if (signResult) {log.info(收到支付宝发送的支付结果通知);String out_trade_no request.getParameter(out_trade_no);log.info(交易流水号{}, out_trade_no);//交易状态String trade_status new String(request.getParameter(trade_status).getBytes(ISO-8859-1), UTF-8);//交易成功switch (trade_status) {case TRADE_SUCCESS://支付成功的业务逻辑比如落库开vip权限等log.info(订单{} 交易成功, out_trade_no);break;case TRADE_FINISHED:log.info(交易结束不可退款);//其余业务逻辑break;case TRADE_CLOSED:log.info(超时未支付交易已关闭或支付完成后全额退款);//其余业务逻辑break;case WAIT_BUYER_PAY:log.info(交易创建等待买家付款);//其余业务逻辑break;}response.getWriter().write(success); //返回success给支付宝表示消息我已收到不用重调} else {response.getWriter().write(fail); ///返回fail给支付宝表示消息我没收到请重试}}
}到此看下效果请求下单接口 用沙箱版app扫码 支付查看余额 7、查询支付结果
主动查询用户的支付结果订单编号依然写死
RestController
Slf4j
public class PayController {Resourceprivate Config config;GetMapping(/query)public String query() throws Exception {Factory.setOptions(config);AlipayTradeQueryResponse result Factory.Payment.Common().query(LS123qwe123);return result.getHttpBody();}}8、退款
退款操作
RestController
Slf4j
public class PayController {Resourceprivate Config config;GetMapping(/refund)public String refund() throws Exception {Factory.setOptions(config);AlipayTradeRefundResponse refundResponse Factory.Payment.Common().refund(LS123qwe123, 19999);return refundResponse.getHttpBody();}
}9、通用版SDK
官方文档就是以这个SDK为例的贴个代码示例
private static final String GATEWAY_URL https://openapi.alipaydev.com/gateway.do;
private static final String FORMAT JSON;
private static final String CHARSET UTF-8;//签名方式private static final String SIGN_TYPE RSA2;
Resource
private AliPayConfig aliPayConfig;Resource
private OrdersMapper ordersMapper;GetMapping(/pay) // subjectxxxtraceNoxxxtotalAmountxxx
public void pay(AliPay aliPay, HttpServletResponse httpResponse) throws Exception {// 1. 创建Client通用SDK提供的Client负责调用支付宝的APIAlipayClient alipayClient new DefaultAlipayClient(GATEWAY_URL, aliPayConfig.getAppId(),aliPayConfig.getAppPrivateKey(), FORMAT, CHARSET, aliPayConfig.getAlipayPublicKey(), SIGN_TYPE);// 2. 创建 Request并设置Request参数AlipayTradePagePayRequest request new AlipayTradePagePayRequest(); // 发送请求的 Request类request.setNotifyUrl(aliPayConfig.getNotifyUrl());JSONObject bizContent new JSONObject();bizContent.set(out_trade_no, aliPay.getTraceNo()); // 我们自己生成的订单编号bizContent.set(total_amount, aliPay.getTotalAmount()); // 订单的总金额bizContent.set(subject, aliPay.getSubject()); // 支付的名称bizContent.set(product_code, FAST_INSTANT_TRADE_PAY); // 固定配置request.setBizContent(bizContent.toString());// 执行请求拿到响应的结果返回给浏览器String form ;try {form alipayClient.pageExecute(request).getBody(); // 调用SDK生成表单} catch (AlipayApiException e) {e.printStackTrace();}httpResponse.setContentType(text/html;charset CHARSET);httpResponse.getWriter().write(form);// 直接将完整的表单html输出到页面httpResponse.getWriter().flush();httpResponse.getWriter().close();
}具体有业务数据逻辑的对接支付宝接口可跳转【支付宝业务对接】