参考链接
支付宝开发者控制台:登录 - 支付宝
扫码支付API文档:小程序文档 - 支付宝文档中心
一、支付宝沙箱环境准备
1.1 支付宝沙箱介绍
沙箱环境是支付宝开放平台为开发者提供的与生产环境完全隔离的联调测试环境,开发者在沙箱环境中完成的接口调用不会对生产环境中的数据造成任何影响。
沙箱为开放的产品提供有限功能范围的支持,可以覆盖产品的绝大部分核心链路和对接逻辑,便于开发者快速学习/尝试/开发/调试。
沙箱环境会自动完成或忽略一些场景的业务门槛,例如:开发者无需等待产品开通,即可直接在沙箱环境调用接口,使得开发集成工作可以与业务流程并行,从而提高项目整体的交付效率。
注意:
- 由于沙箱环境并非 100% 与生产环境一致,接口的实际响应逻辑请以生产环境为准,沙箱环境开发调试完成后,仍然需要在生产环境进行测试验收。
- 沙箱环境拥有完全独立的数据体系,沙箱环境下返回的数据(比如用户 ID 等)在生产环境中都是不存在的,开发者不可将沙箱环境返回的数据与生产环境中的数据混淆。
1.2 支付宝沙箱注册及配置
支付宝开发者控制台页面:登录 - 支付宝
登陆后进入开放平台控制台:选择左下解沙箱

在沙箱应用中可以查看沙箱的相关信息,其中APPID需要复制并记录,我们在进行支付时要指定APPID。

其它的均保持默认配置即可,接口的加签方式选择系统默认密钥即可,选用公钥模式,点击查看可以看到对应的公钥和私钥:做支付时需要用到。

支付宝网关地址:https://openapi-sandbox.dl.alipaydev.com/gateway.do
支付宝沙箱网关地址,开发者在沙箱环境调用 OpenAPI 发送 http(s) 请求的目标地址,需配置在AlipayClient中。此地址为固定的,在程序中需要配置。
选择左边沙账户:创建申请个人和商家对应的虚拟账户,并可以在线模拟充值和取现。

至此,我们需要准备的沙箱环境和配置己准备就绪。
二、内网穿透
参考文档:06-Nps 新增Windows穿透主机
三、沙箱支付相关API说明
支付宝API官方文档:开放平台

