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对应的PID
private 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")
@ResponseBody
public 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数据后再强转JSONObject
JSONObject 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“ div
zfbQRCode=$('#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/105806
String 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最终效果图
走到这里就说明用户支付成功了,然后就可以根据自己的业务需求做相应的业务逻辑处理了