文章目录
- 收款码支付
- 接入流程
- 安全设计
- 系统交互流程
- 交易状态
- 统一收单交易支付接口
- 请求参数
- 测试结果
- 查询支付
- 撤销支付
- 退款支付
- 退款结果
- 退款说明
收款码支付
继:支付宝支付之入门支付
接入流程
安全设计
支付宝为了保证交易安全采取了一系列安全手段以保证交易安全。主要采用以下安全设计策略,详情可查看 应用安全开发指南。
- 采用 HTTPS 协议传输交易数据,防止数据被截获、解密。
- 采用 RSA/RSA2 非对称密钥,明确交易双方的身份,保证交易主体的正确性和唯一性。
- 付款码定时刷新,防止被拍照。
- 防止截屏 (截屏后二维码失效)
系统交互流程
- 买家出示钱包内付款码,商家通过扫码设备获取付款码信息,并将付款码上传到 alipay.trade.pay(统一收单交易支付接口)请求支付。
- 根据返回的结果,确定支付状态,从而进行相应处理(包括必要时关闭交易), 如图 opt 区块中所示根据付款码支付接口返回公共参数code,付款码支付存在以下四种状态:
结果码 | 描述 |
---|---|
请求成功(10000) | 只表示请求成功,若存在扣款异常可能发生回滚导致扣款失败,必须根据查询接口或者异步通知返回的交易状态进行判断,交易失败,建议关闭交易,更换订单号out_trade_no和买家支付渠道重新扣款。 |
等待用户付款(10003) | 由于余额不足、超额等原因导致订单创建但支付未成功,等待用户付款。付款码支付由于是线下面对面支付,需尽快确认交易情况,建议通过 轮询方式 判断交易状态,避免单边账。 |
未知异常(20000) | 未知异常,可能由于系统异常或者网络超时等问题导致接口报错,建议调用查询接口确认支付结果,详情可查看 异常处理 。 |
支付失败(40001- 40006) | 业务出现未知错误或者系统异常,需要重新检查参数,重新发起支付。详情可查看 异常处理 。 |
交易状态
随着订单支付成功、退款、关闭等操作,订单交易的每一个环节 trade_status(交易状态)不同。
- 交易创建成功后,用户支付成功,交易状态转为 TRADE_SUCCESS(交易成功)。
- 交易成功后,规定退款时间内没有退款,交易状态转为 TRADE_FINISHED(交易完成)。
- 交易支付成功后,交易部分退款,交易状态为 TRADE_SUCCESS(交易成功)。
- 交易成功后,交易全额退款,交易状态转为 TRADE_CLOSED(交易关闭)。
- 交易创建成功后,用户未付款交易超时关闭,交易状态转为 TRADE_CLOSED(交易关闭)。
- 交易创建成功后,用户支付成功后,若用户商品不支持退款,交易状态直接转为 TRADE_FINISHED(交易完成)。
注意:交易成功后部分退款,交易状态仍为 TRADE_SUCCESS(交易成功),如果一直部分退款退完所有交易金额则交易状态转为 TRADE_CLOSED(交易关闭),如果未退完所有交易金额,超过有效退款时间后交易状态转为 TRADE_FINISHED(交易完成)不可退款。
继:支付宝支付之入门支付
统一收单交易支付接口
package com.sin.demo.controller;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.*;
import com.alipay.api.request.AlipayTradePayRequest;
import com.alipay.api.response.AlipayTradePayResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @createTime 2024/6/29 12:51
* @createAuthor SIN
* @use 付款码支付
*/
@RestController
public class AliPayTradePayController {
// 从配置文件中获取参数值
@Value("${alipay.appId}")
private String appId; // 支付宝应用ID
@Value("${alipay.privateKey}")
private String privateKey; // 商户应用私钥
@Value("${alipay.publicKey}")
private String publicKey; // 支付宝公钥
@Value("${alipay.gatewayUrl}")
private String gatewayUrl; // 支付宝网关URL
/**
* 创建订单
* @param subject 交易订单标题
* @param authCode 支付授权码
* @param scene 支付场景
* @param outTradeNo 商户订单号
* @param totalAount 订单金额
* @return
* @throws AlipayApiException
*/
@GetMapping("/testAliPayTradePay")
public String testAliPayTradePay(
String subject,
String authCode,
String scene,
String outTradeNo,
String totalAount) throws AlipayApiException {
// 创建支付宝客户端
AlipayClient alipayClient = new DefaultAlipayClient(gatewayUrl, appId, privateKey, "json", "UTF-8", publicKey, "RSA2");
// 构造请求参数以调用接口
AlipayTradePayRequest request = new AlipayTradePayRequest();
// 用户在线下交易,创建交易信息并支付交易
AlipayTradePayModel model = new AlipayTradePayModel();
// 设置交易订单标题
model.setSubject(subject);
// 设置支付授权码
model.setAuthCode(authCode);
/**
* 设置支付场景
* 当面付条码支付场景: bar_code
* 当面付刷脸支付场景,对应的auth_code为fp开头的刷脸标识串: security_code
*/
model.setScene(scene);
// 设置商户订单号
model.setOutTradeNo(outTradeNo);
// 设置订单总金额
model.setTotalAmount(totalAount);
request.setBizModel(model);
// 提交支付交易
AlipayTradePayResponse response = alipayClient.execute(request);
// 输出相应数据
System.out.println(response.getBody());
if (response.isSuccess()) {
System.out.println("调用成功");
} else {
System.out.println("调用失败");
}
return response.getBody();
}
}
将付款码数字传入到setAuthCode()方法中
如果有扫码设别的话,扫描付款码即可获取支付授权码
请求参数
测试结果
查询支付
商家可调用接口 [alipay.trade.query](统一收单交易查询接口),通过商家网站唯一订单号 out_trade_no 或支付宝交易号trade_no 查询对应订单支付情况。
/**
* 查询订单
* @param outTradeNode 商户订单号
* @return 返回订单信息
* @throws AlipayApiException
*/
@GetMapping("/getQrCode/{outTradeNode}")
public String selectQrCode(@PathVariable("outTradeNode") String outTradeNode) throws AlipayApiException {
// 创建支付宝客户端
AlipayClient alipayClient = new DefaultAlipayClient(gatewayUrl, appId, privateKey,
"json", "UTF-8", publicKey, "RSA2");
// 构造请求参数以调用接口
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
// 统一收单线下交易查询
AlipayTradeQueryModel model = new AlipayTradeQueryModel();
// 设置订单支付时传入的商户订单号
model.setOutTradeNo(outTradeNode);
// 设置查询选项
List<String> queryOptions = new ArrayList<String>();
queryOptions.add("trade_settle_info");
model.setQueryOptions(queryOptions);
// // 设置支付宝交易号
// model.setTradeNo("2014112611001004680 073956707");
request.setBizModel(model);
AlipayTradeQueryResponse response = alipayClient.execute(request);
System.out.println(response.getBody());
if (response.isSuccess()) {
System.out.println("调用成功");
} else {
System.out.println("调用失败");
// sdk版本是"4.38.0.ALL"及以上,可以参考下面的示例获取诊断链接
// String diagnosisUrl = DiagnosisUtils.getDiagnosisUrl(response);
// System.out.println(diagnosisUrl);
}
return response.getBody();
}
参数名 | 参数说明 |
---|---|
out_trade_no | 支付时传入的商户订单号,与 trade_no 必填一个。 |
trade_no | 支付时返回的支付宝交易号,与 out_trade_no 必填一个。 |
撤销支付
支付交易返回失败或支付系统超时(交易状态不明确),商家可调用接口 [alipay.trade.cancel](统一收单交易撤销接口)通过商家网站唯一订单号 out_trade_no 或支付宝交易号trade_no 撤销交易。
重要说明:
- 如果此订单用户支付失败,支付宝将关闭此订单,用户无法继续支付。
- 如果此订单用户支付成功,支付宝将退还订单资金给用户,交易状态变为 trade_closed(交易关闭)。
- 仅发生支付系统超时或者支付结果未知时可调用本接口撤销交易,其它正常支付的单如需实现相同功能请调用接口 [alipay.trade.refund](统一收单交易退款接口)。
/**
* 撤销订单编号
* @param outTradeNode 商户订单号
* @return
* @throws AlipayApiException
*/
@GetMapping("/cancelQrCode/{outTradeNode}")
public String cancelQrCode(@PathVariable("outTradeNode") String outTradeNode) throws AlipayApiException {
// 创建支付宝客户端
AlipayClient alipayClient = new DefaultAlipayClient(gatewayUrl, appId, privateKey,
"json", "UTF-8", publicKey, "RSA2");
AlipayTradeCancelRequest request = new AlipayTradeCancelRequest();
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", outTradeNode);
request.setBizContent(bizContent.toString());
AlipayTradeCancelResponse response = alipayClient.execute(request);
if(response.isSuccess()){
System.out.println("调用成功");
} else {
System.out.println("调用失败");
}
return response.getBody();
}
}
重要入参说明
注意:请严格按照接口文档中的参数入参,传入非接口文档中的参数是无效的,并且可能会导致请求被拦截或其它异常。
参数名 | 参数说明 |
---|---|
out_trade_no | 支付时传入的商户订单号,与 trade_no 必填一个。 |
trade_no | 支付时返回的支付宝交易号,与 out_trade_no 必填一个。 |
重要出参说明
参数名 | 参数说明 |
---|---|
retry_flag | 是否需要重试,Y/N。 |
action | 本次撤销触发的交易动作。close:关闭交易,无退款 。refund:产生了退款。 |
退款支付
当交易发生之后一段时间内,由于业务原因(如金额错误,用户退款或者对账不平等等)需要退款时,商家可以调用接口 [alipay.trade.refund](统一收单交易退款接口)通过商家网站唯一订单号 out_trade_no 或支付宝交易号trade_no,将对应订单支付款退还给买家,支付宝将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家账号上。支持全额或部分退款。
退款结果
-
退款成功:退款是否成功可以根据同步响应的
fund_change
参数来判断,返回值为Y
则表示退款成功。 -
退款失败:根据 [错误码]确认是否要重试,重试时注意
out_request_no
需保持一致。 -
系统异常:无法确认退款结果。
-
- 方案一:重试,重试时注意
out_request_no
需保持一致。 - 方案二:先调用 [alipay.trade.fastpay.refund.query](统一收单交易退款查询接口)确认退款结果,如失败,再发起重试。
- 方案一:重试,重试时注意
退款说明
- **退款周期:**以签约协议为准,默认12个月,即交易发生后12个月内可发起退款,超期不可退款。
- **退款方式:**资金原路返回用户账号。
- **退款退费:**默认退款时手续费退回。
- 退款时效:支付渠道为花呗、余额等退款即时到账。银行卡的退款时间以银行退款时间为准,一般情况下 2 小时内可到账(若退款到卡失败,退款金额会退回用户支付宝账户余额)。
- 商家可以在 商家平台 > 对账中心 > 交易订单 中退款。
- 退款接口会根据外部请求号
out_request_no
幂等返回,因此同一笔交易需要多次部分退款时,必须使用不同的out_request_no
。
/**
* 退款
* @param outTradeNode 退款账单
* @param refundAmount 退款金额
* @return
* @throws AlipayApiException
*/
@GetMapping("/refundQrCode/{outTradeNode}/{refundAmount}")
public String refundQrCode(
@PathVariable("outTradeNode") String outTradeNode,
@PathVariable("refundAmount")Double refundAmount) throws AlipayApiException {
AlipayClient alipayClient = new DefaultAlipayClient(gatewayUrl, appId, privateKey,
"json", "UTF-8", publicKey, "RSA2");
AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
JSONObject bizContent = new JSONObject();
// bizContent.put("trade_no", "2021081722001419121412730660");
bizContent.put("refund_amount", refundAmount);
bizContent.put("out_trade_no", outTradeNode);
返回参数选项,按需传入
//JSONArray queryOptions = new JSONArray();
//queryOptions.add("refund_detail_item_list");
//bizContent.put("query_options", queryOptions);
request.setBizContent(bizContent.toString());
AlipayTradeRefundResponse response = alipayClient.execute(request);
if(response.isSuccess()){
System.out.println("调用成功");
} else {
System.out.println("调用失败");
}
return response.getBody();
}
重要入参说明
注意:请严格按照接口文档中的参数入参,传入非接口文档中的参数是无效的,并且可能会导致请求被拦截或其它异常。
参数名 | 参数说明 |
---|---|
out_trade_no | 支付时传入的商户订单号,与 trade_no 必填一个。 |
trade_no | 支付时返回的支付宝交易号,与 out_trade_no 必填一个。 |
out_request_no | 本次退款请求流水号,部分退款时必传。 |
refund_amount | 本次退款金额。 |
重要出参说明
参数名 | 参数说明 |
---|---|
refund_fee | 该笔交易已退款的总金额。 |