扫码支付是web网站的最佳选择,而扫码支付是当面付的其中一种。
扫码支付官方API:小程序文档 - 支付宝文档中心
各个接口有在线文档和在线调试的案例,可以自行查看。以其中的扫码支付为例说明:
package com.java.sdk.demo;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.CertAlipayRequest;
import com.alipay.api.AlipayConfig;
import com.alipay.api.domain.AlipayTradePrecreateModel;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.alipay.api.request.AlipayTradePrecreateRequest;
import com.alipay.api.FileItem;
import java.util.Base64;
import java.util.ArrayList;
import java.util.List;
public class AlipayTradePrecreate {
public static void main(String[] args) throws AlipayApiException {
String privateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCs5FX6NlZEq8t43tnAT91hcGhTPL6Iieq/e1foh3N0ME601PwtfXzFPac0Qp5qygLtyZkYFY8UiYqdXPQpvO4T4ah5Rus8IiCwGDBpyg3ha6xSWnD7QCvc/YgOJ+ei1XD6F5Y+t2X3gbTCsy1H9wTe9XLh78tcpuhib/4OEwxJfHxEi5fBzQQJBE6vo8pX7mTeeP7f1RBxAgRsswMvCQs1SX362XdD3IRWErf8dUOqV0tT5ahjhXdbFv6fUQmnt+1i00CIAlOPm7vLWslw1qJT4ubZuL0HPQLWEoD3ab0OdgaJvPdoWvnUVzBQqwZgnXHFN5nV1QQbVTe3Q3XWIWTNAgMBAAECggEBAI2JI8WBRCBVj2306cgg2XfSQOWbfdNjLHTzMaUfdemxPk2VapiG+WSzMzV6MBv2Im2tsxHfXFGQw5RNq2ibzT8jZftajmqc+auPMdr42WIn9ls4xPM6tm3kc8Q68MsB9soWEx5mnyf+3A7kBfa7BxbLUvAZkZ7Y2Lb1PcMh3tYso7FYERStxpdtUgPSkqXgZERrzrtHpFDe05CI/xjZUfmvXsTrKiIXxqvil9hYGISkMyVJjaJUXvZwmgzLtHd7I8z7sNXOw26Ie1DihCy3VzPh5pwZPQId2v2LBABdUogTwVfb7GAV6swP+OMxoffYPdW7AV+82zOYybm3GjKCTiECgYEA57ukyNybLE7h7yeDSFKdkoBnY7RmIW4NRwQNFopb5LlNt9vTm5easuEdDhPlGoNpJ5Ul/ms9pDVDnH2ZkU7IaiMPd/MxQpQ5dos5tRsjb02YwligiD3ZxDBRCNSjcqiR7+5jYb68H2c8bXLtOtZ/dsYyqZmEhH0OsV/2WKMr9bUCgYEAvv9EO9J2UEVBPkEKvhLfH7X8GZK6CROEehNG8qbDqxJ5AbFmo8lzNvbcHjJoiwEMzYU1q5VMhMVMepk1Lk5Q4VRouAMTUY3o8byQMeX7BYHlmmLaXuw8NAFFE43Mr14UGkrWesZR9u1LSw0QPNag3cyL2XxmgpWCmPAwLzQ+obkCgYEAnFukKFOR8CeZkwCaOGZmI7+4AzJp5wyWsOAu5JKexomxwuj0svtsSl5SeHk8ENOyVB7y7+P0R6QY5rJot/7sg5D8kvbouf/Bdhci7OxO0H8PH7MlPkhdc18WjlrLnkXXkojLTdygmp/RyhfNsfGhN8crz/vhynOHQm95jP0sYDECgYBGJ2zgrEVY5pc4TpurhTL/atDWc9ZxTwHx9PKyXh6wz3Ay9v5EWtI+9h2T2eAcbp5NPwifpY8dlXqp7WxjPdDncyGjtT17Tyyo3iH4sgip1TSIqJadFxhl4bGFpSfxohSwM5zkK8QfdrmZL4svTYZ48ZDMqazdeh0mH9MTA5WdwQKBgFO79TUR5fq+u0sBjYW7VLiQ5J6Tq7U0phFjFFjR/eYqkKUtE5K/BdOsSQjwY21TOkoTazyDJiVLEx5pWQueIa0ez1WPNswz7VN2su9N/jwP+G5ILrz1I+nj2PiWXVbfWZ2qd2h0qB27CF7gZ9lQgjg5dQMt4Fmd7f8uzBzrwLQW";
String alipayPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAydfxVYSMISxqvitww5063ZQFVRzrcRW9a+xQ0Pozz7ve9IeK1h9p7xFwSUtTNi/ki9ZcnZ7wP0xunKrYPVXyUvRz/vSVnovkzvmcSTdqlgtzTAQTJFYL0nmzhnv4RWbcDnVbo+IIlu8g0p6wQv7V8P5EGpbh63+d3q/DYhsbxVVZN1fKjnw/y38/GuqGoiaxkcrf6h65rGO/aRGbX6gbGnRxUgBOEiFGFdkuCx9ACg4cskSme69lDbIBDpsmSbQpUKhTEA4Wv36mqrbee+hGf5sTaqDUHl0/SPgP45ab15J3BeWHB/yXbdK4Z/gPfMFMweOupg54Sm+kJOGmq73cMwIDAQAB";
AlipayConfig alipayConfig = new AlipayConfig();
alipayConfig.setServerUrl("https://openapi.alipaydev.com/gateway.do");
alipayConfig.setAppId("2021000121607425");
alipayConfig.setPrivateKey(privateKey);
alipayConfig.setFormat("json");
alipayConfig.setAlipayPublicKey(alipayPublicKey);
alipayConfig.setCharset("UTF8");
alipayConfig.setSignType("RSA2");
AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig);
AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();
model.setOutTradeNo("20150320010101001");
model.setTotalAmount("88.88");
model.setSubject("Iphone6 16G");
request.setBizModel(model);
AlipayTradePrecreateResponse response = alipayClient.execute(request);
//可以将此字符串返回到客户端,使用二维码生成工具来生成二维码图片即可
System.out.println("支付地址:"+response.getQrCode());
if (response.isSuccess()) {
System.out.println("调用成功");
} else {
System.out.println("调用失败");
}
}
}
四、沙箱支付功能实现
以电脑网站扫码支付为例进行说明
4.1 支付功能的实现
1、创建配置类:配置沙箱相关环境信息 把配置类中相关的信息改为自己的即可。
package com.qiu.config;
import com.qiu.util.general.PropertiesUtil;
import lombok.Data;
import org.springframework.stereotype.Component;
/**
* @author ZNZ
*/
@Data
@Component
public class AlipayConfig {
/**
* 沙箱appId
*/
public static final String APPID = "2021000121607425";
/**
* 请求网关 固定
*/
public static final String URL = "https://openapi.alipaydev.com/gateway.do";
/**
* 设置内网穿透回调地址
*/
public static final String CALLBACK = "fcswjw.natappfree.cc";
/**
* 编码
*/
public static final String CHARSET = "UTF-8";
/**
* 返回格式
*/
public static final String FORMAT = "json";
/**
* RSA2
*/
public static final String SIGNTYPE = "RSA2";
/**
* 异步通知地址
*/
public static final String NOTIFY_URL = "http://"+CALLBACK+"/alipay/notify";
/**
* 同步地址
*/
//使用内网穿透进行回调
public static final String RETURN_URL = "http://"+CALLBACK+"/alipay/success";
/**
* 应用私钥 pkcs8格式
*/
public static final String RSA_PRIVATE_KEY =
"MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCs5FX6NlZEq8t43tnAT91hcGhTPL6Iieq/e1foh3N0ME601PwtfXzFPac0Qp5qygLtyZkYFY8UiYqdXPQpvO4T4ah5Rus8IiCwGDBpyg3ha6xSWnD7QCvc/YgOJ+ei1XD6F5Y+t2X3gbTCsy1H9wTe9XLh78tcpuhib/4OEwxJfHxEi5fBzQQJBE6vo8pX7mTeeP7f1RBxAgRsswMvCQs1SX362XdD3IRWErf8dUOqV0tT5ahjhXdbFv6fUQmnt+1i00CIAlOPm7vLWslw1qJT4ubZuL0HPQLWEoD3ab0OdgaJvPdoWvnUVzBQqwZgnXHFN5nV1QQbVTe3Q3XWIWTNAgMBAAECggEBAI2JI8WBRCBVj2306cgg2XfSQOWbfdNjLHTzMaUfdemxPk2VapiG+WSzMzV6MBv2Im2tsxHfXFGQw5RNq2ibzT8jZftajmqc+auPMdr42WIn9ls4xPM6tm3kc8Q68MsB9soWEx5mnyf+3A7kBfa7BxbLUvAZkZ7Y2Lb1PcMh3tYso7FYERStxpdtUgPSkqXgZERrzrtHpFDe05CI/xjZUfmvXsTrKiIXxqvil9hYGISkMyVJjaJUXvZwmgzLtHd7I8z7sNXOw26Ie1DihCy3VzPh5pwZPQId2v2LBABdUogTwVfb7GAV6swP+OMxoffYPdW7AV+82zOYybm3GjKCTiECgYEA57ukyNybLE7h7yeDSFKdkoBnY7RmIW4NRwQNFopb5LlNt9vTm5easuEdDhPlGoNpJ5Ul/ms9pDVDnH2ZkU7IaiMPd/MxQpQ5dos5tRsjb02YwligiD3ZxDBRCNSjcqiR7+5jYb68H2c8bXLtOtZ/dsYyqZmEhH0OsV/2WKMr9bUCgYEAvv9EO9J2UEVBPkEKvhLfH7X8GZK6CROEehNG8qbDqxJ5AbFmo8lzNvbcHjJoiwEMzYU1q5VMhMVMepk1Lk5Q4VRouAMTUY3o8byQMeX7BYHlmmLaXuw8NAFFE43Mr14UGkrWesZR9u1LSw0QPNag3cyL2XxmgpWCmPAwLzQ+obkCgYEAnFukKFOR8CeZkwCaOGZmI7+4AzJp5wyWsOAu5JKexomxwuj0svtsSl5SeHk8ENOyVB7y7+P0R6QY5rJot/7sg5D8kvbouf/Bdhci7OxO0H8PH7MlPkhdc18WjlrLnkXXkojLTdygmp/RyhfNsfGhN8crz/vhynOHQm95jP0sYDECgYBGJ2zgrEVY5pc4TpurhTL/atDWc9ZxTwHx9PKyXh6wz3Ay9v5EWtI+9h2T2eAcbp5NPwifpY8dlXqp7WxjPdDncyGjtT17Tyyo3iH4sgip1TSIqJadFxhl4bGFpSfxohSwM5zkK8QfdrmZL4svTYZ48ZDMqazdeh0mH9MTA5WdwQKBgFO79TUR5fq+u0sBjYW7VLiQ5J6Tq7U0phFjFFjR/eYqkKUtE5K/BdOsSQjwY21TOkoTazyDJiVLEx5pWQueIa0ez1WPNswz7VN2su9N/jwP+G5ILrz1I+nj2PiWXVbfWZ2qd2h0qB27CF7gZ9lQgjg5dQMt4Fmd7f8uzBzrwLQW";
/**
* 沙箱支付宝公钥
*/
public static final String ALIPAY_PUBLIC_KEY =
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAydfxVYSMISxqvitww5063ZQFVRzrcRW9a+xQ0Pozz7ve9IeK1h9p7xFwSUtTNi/ki9ZcnZ7wP0xunKrYPVXyUvRz/vSVnovkzvmcSTdqlgtzTAQTJFYL0nmzhnv4RWbcDnVbo+IIlu8g0p6wQv7V8P5EGpbh63+d3q/DYhsbxVVZN1fKjnw/y38/GuqGoiaxkcrf6h65rGO/aRGbX6gbGnRxUgBOEiFGFdkuCx9ACg4cskSme69lDbIBDpsmSbQpUKhTEA4Wv36mqrbee+hGf5sTaqDUHl0/SPgP45ab15J3BeWHB/yXbdK4Z/gPfMFMweOupg54Sm+kJOGmq73cMwIDAQAB";
private AlipayConfig() {
}
}
2、定义支付接口地址:用户点击立即购买后提交请求至下单接口,添加订单成功后,发送请求到支付接口,向支付宝发送支付请求。
package com.java.sdk.demo;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.AlipayConfig;
import com.alipay.api.domain.AlipayTradePrecreateModel;
import com.alipay.api.domain.ExtendParams;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.alipay.api.domain.GoodsDetail;
import com.alipay.api.domain.BusinessParams;
import com.alipay.api.request.AlipayTradePrecreateRequest;
import com.alipay.api.FileItem;
import java.util.Base64;
import java.util.ArrayList;
import java.util.List;
public class AlipayTradePrecreate {
public static void main(String[] args) throws AlipayApiException {
// 初始化SDK
AlipayClient alipayClient = new DefaultAlipayClient(getAlipayConfig());
// 构造请求参数以调用接口
AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();
// 设置商户订单号
model.setOutTradeNo("20150320010101001");
// 设置订单总金额
model.setTotalAmount("88.88");
// 设置订单标题
model.setSubject("Iphone6 16G");
// 设置产品码
model.setProductCode("FACE_TO_FACE_PAYMENT");
// 设置卖家支付宝用户ID
model.setSellerId("2088102146225135");
// 设置订单附加信息
model.setBody("Iphone6 16G");
// 设置订单包含的商品列表信息
List<GoodsDetail> goodsDetail = new ArrayList<GoodsDetail>();
GoodsDetail goodsDetail0 = new GoodsDetail();
goodsDetail0.setGoodsName("ipad");
goodsDetail0.setQuantity(1L);
goodsDetail0.setPrice("2000");
goodsDetail0.setGoodsId("apple-01");
goodsDetail0.setGoodsCategory("34543238");
goodsDetail0.setCategoriesTree("124868003|126232002|126252004");
goodsDetail0.setShowUrl("支付宝");
goodsDetail.add(goodsDetail0);
model.setGoodsDetail(goodsDetail);
// 设置业务扩展参数
ExtendParams extendParams = new ExtendParams();
extendParams.setSysServiceProviderId("2088511833207846");
extendParams.setSpecifiedSellerName("XXX的跨境小铺");
extendParams.setCardType("S0JP0000");
model.setExtendParams(extendParams);
// 设置商户传入业务信息
BusinessParams businessParams = new BusinessParams();
businessParams.setMcCreateTradeIp("127.0.0.1");
model.setBusinessParams(businessParams);
// 设置可打折金额
model.setDiscountableAmount("80.00");
// 设置商户门店编号
model.setStoreId("NJ_001");
// 设置商户操作员编号
model.setOperatorId("yx_001");
// 设置商户机具终端编号
model.setTerminalId("NJ_T_001");
// 设置商户的原始订单号
model.setMerchantOrderNo("20161008001");
request.setBizModel(model);
// 第三方代调用模式下请设置app_auth_token
// request.putOtherTextParam("app_auth_token", "<-- 请填写应用授权令牌 -->");
AlipayTradePrecreateResponse 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);
}
}
private static AlipayConfig getAlipayConfig() {
String privateKey = "<-- 请填写您的应用私钥,例如:MIIEvQIBADANB ... ... -->";
String alipayPublicKey = "<-- 请填写您的支付宝公钥,例如:MIIBIjANBg... -->";
AlipayConfig alipayConfig = new AlipayConfig();
alipayConfig.setServerUrl("支付宝 - 网上支付 安全快速!");
alipayConfig.setAppId("<-- 请填写您的AppId,例如:2019091767145019 -->");
alipayConfig.setPrivateKey(privateKey);
alipayConfig.setFormat("json");
alipayConfig.setAlipayPublicKey(alipayPublicKey);
alipayConfig.setCharset("UTF-8");
alipayConfig.setSignType("RSA2");
return alipayConfig;
}
}
此接口返回的字符串其实是一个JSON数据。
{
"alipay_trade_precreate_response": {
"code": "10000",
"msg": "Success",
"out_trade_no": "6823789339978248",
"qr_code": "支付宝"
},
"sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
}
然后就进入到了开头展示的支付流程页面。
可以对照着沙箱的商家账户和买家账户的钱数来看看是否实现了买家账户扣款和商家账户收款的功能。

