100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > 支付宝支付快速集成实现

支付宝支付快速集成实现

时间:2020-10-31 08:40:30

相关推荐

支付宝支付快速集成实现

之前写过一篇微信支付的文章,相对来说,微信支付是比较麻烦的,申请商户号需要真实资质。支付宝就方便很多,文档和demo也相当齐全,尤其是友好的沙箱环境,可以实现支付功能的快速开发

概念

支付场景

支付宝支付支持多种支付场景包括当面付、app支付、手机网页支付等。本文已当面付为例说明

当面付-扫码支付

场景简介:买家通过使用支付宝 “扫一扫” 功能,扫描商家的二维码等方式完成支付

支付流程:展示二维码->扫码支付->等待回调->修改订单状态->定期对帐

应用场景

用户打开支付宝中的 扫一扫 功能,扫描商家展示的二维码进行支付。该模式适用于线下实体店支付、面对面支付、自助售货机等场景。流程如下图所示:

使用说明

收银员在商家收银系统操作生成支付宝订单,并生成二维码。用户登录支付宝,点击首页 扫一扫 或点击 付钱-扫码付,进入扫码界面。用户扫收银员提供的二维码,核对金额,确认支付。用户付款支付宝提示成功或失败,商家收银系统会拿到支付成功或者失败的结果。

更多说明查看链接:支付宝支付开发文档

开始开发

准备

下载sdk,sdk&demo提供了基础的支付功能接口 引入项目,新建支付宝支付工程,拷贝一下项目即可

(引入示例)

核心依赖包

<!-- 支付宝支付 --><dependency><groupId>com.alipay.sdk</groupId><artifactId>alipay-sdk-java</artifactId><version>3.3.87.ALL</version><exclusions><exclusion><artifactId>commons-logging</artifactId><groupId>commons-logging</groupId></exclusion></exclusions></dependency><!--gson--><dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.8.5</version></dependency><!--zxing--><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.2.1</version></dependency>

配置支付信息

即配置zfbinfo.properties中的参数信息。由于我没有正式账户,以下已沙箱环境为例

配置过程

进入沙箱环境appidopen_api_domain:appid拷贝信息配置的有关内容,替换pid: 进入个人账户中心,主账号信息中的账户ID就是pid

配置rsa公私钥

下载指定的加密工具,生成工具下载和使用

打开应用,点击“生成密钥”

private_key:将“应用私钥”拷贝到 private_key

public_key: 将“应用公钥”拷贝到 public_key

将公私钥拷贝到沙箱中

alipay_public_key: 支付宝公钥拷贝到 alipay_public_key

其他保持不变

代码测试

下载sdk,配置zfbinfo.properties后,启动main.java的main()方法,测试代码

自定义业务

代码改写

参考main.java的中的支付方法,改写当面付

