Java接入支付宝支付教程

Java接入支付宝支付教程

一、创建应用

1.登录支付宝开放平台

支付宝开放平台网址:https://open.alipay.com/platform/developerIndex.htm

2.创建一个应用

在这里插入图片描述
?

图1

在这里插入图片描述

二 、设置应用密钥

1.下载安装支付宝开放平台助手

软件下载地址:https://gw.alipayobjects.com/os/bmw-prod/0e6297a2-89b9-4cab-972b-f9534ba07082.exe)如:图3
在这里插入图片描述

图3

2.使用支付宝开放平台助手生成密钥

(请注意此处会生成两个密钥,一个是应用公钥、一个是应用私钥,应用公钥需要填写到支付宝开放平台–具体请看下方图片,应用私钥需要开发者自己保存注意保密) 如:图4
在这里插入图片描述

图4

3.将生成的生成的应用公钥填写到支付宝开放平台

在这里插入图片描述

图5

复制支付宝开放平台助手生成的公钥 如:图6
在这里插入图片描述

图6

将公钥填写至支付宝开放平台然后点击“保存设置” 如:图7
在这里插入图片描述

图7

提交审核
在这里插入图片描述

图8

三、沙箱环境控配置

说明:(由于上方的应用审核需要时间,所以在没有通过之前,使用沙箱环境进行测试)

沙箱环境网址:https://openhome.alipay.com/platform/appDaily.htm?tab=info

1设置沙箱环境密钥

我这里之前已经设置好了,根据上方的:“二 、设置应用密钥” 步骤进行操作(注意应用私钥也是需要开发者自己保存的)如:图9
在这里插入图片描述

图9

四、搭建支付宝开放环境

说明:我这里的项目结构采用Spring+springmvc+mybatis 其他框架差不多没有太大的区别

1.导入jar包(支付宝SDK)

JDK 1.8 及其以上版本

1.1maven方式

将下列代码放入到项目中的pom.xml 文件中

<!-- https://mvnrepository.com/artifact/com.alipay.sdk/alipay-sdk-java -->
<dependency><groupId>com.alipay.sdk</groupId><artifactId>alipay-sdk-java</artifactId><version>4.10.209.ALL</version>
</dependency>

1.2下载jar导入

jar包下载地址:https://repo1.maven.org/maven2/com/alipay/sdk/alipay-sdk-java/4.10.209.ALL/alipay-sdk-java-4.10.209.ALL.jar

将该jar包导入到项目中的lib文件夹中

2创建zfbinfo.properties资源文件

在里面输入如下代码(请注意参数值后面是否有空格)

# 支付宝网关名、partnerId和appId
open_api_domain = https://openapi.alipay.com/gateway.do
mcloud_api_domain = http://mcloudmonitor.com/gateway.do
#此处请填写你的PID
pid = 
#此处请填写你当面付的APPID
appid =# RSA私钥、公钥和支付宝公钥
#此处请填写你的应用私钥且转PKCS8格式
private_key =
#此处请填写你的应用公钥
public_key = #SHA256withRsa对应支付宝公钥
alipay_public_key = # 签名类型: RSA->SHA1withRsa,RSA2->SHA256withRsa
sign_type = RSA2
# 当面付最大查询次数和查询间隔(毫秒)
max_query_retry = 5
query_duration = 5000# 当面付最大撤销次数和撤销间隔(毫秒)
max_cancel_retry = 3
cancel_duration = 2000# 交易保障线程第一次调度延迟和调度间隔(秒)
heartbeat_delay = 5
heartbeat_duration = 900#异步通知url(注意拦截器是否拦截)
NotifyUrl=http://8u862c.natappfree.cc/NBOSTA_WSBM/Alipay/ZFBcallbackAction.do

将下方 图10中的"appid"和"支付宝网关"写入到zfbinfo.properties资源文件对应的变量中
在这里插入图片描述

图10

将下面 图11中的“ ”和“ ”分别写入到 资源文件对应的变量中( 和 )在这里插入图片描述

将图12中的pid填入到zfbinfo.properties资源文件对应的变量中
在这里插入图片描述

图12

3.创建CommonUtils.java工具类

getZFBinfoValue():用于获取zfbinfo.properties资源文件中的参数

package Alipay.util;import java.util.Properties;public class CommonUtils {/*** 获取zfbinfo.properties文件里的值* @param name key* @return* @throws Exception*/public String getZFBinfoValue(String name) throws Exception{Properties props = new Properties();props.load(getClass().getResourceAsStream("/zfbinfo.properties"));String filepath = props.getProperty(name);;return filepath;}
}

3.项目结构图

在这里插入图片描述

五、支付当面付(扫码付款)

1.预约下单

1.1main函数执行

下方代码为预下单,main函数调用一下这个方法就可以运行了,调用此方法后支付宝会返回一个json格式的字符串里面包含二维码地址,然后可以利用二维码生成器 生成二维码然后就可以使用支付宝付款了,如:图13