4.2 回调本地接口更改支付状态
在线支付成功了,买家支付宝的账户的钱也已经转移到了商家的账户,可是本地应用怎么知道支付是否成功呢?我们在本地的应用中也需要根据支付成功与否来更改订单的支付状态,这就需要我们定义回调通知接口来实现。
我们在AlipayConfig中已经定义了回调的接口地址:
/**
* 异步通知地址
*/
public static final String NOTIFY_URL = "http://"+CALLBACK+"/alipay/notify";
那么这个接口主要实现的功能就是根据支付的结果来实现订单状态的更新:
在AlipayController中定义alipay/notify接口:
/**
* @param map
*/
@ResponseBody
@PostMapping(value = "/notify")
public String payNotify(@RequestParam Map<String, String> map) {
if (TRADE_SUCCESS.equals(map.get(TRADE_STATUS))) {
String payTime = map.get(TRADE_TIME);
String tradeNo = map.get(OUT_TRADE_NO);
String tradeName = map.get(TRADE_NAME);
String payAmount = map.get(TRADE_AMOUNT);
log.info("[支付成功] {交易时间:{},订单号:{},订单名称:{},交易金额:{}}", payTime, tradeNo, tradeName, payAmount);
}
return "success";
}
/**
* 支付回调成功
* @param map
* @param response
*/
@GetMapping(value = "/success")
public void success(@RequestParam Map<String, String> map, HttpServletResponse response) {
try {
String tradeNo = map.get(OUT_TRADE_NO);
if (tradeNo.contains(VIP)) {
openMember(response, tradeNo);
} else {
updateProductStatus(response, tradeNo);
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 支付成功,更新商品状态为待发货
*
* @param response HTTP响应
* @param tradeNo 订单编号
* @throws IOException IO异常信息
*/
private void updateProductStatus(HttpServletResponse response, String tradeNo) throws IOException {
Integer orderId = orderService.selectIdByKey(orderNo);
Order order = new Order();
order.setOrderId(orderId);
order.setOrderState("待发货");
orderService.updateById(order);
//跳转到我的订单页面
response.sendRedirect("http://localhost:8080/#/myOrder");
}
310

被折叠的 条评论
为什么被折叠?