public String f2fPayTradePay(@RequestBody TradePaySummary tradePaySummary) {// 订单列表List<TradePaySummary.Item> items = tradePaySummary.getItems();//支付二维码的访问路径String qrCodePath = null;// (必填) 商户网站订单系统中唯一订单号,64个字符以内,只能包含字母、数字、下划线,// 需保证商户系统端不能重复,建议通过数据库sequence生成,String outTradeNo = "flow" + System.currentTimeMillis() + (long) (Math.random() * 10000000L);// (必填) 订单标题,粗略描述用户的支付目的。如“xxx品牌xxx门店消费”String subject = tradePaySummary.getSubject();// (必填) 订单总金额,单位为元,不能超过1亿元// 如果同时传入了【打折金额】,【不可打折金额】,【订单总金额】三者,则必须满足如下条件:【订单总金额】=【打折金额】+【不可打折金额】String totalAmount = items.stream().map(item -> new BigDecimal(item.getPrice())).reduce(new BigDecimal("0"), BigDecimal::add).toString();// (可选) 订单不可打折金额,可以配合商家平台配置折扣活动,如果酒水不参与打折,则将对应金额填写至此字段// 如果该值未传入,但传入了【订单总金额】,【打折金额】,则该值默认为【订单总金额】-【打折金额】String undiscountableAmount = "0.0";// 卖家支付宝账号ID,用于支持一个签约账号下支持打款到不同的收款账号,(打款到sellerId对应的支付宝账号)// 如果该字段为空,则默认为与支付宝签约的商户的PID,也就是appid对应的PIDString sellerId = "";// 订单描述,可以对交易或商品进行一个详细地描述,比如填写"购买商品3件共20.00元"String body = String.format("购买商品%s件共%s元", items.size(), totalAmount);// (必填) 商户门店编号,通过门店号和商家后台可以配置精准到门店的折扣信息,详询支付宝技术支持String storeId = tradePaySummary.getStoreId();// 业务扩展参数,目前可添加由支付宝分配的系统商编号(通过setSysServiceProviderId方法),详情请咨询支付宝技术支持String providerId = "208810000400500";ExtendParams extendParams = new ExtendParams();extendParams.setSysServiceProviderId(providerId);// 支付超时,线下扫码交易定义为5分钟String timeoutExpress = "15m";// 商品明细列表,需填写购买商品详细信息,List<GoodsDetail> goodsDetailList = new ArrayList<>();// 创建一个商品信息,参数含义分别为商品id(使用国标)、名称、单价(单位为分)、数量,如果需要添加商品类别,详见GoodsDetailList<GoodsDetail> instances = items.stream().map(item -> {long price = new BigDecimal(item.getPrice()).multiply(new BigDecimal(100)).longValue();GoodsDetail instance = GoodsDetail.newInstance(item.getGoodsId(), item.getGoodsName(), price, item.getQuantity());return instance;}).collect(Collectors.toList());// 创建好一个商品后添加至商品明细列表goodsDetailList.addAll(instances);// 创建支付请求builder,设置请求参数AlipayTradePrecreateRequestBuilder builder = new AlipayTradePrecreateRequestBuilder().setSubject(subject).setTotalAmount(totalAmount).setOutTradeNo(outTradeNo).setUndiscountableAmount(undiscountableAmount).setSellerId(sellerId).setBody(body).setOperatorId(tradePaySummary.getOperatorId()).setStoreId(storeId).setExtendParams(extendParams).setTimeoutExpress(timeoutExpress)// 支付宝服务器主动通知商户服务器里指定的页面http路径,根据需要设置.setNotifyUrl(tradePayProperties.getPaySuccessCallBack()).setGoodsDetailList(goodsDetailList);// 调用tradePay方法获取当面付应答AlipayF2FPrecreateResult result = alipayTradeService.tradePrecreate(builder);switch (result.getTradeStatus()) {case SUCCESS:log.info("支付宝支付成功: )");AlipayTradePrecreateResponse response = result.getResponse();dumpResponse(response);// 生成二维码保存路径,创建二维码String filePath = String.format(tradePayProperties.getAliPayPath() + "/qr-%s.png", response.getOutTradeNo());// 本地绝对路径:/Users/yc/temp/qr-code/alipay/qr-123.pngString storePath = tradePayProperties.getStorePath() + filePath;ZxingUtils.getQRCodeImge(response.getQrCode(), 256, storePath);// http请求相对路径:http://localhost:9150/static/qrcode/alipay/qr-123.pngqrCodePath = tradePayProperties.getHttpBasePath() + filePath;break;case FAILED:log.error("支付宝支付失败!!!");break;case UNKNOWN:log.error("系统异常,订单状态未知!!!");break;default:log.error("不支持的交易状态,交易返回异常!!!");break;}return qrCodePath;}

二维码路径

路径配置

trade:ali-pay:qrcode:store-path: /Users/yc/temp/qr-code/alipay # 本地文件绝对路径路径http-base-path: /static/qrcode/alipay # http访问的url路径,对应store-pathcallback-url: http://chetwhy./ali-pay/f2f/callback # 支付宝支付的回调地址

读取配置

@Component@ConfigurationProperties(prefix = "trade.ali-pay")@Datapublic class AliPayTradeProperties {/*** 二维码*/private AliPayTradeProperties.Qrcode qrcode;/*** 支付成功回调页面*/private String callbackUrl;@Datapublic static class Qrcode{/*** qrcode存储文件路径设置*/private String storePath;/*** http访问路径设置*/private String httpBasePath;}}

资源路径映射配置

@Configurationpublic class ResourceConfig implements WebMvcConfigurer {@Autowiredprivate TradePayProperties tradePayProperties;@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {String os = System.getProperty("os.name");// 将http请求路径映射到本地路径registry.addResourceHandler(tradePayProperties.getHttpBasePath() + "/**").addResourceLocations("file:" + tradePayProperties.getStorePath() + "/");}}

说明

store-path:二维码存储在服务器本地中的绝对路径

http-base-path:服务器访问二维码的网络路径

ResourceConfig: 配置后,http请求的路径http-base-path可以直接映射到本地store-path,如访问http:localhost:8080/static/qrcode/alipay/qr-123.png,就是访问/Users/yc/temp/qr-code/alipay/qr-123.png,他们指向的都是同一资源

支付回调

当二维码返回后,用户扫描二维码并支付。这是用户感知行为,我们如何得知支付已完成。除了主动查询外(参考main.java),更推荐使用支付回调的方式,让支付宝后台主动通知商家后台

内网穿透

支付宝本身是一个在公网的服务应用,要想支付宝通知到我们的后台,需要我们后台有一个公网可以访问的域名。本地调试时,可以使用内网穿透工具,这样访问指定公网域名,就可以直接访问到本地

回调通知是一个异步通知,不同于socket请求的后端与远程服务器保持长链接

Ngrok CC

之前在微信支付中,用的内网穿透工具的是natapp。这次使用ngrok cc示例,两者都是不错的工具

这里不对ngrok的使用只做简单说明,有兴趣的可以查看官网教程

大体步骤:

注册账户;

登录后,(没有隧道)点击隧道管理-开通隧道,选择免费的即可;

隧道管理-编辑,配置本地端口,这个端口就是支付应用启动的端口

下载客户端

解压本地,进入客户端

拷贝隧道id,当前目录打开终端,输入启动脚本

./sunny clientid 隧道id

回调接口

参考文档

@Slf4j@RestController@RequestMapping("/ali-pay/f2f")public class AliPayF2FController {@Autowiredprivate TradeService tradeService;/*** 支付宝支付回调,根据文档:* 1.支付宝是用 POST 方式发送通知信息* 2.在通知返回参数列表中,除去sign、sign_type两个参数外,凡是通知返回回来的参数皆是待验签的参数。将剩下参数进行 url_decode, 然后进行字典排序,组成字符串,得到待签名字符串* 3.程序执行完后必须通过 PrintWriter 类打印输出"success"(不包含引号)。如果商户反馈给支付宝的字符不是 success 这7个字符,支付宝服务器会不断重发通知,直到超过 24 小时 22 分钟。一般情况下,25 小时以内完成 8 次通知(通知的间隔频率一般是:4m,10m,10m,1h,2h,6h,15h)** @return*/@PostMapping("/callback")public void aliPayCallback(HttpServletRequest request, HttpServletResponse response) {log.info("接受支付宝回调");Enumeration<String> names = request.getParameterNames();Map<String, String> map = new TreeMap<>();while (names.hasMoreElements()) {String name = names.nextElement();String value = request.getParameter(name);log.info("parameter name->{},value->{}", name, value);if (!"sign_type".equalsIgnoreCase(name)) {map.put(name, value);}}// 异步返回结果验签try {boolean checkFlag = AlipaySignature.rsaCheckV2(map, Configs.getAlipayPublicKey(), "utf-8", Configs.getSignType());PrintWriter writer = response.getWriter();if (checkFlag) {writer.print("success");} else {writer.print("unSuccess");}} catch (AlipayApiException | IOException e) {throw new CustomException(ResultCodeEnum.ALIPAY_CALLBACK_CHECK_FAILED);}}// 其他业务接口...}

当前mvc的访问路径就是/ali-pay/f2f/callback,加上ngrok中主机域名后就是:http://chetwhy./ali-pay/f2f/callback,更新到配置文件中

支付测试

支付接口

/*** 支付宝支付* @return*/@PostMapping("/ali-pay")public ResponseEntity<R> doOrder() {log.info("测试支付宝支付...");TradePaySummary summary = new TradePaySummary();summary.setSubject("测试支付宝当面付");summary.setOperatorId("yc");summary.setStoreId("yc");TradePaySummary.Item item = new TradePaySummary.Item();item.setGoodsId("1001");item.setGoodsName("测试商品");item.setPrice("100000");item.setQuantity(1);summary.setItems(Arrays.asList(item));String qrcode = aliPayFeignApi.f2fPayTradePay(summary);if (StringUtils.isEmpty(qrcode)) {return R.error().message("网络繁忙").buildResponseEntity();}// TODO 保存订单,更新状态return R.ok().message("测试成功").data("qrcode",qrcode).buildResponseEntity();}

测试流程

下单沙箱版app

请求二维码接口

点击二维码路径,获取图片

使用沙箱app扫码支付,支付密码默认是111111,详细可查看沙箱账号(记住是沙箱app)

PS: 沙箱平台可为当前账户自由充值超大支付金额,体验一把挥霍。。。

详细源码,请参考:/chetwhy/cloud-flow

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