package Alipay;import Alipay.util.CommonUtils;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradePrecreateModel;
import com.alipay.api.request.AlipayTradePrecreateRequest;
import com.alipay.api.response.AlipayTradePrecreateResponse;import java.util.Properties;/*** 支付宝面对面付款*/
public class AlipayFaceToFace {public static void main(String[] srgs) throws Exception {CommonUtils commonUtils=new CommonUtils();/** 支付宝网关 **/String URL =  commonUtils.getZFBinfoValue("open_api_domain");/** 应用id,如何获取请参考:https://opensupport.alipay.com/support/helpcenter/190/201602493024 **/String APP_ID = commonUtils.getZFBinfoValue("appid");/** 应用私钥,如何获取请参考:https://opensupport.alipay.com/support/helpcenter/207/201602469554 **/String APP_PRIVATE_KEY = commonUtils.getZFBinfoValue("private_key");/** 支付宝公钥,如何获取请参考:https://opensupport.alipay.com/support/helpcenter/207/201602487431 **/String ALIPAY_PUBLIC_KEY =commonUtils.getZFBinfoValue("alipay_public_key");/** 初始化 **/AlipayClient alipayClient = new DefaultAlipayClient(URL,APP_ID,APP_PRIVATE_KEY,"json","UTF-8",ALIPAY_PUBLIC_KEY,"RSA2");/** 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.precreate(统一收单线下交易预创建(扫码支付)) **/AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();/** 设置业务参数  **/AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();/** 商户订单号,商户自定义,需保证在商户端不重复,如:20200612000001 **/model.setOutTradeNo("20200612000005");/**订单标题 **/model.setSubject("毛毛测试");/** 订单金额,精确到小数点后两位 **/model.setTotalAmount("0.01");/** 订单描述 **/model.setBody("");/** 业务扩展参数 **///ExtendParams extendParams = new ExtendParams();/** 系统商编号,填写服务商的PID用于获取返佣,返佣参数传值前提:传值账号需要签约返佣协议,用于isv商户。 **///extendParams.setSysServiceProviderId("2088511****07846");/** 花呗分期参数传值前提:必须有该接口花呗收款准入条件,且需签约花呗分期 **//** 指定可选期数,只支持3/6/12期,还款期数越长手续费越高 **/// extendParams.setHbFqNum("3");/** 指定花呗分期手续费承担方式,手续费可以由用户全承担(该值为0),也可以商户全承担(该值为100),但不可以共同承担,即不可取0和100外的其他值。 			**///extendParams.setHbFqSellerPercent("0");//model.setExtendParams(extendParams);/** 将业务参数传至request中 **/request.setBizModel(model);/** 异步通知地址,以http或者https开头的,商户外网可以post访问的异步地址,用于接收支付宝返回的支付结果,如果未收到该通知可参考该文档进行确认:			https://opensupport.alipay.com/support/helpcenter/193/201602475759 **/request.setNotifyUrl("");/**第三方调用(服务商模式),传值app_auth_token后,会收款至授权app_auth_token对应商家账号,如何获传值app_auth_token请参考文档:				https://opensupport.alipay.com/support/helpcenter/79/201602494631 **///request.putOtherTextParam("app_auth_token", "传入获取到的app_auth_token值");AlipayTradePrecreateResponse response = null;try {/** 通过alipayClient调用API,获得对应的response类  **/response = alipayClient.execute(request);} catch (AlipayApiException e) {e.printStackTrace();}/** 获取接口调用结果,如果调用失败,可根据返回错误信息到该文档寻找排查方案:https://opensupport.alipay.com/support/helpcenter/101 **/System.out.println(response.getBody());}
}

在这里插入图片描述

图13

1.2结合前端扫码付款

1.2.1创建ZFBFaceToFaceModel实体对象
package com.test.model;import com.alipay.api.domain.GoodsDetail;import java.util.List;/*** 支付宝当面付实体类* @author 20201217 sqy**/
public class ZFBFaceToFaceModel {private String outTradeNo;// (必填) 商户网站订单系统中唯一订单号 ,64个字符以内,只能包含字母、数字、下划线, 需保证商户系统端不能重复,建议通过数据库sequence生成private String subject; // (必填) 订单标题,粗略描述用户的支付目的。如“喜士多(浦东店)消费”private String totalAmount;// (必填) 订单总金额单位为元,不能超过1亿元 如果同时传入了【打折金额】,【不可打折金额】,【订单总金额】三者,则必须满足如下条件:【订单总金额】=【打折金额】+【不可打折金额】private String undiscountableAmount;// (可选) 订单不可打折金额,可以配合商家平台配置折扣活动,如果酒水不参与打折,则将对应金额填写至此字段 如果该值未传入,但传入了【订单总金额】,【打折金额】,则该值默认为【订单总金额】-【打折金额】private String sellerId;// 卖家支付宝账号ID,用于支持一个签约账号下支持打款到不同的收款账号,(打款到sellerId对应的支付宝账号)// 如果该字段为空,则默认为与支付宝签约的商户的PID,也就是appid对应的PIDprivate String body;// // 订单描述,可以对交易或商品进行一个详细地描述,比如填写"购买商品2件共15.00元"private String operatorId; // 商户操作员编号,添加此参数可以为商户操作员做销售统计private String storeId; // (必填) 商户门店编号,通过门店号和商家后台可以配置精准到门店的折扣信息,详询支付宝技术支持private String timeoutExpress;//支付超时如:“120m”,定义为120分钟private List<GoodsDetail> goodsDetailList; //商品明细列表,需填写购买商品详细信息,private String NotifyUrl;// 支付成功之后 支付宝异步调用的接口地址;private String MoblieReturnUrl;//手机支付同步通知页面地址;/*** (必填) 商户网站订单系统中唯一订单号 ,64个字符以内,只能包含字母、数字、下划线, 需保证商户系统端不能重复,建议通过数据库sequence生成* @return*/public String getOutTradeNo() {return outTradeNo;}/*** (必填) 商户网站订单系统中唯一订单号 ,64个字符以内,只能包含字母、数字、下划线, 需保证商户系统端不能重复,建议通过数据库sequence生成* @param outTradeNo*/public void setOutTradeNo(String outTradeNo) {this.outTradeNo = outTradeNo;}/*** (必填) 订单标题,粗略描述用户的支付目的。如“喜士多(浦东店)消费”* @return*/public String getSubject() {return subject;}/*** (必填) 订单标题,粗略描述用户的支付目的。如“喜士多(浦东店)消费”* @param subject*/public void setSubject(String subject) {this.subject = subject;}/***  (必填) 订单总金额单位为元,不能超过1亿元 如果同时传入了【打折金额】,【不可打折金额】,【订单总金额】三者,则必须满足如下条件:【订单总金额】=【打折金额】+【不可打折金额】* @return*/public String getTotalAmount() {return totalAmount;}/***  (必填) 订单总金额单位为元,不能超过1亿元 如果同时传入了【打折金额】,【不可打折金额】,【订单总金额】三者,则必须满足如下条件:【订单总金额】=【打折金额】+【不可打折金额】* @param totalAmount*/public void setTotalAmount(String totalAmount) {this.totalAmount = totalAmount;}/*** (可选) 订单不可打折金额,可以配合商家平台配置折扣活动,如果酒水不参与打折,则将对应金额填写至此字段 如果该值未传入,但传入了【订单总金额】,【打折金额】,则该值默认为【订单总金额】-【打折金额】* @return*/public String getUndiscountableAmount() {return undiscountableAmount;}/*** (可选) 订单不可打折金额,可以配合商家平台配置折扣活动,如果酒水不参与打折,则将对应金额填写至此字段 如果该值未传入,但传入了【订单总金额】,【打折金额】,则该值默认为【订单总金额】-【打折金额】* @param undiscountableAmount*/public void setUndiscountableAmount(String undiscountableAmount) {this.undiscountableAmount = undiscountableAmount;}/*** // 卖家支付宝账号ID,用于支持一个签约账号下支持打款到不同的收款账号,(打款到sellerId对应的支付宝账号)// 如果该字段为空,则默认为与支付宝签约的商户的PID,也就是appid对应的PID* @return*/public String getSellerId() {return sellerId;}/*** // 卖家支付宝账号ID,用于支持一个签约账号下支持打款到不同的收款账号,(打款到sellerId对应的支付宝账号)// 如果该字段为空,则默认为与支付宝签约的商户的PID,也就是appid对应的PID* @param sellerId*/public void setSellerId(String sellerId) {this.sellerId = sellerId;}/*** 订单描述,可以对交易或商品进行一个详细地描述,比如填写"购买商品2件共15.00元"* @return*/public String getBody() {return body;}/*** 订单描述,可以对交易或商品进行一个详细地描述,比如填写"购买商品2件共15.00元"* @param body*/public void setBody(String body) {this.body = body;}/***商户操作员编号,添加此参数可以为商户操作员做销售统计* @return*/public String getOperatorId() {return operatorId;}/***商户操作员编号,添加此参数可以为商户操作员做销售统计* @param operatorId*/public void setOperatorId(String operatorId) {this.operatorId = operatorId;}/*** (必填) 商户门店编号,通过门店号和商家后台可以配置精准到门店的折扣信息,详询支付宝技术支持* @return*/public String getStoreId() {return storeId;}/*** (必填) 商户门店编号,通过门店号和商家后台可以配置精准到门店的折扣信息,详询支付宝技术支持* @param storeId*/public void setStoreId(String storeId) {this.storeId = storeId;}/***支付超时如:“120m”,定义为120分钟* @return*/public String getTimeoutExpress() {return timeoutExpress;}/***支付超时如:“120m”,定义为120分钟* @param timeoutExpress*/public void setTimeoutExpress(String timeoutExpress) {this.timeoutExpress = timeoutExpress;}/***商品明细列表,需填写购买商品详细信息,* @return*/public List<GoodsDetail> getGoodsDetailList() {return goodsDetailList;}/***商品明细列表,需填写购买商品详细信息,* @param goodsDetailList*/public void setGoodsDetailList(List<GoodsDetail> goodsDetailList) {this.goodsDetailList = goodsDetailList;}/***支付成功之后 支付宝异步调用的接口地址;* @return*/public String getNotifyUrl() {return NotifyUrl;}/***支付成功之后 支付宝异步调用的接口地址;* @param notifyUrl*/public void setNotifyUrl(String notifyUrl) {NotifyUrl = notifyUrl;}/*** 手机支付后跳转的页面地址* @return*/public String getMoblieReturnUrl() {return MoblieReturnUrl;}/*** 手机支付后跳转的页面地址* @return*/public void setMoblieReturnUrl(String moblieReturnUrl) {MoblieReturnUrl = moblieReturnUrl;}
}
1.2.2编写AlipayFaceToFace类

控制层调用ZFBPreorder方法支付宝预下单 返回json字符串其中包括二维码地址

package Alipay;import Alipay.util.CommonUtils;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradePrecreateModel;
import com.alipay.api.request.AlipayTradePrecreateRequest;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.test.model.ZFBFaceToFaceModel;/*** 支付宝面对面付款*/
public class AlipayFaceToFace {/*** 支付宝预下单* @param zfbFaceToFaceModel* @return*/public static String ZFBPreorder(ZFBFaceToFaceModel zfbFaceToFaceModel) {try {CommonUtils commonUtils = new CommonUtils();/** 支付宝网关 **/String URL = commonUtils.getZFBinfoValue("open_api_domain");/** 应用id,如何获取请参考:https://opensupport.alipay.com/support/helpcenter/190/201602493024 **/String APP_ID = commonUtils.getZFBinfoValue("appid");/** 应用私钥,如何获取请参考:https://opensupport.alipay.com/support/helpcenter/207/201602469554 **/String APP_PRIVATE_KEY = commonUtils.getZFBinfoValue("private_key");/** 支付宝公钥,如何获取请参考:https://opensupport.alipay.com/support/helpcenter/207/201602487431 **/String ALIPAY_PUBLIC_KEY = commonUtils.getZFBinfoValue("alipay_public_key");/** 初始化 **/AlipayClient alipayClient = new DefaultAlipayClient(URL, APP_ID, APP_PRIVATE_KEY, "json", "UTF-8", ALIPAY_PUBLIC_KEY, "RSA2");/** 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.precreate(统一收单线下交易预创建(扫码支付)) **/AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();/** 设置业务参数  **/AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();/** 商户订单号,商户自定义,需保证在商户端不重复,如:20200612000001 **/model.setOutTradeNo(zfbFaceToFaceModel.getOutTradeNo());/**订单标题 **/model.setSubject(zfbFaceToFaceModel.getSubject());/** 订单金额,精确到小数点后两位 **/model.setTotalAmount(zfbFaceToFaceModel.getTotalAmount());/** 订单描述 **/model.setBody(zfbFaceToFaceModel.getBody());/** 业务扩展参数 **///ExtendParams extendParams = new ExtendParams();/** 系统商编号,填写服务商的PID用于获取返佣,返佣参数传值前提:传值账号需要签约返佣协议,用于isv商户。 **///extendParams.setSysServiceProviderId("2088511****07846");/** 花呗分期参数传值前提:必须有该接口花呗收款准入条件,且需签约花呗分期 **//** 指定可选期数,只支持3/6/12期,还款期数越长手续费越高 **/// extendParams.setHbFqNum("3");/** 指定花呗分期手续费承担方式,手续费可以由用户全承担(该值为0),也可以商户全承担(该值为100),但不可以共同承担,即不可取0和100外的其他值。 **///extendParams.setHbFqSellerPercent("0");//model.setExtendParams(extendParams);/** 将业务参数传至request中 **/request.setBizModel(model);/** 异步通知地址,以http或者https开头的,商户外网可以post访问的异步地址,用于接收支付宝返回的支付结果,如果未收到该通知可参考该文档进行确认:https://opensupport.alipay.com/support/helpcenter/193/201602475759 **/request.setNotifyUrl(zfbFaceToFaceModel.getNotifyUrl());/**第三方调用(服务商模式),传值app_auth_token后,会收款至授权app_auth_token对应商家账号,如何获传值app_auth_token请参考文档:https://opensupport.alipay.com/support/helpcenter/79/201602494631 **///request.putOtherTextParam("app_auth_token", "传入获取到的app_auth_token值");/** 通过alipayClient调用API,获得对应的response类  **/AlipayTradePrecreateResponse response = alipayClient.execute(request);/** 获取接口调用结果,如果调用失败,可根据返回错误信息到该文档寻找排查方案:https://opensupport.alipay.com/support/helpcenter/101 **/System.out.println(response.getBody());return response.getBody();} catch (Exception e) {e.printStackTrace();return null;}}
}
1.2.3编写ZFBPreorderAction类
package com.test.controller;import Alipay.AlipayFaceToFace;
import Alipay.util.CommonUtils;
import com.alibaba.fastjson.JSONObject;
import com.test.model.ZFBFaceToFaceModel;
import com.test.service.TestService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.List;
import java.util.Map;@Controller
@RequestMapping("/AlipayFaceToFaceController")
public class AlipayFaceToFaceController {/*** 支付宝预约下单* 用于接受前端请求 返回给前端二维码地址和商户唯一订单编号* @param request* @param response* @return*/@RequestMapping("/ZFBPreorderAction")@ResponseBodypublic Map<String,Object> ZFBPreorderAction(HttpServletRequest request,HttpServletResponse response){Map<String,Object> resultMap=new HashMap<String, Object>();try {CommonUtils commonUtils=new CommonUtils();//(必填)商户唯一订单编号String outTradeNo= CommonUtils.getUuid();// (必填) 订单标题,粗略描述用户的支付目的。如“喜士多(浦东店)消费”String subject ="毛毛消费(中国)";// (必填) 订单总金额,单位为元,不能超过1亿元String totalAmount = "0.01";//(必填)支付成功支付支付宝异步通知的接口地址String NotifyUrl=commonUtils.getZFBinfoValue("NotifyUrl");//将参数放入实体对象中ZFBFaceToFaceModel zfbFaceToFaceModel=new ZFBFaceToFaceModel();zfbFaceToFaceModel.setOutTradeNo(outTradeNo);zfbFaceToFaceModel.setSubject(subject);zfbFaceToFaceModel.setTotalAmount(totalAmount);zfbFaceToFaceModel.setNotifyUrl(NotifyUrl);//支付宝预下单String json=AlipayFaceToFace.ZFBPreorder(zfbFaceToFaceModel);//解析json数据JSONObject jsonObject=JSONObject.parseObject(json);//得到alipay_trade_precreate_response数据后再强转JSONObjectJSONObject jsonobj_two=(JSONObject)jsonObject.get("alipay_trade_precreate_response");//再通过jsonobj_two获取到二维码地址String qrcode=jsonobj_two.get("qr_code").toString();resultMap.put("qrcode",qrcode);resultMap.put("outTradeNo",outTradeNo);} catch (Exception e) {e.printStackTrace();}return resultMap;}
}
1.2.4前端页面调用ZFBPreorderAction方法生成付款二维码

请注意一定要引用jquery.qrcode.min.js 要不然生成不了二维码

运行项目点击页面中的“点击生成付款二维码”就可以啦,然后就可以使用手机进行扫码支付

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>支付宝二维码付款</title><script type="text/javascript" src="js/jquery-1.11.2.min.js"></script><script type="text/javascript" src="js/jquery.qrcode.min.js"></script>
</head>
<body><a href="javascript:void(0);" onclick="Pay()">点击生成付款二维码</a><div id="Orcode_div" style="height: 165px;width: 165px"></div>
</body>
<script type="application/javascript">//项目名称var basePath="/byyu";//商户订单编号var outTradeNo;//预下单function Pay(){$.ajax({url: basePath + '/AlipayFaceToFaceController/ZFBPreorderAction.action',method: 'post',async: false,  //是否异步请求  默认truefalse为同步请求 ,true为异步请求)dataType: 'JSON',success: function (res) {//商户订单编号outTradeNo=res.outTradeNo;//创建订单二维码createQrcode(res.qrcode);}})}var zfbQRCode;//生成付款二维码function createQrcode(url){if (zfbQRCode!=undefined && zfbQRCode!='') {//清空之前的二维码$("#Orcode_div canvas").remove()$("#yes_qrcode").hide();}//生成二维码放入”Orcode_div“ divzfbQRCode=$('#Orcode_div').qrcode({width: 168, //宽度height: 168, //高度text:url});}
</script>
</html>
1.2.5效果图 如:图14

在这里插入图片描述

图14

2.交易状态查询

前言:上方我们已经调用了支付宝的预下单功能生成了二维码,接下来需要进行查询交易状态然后提示用户支付成功

注意:查询交易状态不要做任何系统业务逻辑,就算是如果用户支付成功也不要做业务逻辑处理,业务逻辑处理需要再异步回调中处理,下面会讲的

2.1创建findZFB_trade方法

将下列方法复制到AlipayFaceToFace类中

public static String findZFB_trade(ZFBFaceToFaceModel zfbFaceToFaceModel) throws Exception{CommonUtils commonUtils = new CommonUtils();/** 支付宝网关 **/String URL = commonUtils.getZFBinfoValue("open_api_domain");/** 应用id,如何获取请参考:https://opensupport.alipay.com/support/helpcenter/190/201602493024 **/String APP_ID = commonUtils.getZFBinfoValue("appid");/** 应用私钥,如何获取请参考:https://opensupport.alipay.com/support/helpcenter/207/201602469554 **/String APP_PRIVATE_KEY = commonUtils.getZFBinfoValue("private_key");/** 支付宝公钥,如何获取请参考:https://opensupport.alipay.com/support/helpcenter/207/201602487431 **/String ALIPAY_PUBLIC_KEY = commonUtils.getZFBinfoValue("alipay_public_key");/** 初始化 **/AlipayClient alipayClient = new DefaultAlipayClient(URL,APP_ID,APP_PRIVATE_KEY,"json","UTF-8",ALIPAY_PUBLIC_KEY,"RSA2");/** 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.query(统一收单线下交易查询) **/AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();/** 设置业务参数 **/AlipayTradeQueryModel model = new AlipayTradeQueryModel();/** 注:交易号(TradeNo)与订单号(OutTradeNo)二选一传入即可,如果2个同时传入,则以交易号为准 **//** 支付接口传入的商户订单号。如:2020061601290011200000140004 **/model.setOutTradeNo(zfbFaceToFaceModel.getOutTradeNo());/** 异步通知/查询接口返回的支付宝交易号,如:2020061622001473951448314322 **///model.setTradeNo("2020061622001473951448314322");/** 将业务参数传至request中 **/request.setBizModel(model);/** 第三方调用(服务商模式),必须传值与支付接口相同的app_auth_token **///request.putOtherTextParam("app_auth_token", "传入获取到的app_auth_token值");/** 通过alipayClient调用API,获得对应的response类  **/AlipayTradeQueryResponse response = alipayClient.execute(request);/** 获取接口调用结果,如果调用失败,可根据返回错误信息到该文档寻找排查方案:https://opensupport.alipay.com/support/helpcenter/101 **/return response.getBody();
}

2.2创建findZFB_tradeAction方法

将下列方法复制到AlipayFaceToFaceController类中

@RequestMapping("/findZFB_tradeAction")
@ResponseBody
public Map<String,Object> findZFB_tradeAction(HttpServletRequest request,HttpServletResponse response){Map<String,Object> resultMap=new HashMap<String, Object>();try {//(必填)商户唯一订单编号String outTradeNo=request.getParameter("outTradeNo");ZFBFaceToFaceModel zfbFaceToFaceModel=new ZFBFaceToFaceModel();zfbFaceToFaceModel.setOutTradeNo(outTradeNo);//查询交易状态String json=AlipayFaceToFace.findZFB_trade(zfbFaceToFaceModel);System.out.println(json);JSONObject jsonObject=JSONObject.parseObject(json);JSONObject jsonobj_two=(JSONObject)jsonObject.get("alipay_trade_query_response");//网关返回码,详见文档 https://opendocs.alipay.com/open/common/105806String ZFBCode=(String)jsonobj_two.get("code");//业务返回码String ZFBSubCode=(String)jsonobj_two.get("sub_code");//业务返回码描述String sub_msg=(String)jsonobj_two.get("sub_msg");//交易状态:WAIT_BUYER_PAY(交易创建,等待买家付款)、TRADE_CLOSED(未付款交易超时关闭,或支付完成后全额退款)、TRADE_SUCCESS(交易支付成功)、TRADE_FINISHED(交易结束,不可退款)String trade_status=(String)jsonobj_two.get("trade_status");if (ZFBCode.equals("40004") && ZFBSubCode.equals("ACQ.TRADE_NOT_EXIST")) {//订单未创建(用户未扫码)resultMap.put("code", ZFBCode);resultMap.put("data", "用户未扫码");} else if (ZFBCode.equals("10000") && trade_status.equals("WAIT_BUYER_PAY")) {//订单已经创建但未支付(用户扫码后但是未支付)resultMap.put("code", ZFBCode);resultMap.put("data", "non-payment");} else if (ZFBCode.equals("10000") && (trade_status.equals("TRADE_SUCCESS") || trade_status.equals("TRADE_FINISHED"))) {//判断ZFBCode是否等于”10000“ 并且 trade_status等于TRADE_SUCCESS(交易支付成功)或者 trade_status等于TRADE_FINISHED(交易结束,不可退款)//订单已支付(用户扫码完成并且支付成功之后)resultMap.put("code", ZFBCode);resultMap.put("data", "yes-payment");} else {resultMap.put("code", ZFBCode);resultMap.put("data", sub_msg);}} catch (Exception e) {e.printStackTrace();}return resultMap;
}

2.2HTML定时调用

将下方代码复制到AlipaQrcodePayment.html页面中

需要再二维码创建完成之后调用该方法。此方法会定时查询交易状态,然后根据自己业务要求编写后面的代码,如跳转页面等。。。

//定时任务
var trade;
//记录是否通知页面“用户已扫码”
var findNumber=true
/*** 查询交易状态*/
function findZFB_trade(){trade = setInterval(function(){console.log("每3秒执行一次");$.ajax({url: basePath +'/AlipayFaceToFaceController/findZFB_tradeAction.action',method: 'post',async: false,  //是否异步请求  默认truefalse为同步请求 ,true为异步请求)data: {"outTradeNo": outTradeNo},dataType: 'JSON',success: function (res) {if (res.code=='10000' && res.data=='non-payment'){//订单已经创建但未支付(用户扫码后但是未支付)if (findNumber){console.log("用户已扫码但是未支付");findNumber=false;}}else if (res.code=='10000' && res.data=='yes-payment'){//阻止定时window.clearInterval(trade);alert("订单已支付,感谢支持。。。");}}})},3000);
}

2.4最终效果图

在这里插入图片描述

3.支付宝异步通知

注意:这里再声明一下,为了系统的安全性考虑所有的业务逻辑处理,都需要再异步回调中处理不能再查询交易状态中处理。

异步通知作用:文章上方有说过再查询交易状态不做任何 业务逻辑处理,接下来就需要靠此方法进行业务逻辑代码处理。下面的ZFBcallback支付宝会再用户扫码成功之后会自动调用该方法会传输一些参数告诉服务器这个订单付款成功了然后系统就需要做出一些列的业务,如修改用户的订单状态等等。。

3.1内网穿透

由于我们需要接受支付宝到回调,所以我们的电脑就需要让外网可以访问,所以我们的电脑需要做内网穿透让外网的机子可以访问到我们的接口

具体操作方法各位码农可以百度一下

3.2设置回调接口地址

再AlipayFaceToFace类中的ZFBPreorder方法加上下放代码,setNotifyUrl()中的参数填写你的项目地址,如:https://test.domainName.com/project/AlipayFaceToFaceController/ZFBcallback.action 我下方代码写的是zfbFaceToFaceModel.getNotifyUrl(),是从zfbinfo.properties文件中获取的,此地址必须是外网可以正常访问到的,并且接受POST请求(GET不可以)。

 	/** 异步通知地址,以http或者https开头的,商户外网可以post访问的异步地址,用于接收支付宝返回的支付结果,如果未收到该通知可参考该文档进行确认:			https://opensupport.alipay.com/support/helpcenter/193/201602475759 **/request.setNotifyUrl(zfbFaceToFaceModel.getNotifyUrl());

3.3编写ZFBcallback回调函数

将下面的方法拷贝到AlipayFaceToFaceController类

需要注意的是,在这个方法中你一定一定需要做异步验签,简单来说你就是需要验证这个接口是不是支付宝在调用的,主要是为了安全为了防止不法分子调用你这个方法伪造数据,所以在此方法调用的时候需要做验签。

//异步验签:切记alipaypublickey是支付宝的公钥,请去open.alipay.com对应应用下查看。
boolean flag = AlipaySignature.rsaCheckV1(params, alipay_public_key, "utf-8","RSA2");

注意:支付宝再用户支付成功之后支付宝会自动调用下方的方法(前提是你设置了回调接口地址,具体操作请看上方代码),调用完成之后你需要返回“success”支付宝,如果不返回支付宝就会定时调用该方法所以你需要在接收到此方法之后完成一系列您的业务操作然后需要返回“success”字符串。并且注意拦截器是否拦截(如果被被您的登录拦截器拦截了,支付宝就无法访问此方法了)

/*** 支付回调函数(当用户支付成功之后,支付宝会自动调用该方法)* 此接口需要可以被外网访问而且必须是POST请求,并且注意拦截器是否拦截(如果被被您的登录拦截器拦截了,支付宝就无法访问此方法了)* @param request* @param response*/
@RequestMapping("/ZFBcallback")
public void ZFBcallback(HttpServletRequest request, HttpServletResponse response) throws IOException {try {CommonUtils commonUtils=new CommonUtils();//支付宝公钥String alipay_public_key=commonUtils.getZFBinfoValue("alipay_public_key");PrintWriter out;out = response.getWriter();//获取支付宝POST过来反馈信息Map<String, String> params = new HashMap<String, String>();Map requestParams = request.getParameterMap();//循环遍历支付宝请求过来的参数存入到params中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);}//异步验签:切记alipaypublickey是支付宝的公钥,请去open.alipay.com对应应用下查看。boolean flag = AlipaySignature.rsaCheckV1(params, alipay_public_key, "utf-8","RSA2");if (flag){//说明是支付宝调用的本接口if (params.get("trade_status").equals("TRADE_SUCCESS") || params.get("trade_status").equals("TRADE_FINISHED")) {System.out.println("收到回调结果,用户已经完成支付");/**** 这里写您的业务逻辑代码*/out.write("success");}}else {//验签失败该接口被别人调用out.write("支付宝异步回调验签失败,请留意");}out.flush();out.close();} catch (Exception e) {e.printStackTrace();}
}

3.4最终效果图

走到这里就说明用户支付成功了,然后就可以根据自己的业务需求做相应的业务逻辑处理了
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/556164.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

虚拟同步发电机_测量虚拟同步发电机惯量与阻尼系数的新方法

华北电力大学分布式储能与微网河北省重点实验室的研究人员颜湘武、王俣珂、贾焦心、王德胜、张波&#xff0c;在2019年第7期《电工技术学报》上撰文(论文标题为“基于非线性最小二乘曲线拟合的虚拟同步发电机惯量与阻尼系数测量方法”)指出&#xff0c;虚拟同步发电机(VSG)技术…

SpringBoot整合阿里云OSS上传文件

一、需求分析 文件上传是一个非常常见的功能&#xff0c;就是通过IO流将文件写到另外一个地方&#xff0c;这个地方可以是项目下的某个文件夹里&#xff0c;或者是本地电脑某个盘下面&#xff0c;还可以是云服务OSS里面&#xff0c;这里就是我要讲到的OSS&#xff0c;我写的是…

js 原型以及原型链

原型编程的基本规则&#xff1a; 所有的数据都是对象要得到一个对象&#xff0c;不是通过实例化类&#xff0c;而是找到一个对象作为原型并克隆它对象会记住它的原型如果对象无法相应某个请求&#xff0c;它会把这个请求委托给它自己的原型 直接上图 一、继续说说构造函数 …

SpringBoot整合阿里云OSS文件上传、下载、查看、删除

SpringBoot整合阿里云OSS文件上传、下载、查看、删除 该项目源码地址&#xff1a;https://github.com/ggb2312/springboot-integration-examples &#xff08;其中包含SpringBoot和其他常用技术的整合&#xff0c;配套源码以及笔记。基于最新的 SpringBoot2.1&#xff0c;欢迎各…

SpringBoot整合oss实现文件的上传,查看,删除,下载

springboot整合oss实现文件的上传,查看,删除,下载 1.什么是对象存储 OSS? 答&#xff1a;阿里云对象存储服务&#xff08;Object Storage Service&#xff0c;简称 OSS&#xff09;&#xff0c;是阿里云提供的海量、安全、低成本、高可靠的云存储服务。其数据设计持久性不低…

minio实现文件上传下载和删除功能

前言 之前用到文件上传功能&#xff0c;在这里做个学习记录。使用minio实现&#xff0c;后面会记录使用fastdfs和阿里云的oss实现文件上传以及他们的比较&#xff08;oss根据流量收费&#xff09;。minio的中文文档&#xff1a;https://docs.min.io/cn/ minio安装 首先查询d…

Spring Boot配置MinIO(实现文件上传、下载、删除)

1 MinIO MinIO 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口&#xff0c;非常适合于存储大容量非结构化的数据&#xff0c;例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等&#xff0c;而一个对象文件可以是任意大小&#xff…

Stream中toMap引发NullPointerException____Stream的执行流程

Stream中toMap引发NullPointerException 1、引发NullPointerException的代码如下&#xff1a; List<SelfSettlementCardInfoDto> selfSettlementCardInfoDtos selfCardAdapterManager.listSelfSettlementCardInfoDtoByCardIds(queryDto.getPartnerId(), cardIds, false…

Map集合使用get方法返回null抛出空指针异常问题

Map集合使用get方法空指针异常问题 前言 1.Map里面只能存放对象&#xff0c;不能存放基本类型&#xff0c;例如int&#xff0c;需要使用Integer 2.Map集合取出时&#xff0c;如果变量声明了类型&#xff0c;会先进行拆箱&#xff0c;再进行转换。 空指针问题 如图&#xff…

java各map中存放null值

java中各map中是否可以存储null值情况

Java 8————Collectors中的中的joining 方法和mapping方法

先定义好后面做示例要用的数据&#xff1a; List<User> listUser new ArrayList<>(); listUser.add(new User("李白", 20, true)); listUser.add(new User("杜甫", 40, true)); listUser.add(new User("李清照", 18, false)); lis…

编译器说 Lambda 表达式中的变量必须是 final 的,我偏不信

偶尔&#xff0c;我们需要在 Lambda 表达式中修改变量的值&#xff0c;但如果直接尝试修改的话&#xff0c;编译器不会视而不见听而不闻&#xff0c;它会警告我们说&#xff1a;“variable used in lambda expression should be final or effectively final”。 这个问题发生的…

pandas series取值_【小学生级】pandas入门到精通备查表——AI未来系列3

在未来面前&#xff0c;每个人都是学生江海升月明&#xff0c;天涯共此时&#xff0c;关注江时&#xff01;引子篇为AI未来系列第三篇&#xff0c;中阶部分开始。pandas的数据分析功能比excel强太多&#xff0c;基本上学会pandas&#xff0c;走遍天下都不怕。这是我的备查字典&…

instanceof 和 对象转型

一、instanceof 判断某个对象是否属于某个类 father1 instanceof Father; // true// 如果有子类继承父类的话 son instanceof Father; // true二、对象转型 子转父 > 自动转&#xff08;向下转型&#xff09; 父转子 > 强转&#xff08;向上转型&#xff09; 三、Obj…

从lambda表达式看final关键字

Variable used in lambda expression should be final or effectively final 想必大家在开发java程序的时候应该经常见到。 这是因为在lambda的匿名表达式里需要传入final的对象&#xff0c;那么这是为什么呢&#xff1f; 因为lambda是匿名表达式&#xff0c;它是在新开的一个…

lambda表达式或者匿名函数中为什么要求外部变量为final

1、参考博客 关于Lambda表达式里面修改外部变量问题JDK8之前&#xff0c;匿名内部类访问的局部变量为什么必须要用final修饰 2、匿名内部类 在jdk7之前&#xff0c;匿名内部类访问外部类的局部变量时&#xff0c;那么这个局部变量必须用final修饰符修饰&#xff0c;如下图1所…

你知道Java中final和static修饰的变量是在什么时候赋值的吗?

开始 一位朋友在群里问了这样一个问题&#xff1a; 本着乐于助人的想法&#xff0c;我当时给出的回答&#xff1a; 后来我总觉得哪里不对劲&#xff0c;仔细翻阅了《Java虚拟机规范》和《深入理解Java虚拟机》这一部分的内容&#xff0c;害&#xff01;发现自己理解的有问题。…

获取元素大小和位置的方式

一、直接获取元素样式属性值 – element.style.width console.log(div.style.width); // 500px console.log(parseInt(div.style.width)); // 500 console.log(typeof (div.style.width)); // string二、Offset 偏移量 offsetWidth width padding border offsetHeight he…

normalize函数_Pandas 数据处理(一) —— 几个简单函数掌握!

对于 Pandas&#xff0c; 接触过 Python 数据处理的小伙伴们都应该挺熟悉的&#xff0c;做数据处理不可或缺的一个程序包&#xff0c;最大的特点高效&#xff0c;本篇文章将通过案例介绍一下 Pandas 的一些基础使用&#xff01;1&#xff0c;读入数据大部分数据都可以用 read_c…

Java Collections.emptyList() 方法的使用及注意事项

Java Collections.emptyList方法的使用及注意事项 一、emptyList() 作用&#xff1a;返回一个空的List&#xff08;使用前提是不会再对返回的list进行增加和删除操作&#xff09;&#xff1b;好处&#xff1a; 1. new ArrayList()创建时有初始大小&#xff0c;占用内存&#…