在线支付是指卖方与买方通过因特网上的电子商务网站进行交易时,银行为其提供网上资金结算服务的一种业务。它为企业和个人提供了一个安全、快捷、方便的电子商务应用环境和网上资金结算工具。在线支付不仅帮助企业实现了销售款项的快速归集,缩短收款周期,同时也为个人网上银行客户提供了网上消费支付结算方式,使客户真正做到足不出户,网上购物。
聚合支付:也称“融合支付”,是指只从事“支付、结算、清算”服务之外的“支付服务”,依托银行、非银机构或清算组织,借助银行、非银机构或清算组织的支付通道与清结算能力,利用自身的技术与服务集成能力,将一个以上的银行、非银机构或清算组织的支付服务,整合到一起,为商户提供包括但不限于“支付通道服务”、“集合对账服务”、“技术对接服务”、“差错处理服务”、“金融服务引导”、“会员账户服务”、“作业流程软件服务”、“运行维护服务”、“终端提供与维护”等服务内容,以此减少商户接入、维护支付结算服务时面临的成本支出,提高商户支付结算系统运行效率的,并收取增值收益的支付服务。
回调方式:同步回调、异步回调
回调场景: 告诉商户支付通知结果
同步回调: 整个支付流程完毕,使用同步方式将参数重定向给商户平台,一般场景用于展示结果。
异步回调: 第三方支付接口发一个后台通知给商户平台,一般场景用户修改订单信息。
支付宝开发环境
蚂蚁沙箱环境(Beta)是协助开发者进行接口功能开发及主要功能联调的辅助环境。沙箱环境模拟了开放平台部分产品的主要功能和主要逻辑(当前沙箱支持产品请参考“沙箱支持产品列表”)。
在开发者应用上线审核前,开发者可以根据自身需求,先在沙箱环境中了解、组合和调试各种开放接口,进行开发调通工作,从而帮助开发者在应用上线审核完成后,能更快速、更顺利的进行线上调试和验收工作。
如何使用和配置沙箱环境请参考《沙箱环境使用说明》。
注意:
o由于沙箱为模拟环境,在沙箱完成接口开发及主要功能调试后,请务必在蚂蚁正式环境进行完整的功能验收测试。所有返回码及业务逻辑以正式环境为准。
o为保证沙箱稳定,沙箱环境测试数据会进行定期数据清理。Beta测试阶段每周日中午12点至每周一中午12点为维护时间。在此时间内沙箱环境部分功能可能会不可用,敬请谅解。
o请勿在沙箱进行压力测试,以免触发相应的限流措施,导致无法正常使用沙箱环境。
o沙箱支持的各个开放产品,沙箱使用的特别说明请参考各产品的快速接入文档或技术接入文档章节。
1、创建沙箱环境:
沙箱创建文档
2、注册支付宝开放平台:
进入电脑支付网站支付文档:
3、在文档中下载SDK和demo文档
4、使用支付宝工具生成私钥和公钥文档
5、使用沙箱环境参数导入demo项目,填入参数进行项目运行:
demo启动配置模板:
/* *
*类名:AlipayConfig
*功能:基础配置类
*详细:设置帐户有关信息及返回路径
*修改日期:2017-04-05
*说明:
*以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
*该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
*/
public class AlipayConfig {
//↓↓↓↓↓↓↓↓↓↓请在这里配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
// 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
public static String app_id = "xxxx";
// 商户私钥,您的PKCS8格式RSA2私钥(工具生成的对应的密钥)
public static String merchant_private_key = "xxxxxxx";
// 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。(填入公钥后生成的支付宝公钥)
public static String alipay_public_key = "xxxxxxx";
// 服务器异步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问(使用内网穿透工具发布到公网上自动生成公网地址亦或自己购买的域名地址)
public static String notify_url = "填写项目公网地址/notify_url.jsp";
// 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String return_url = "填写项目公网地址/return_url.jsp";
// 签名方式
public static String sign_type = "RSA2";
// 字符编码格式
public static String charset = "utf-8";
// 支付宝网关
public static String gatewayUrl = "https://openapi.alipaydev.com/gateway.do";
// 支付宝网关
public static String log_path = "F:\\ideaLog\\zhifubaoLog";
//↑↑↑↑↑↑↑↑↑↑请在这里配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
/**
* 写日志,方便测试(看网站需求,也可以改成把记录存入数据库)
* @param sWord 要写入日志里的文本内容
*/
public static void logResult(String sWord) {
FileWriter writer = null;
try {
writer = new FileWriter(log_path + "alipay_log_" + System.currentTimeMillis()+".txt");
writer.write(sWord);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
使用沙箱测试用户账户进行模拟:
如果使用的免费隧道:重启natapp就会改变域名地址:出现该现象
成功:
demo页面
1、创建支付服务接口:
2、创建支付实现层:
逻辑处理、token生成
@RestController
@Slf4j
public class PayServiceImpl extends BaseApiService implements PayService {
@Autowired
private PayServiceDao payServiceDao;
//插入支付信息生成token
@Override
public ResponseBase createToken(@RequestBody PaymentInfo paymentInfo) {
Integer payToken = payServiceDao.savePaymentType(paymentInfo);
if (payToken <= 0) {
log.info("paymentInfo" + paymentInfo.toString());
return setResultError("支付信息生成插入失败");
}
String memberToken = TokenUtils.getPayToken();
baseRedisService.setString(memberToken, paymentInfo.getId(), Constants.PAY_COOKIE_TOKEN_MEMBER_TIME);
JSONObject jsonObject = new JSONObject();
jsonObject.put("payToken", memberToken);
return setResultSuccess(jsonObject);
}
//用获取的支付token去查询信息是否有效
@Override
public ResponseBase findByIdToken(@RequestParam("payToken") String payToken) throws AlipayApiException {
//验证token
if (StringUtils.isEmpty(payToken)) {
return setResultError("token不能为空");
}
//查找token
String strTokenId = (String) baseRedisService.getString(payToken);
if (StringUtils.isEmpty(strTokenId)) {
return setResultError("支付超时");
}
Long payId = Long.parseLong(strTokenId);
//查找支付信息
PaymentInfo paymentInfo = payServiceDao.getPaymentInfo(payId);
if (null == paymentInfo) {
return setResultError("未生成支付订单");
}
//使用支付信息对接支付宝
//获得初始化的AlipayClient
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);
//设置请求参数
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
alipayRequest.setReturnUrl(AlipayConfig.return_url);
alipayRequest.setNotifyUrl(AlipayConfig.notify_url);
//商户订单号,商户网站订单系统中唯一订单号,必填
String out_trade_no = paymentInfo.getOrderId();
//付款金额,必填
String total_amount = paymentInfo.getPrice() + "";
//订单名称,必填
String subject = "个人学习";
//商品描述,可空
String body = "";
alipayRequest.setBizContent("{\"out_trade_no\":\"" + out_trade_no + "\","
+ "\"total_amount\":\"" + total_amount + "\","
+ "\"subject\":\"" + subject + "\","
+ "\"body\":\"" + body + "\","
+ "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
//若想给BizContent增加其他可选请求参数,以增加自定义超时时间参数timeout_express来举例说明
//alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
// + "\"total_amount\":\""+ total_amount +"\","
// + "\"subject\":\""+ subject +"\","
// + "\"body\":\""+ body +"\","
// + "\"timeout_express\":\"10m\","
// + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
//请求参数可查阅【电脑网站支付的API文档-alipay.trade.page.pay-请求参数】章节
//请求
String result = null;
result = alipayClient.pageExecute(alipayRequest).getBody();
//输出
JSONObject jsonObject = new JSONObject();
jsonObject.put("payHtml", result);
return setResultSuccess(jsonObject);
}
}
公共工具类等
引入相关配置:(分库实现)
server:
port: 8768
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
spring:
application:
name: pay
devtools:
restart:
enabled: true #设置开启热部署
additional-paths: src/main/java #重启目录
freemarker:
cache: false #页面不加在缓存,修改及时生效
#redis引用
redis:
host: 127.0.0.1
password:
port: 6379
jedis:
pool:
max-active: 1000
max-wait: -1
max-idle: 100
min-idle: 1
#数据库引用
datasource:
name: testsys
url: jdbc:mysql://127.0.0.1:3306/testpay?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai&useSSL=false
username: root
password: admin
# 使用druid数据源
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
filters: stat
maxActive: 20
initialSize: 1
maxWait: 60000
minIdle: 1
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxOpenPreparedStatements: 20
3、创建web层,利用fegin调用接口实现数据传输和页面跳转
fegin远程调用
@Component
@FeignClient("pay")
public interface PayFeignService extends PayService {
}
进入支付页面
@Slf4j
@Controller
public class PayController extends BaseApiService {
@Autowired
private PayFeignService PayFeignService;
//插入支付信息
@RequestMapping("/aliPayInfo")
public ResponseBase aliPayInfo(PaymentInfo paymentInfo,HttpServletResponse response){
ResponseBase responseBase = valiData(paymentInfo);
if(!responseBase.getCode().equals(Constants.HTTP_RES_CODE_200)){
return setResultError(responseBase.getMsg());
}
ResponseBase token = PayFeignService.createToken(paymentInfo);
if (!token.getCode().equals(Constants.HTTP_RES_CODE_200)){
return setResultError("支付信息生成失败");
}
LinkedHashMap data = (LinkedHashMap) token.getData();
String payToken = (String) data.get("payToken");
setCookie(payToken,response);//存入token
return responseBase;
}
/**
* 添加客户端cookie
*
* @param token
* @param response
*/
private void setCookie(String token, HttpServletResponse response) {
CookieUtil.addCookie(response, Constants.USER_PAY_COOKIE_TOKEN_MEMBER, token, Constants.USER_PAY_COOKIE_TOKEN_MEMBER_TIME);
}
private ResponseBase valiData(PaymentInfo paymentInfo) {
if (StringUtils.isEmpty(paymentInfo.getPrice()+"")){
return setResultError("支付金额不能为空");
}
return setResultSuccess();
}
//获取token完成支付
@RequestMapping("/aliPay")
public void aliPay(String payToken, HttpServletResponse response)throws IOException, AlipayApiException {
PrintWriter writer = response.getWriter();
response.setContentType("text/html;charset=utf-8");
if(StringUtils.isEmpty(payToken)){
return;
}
ResponseBase payTokenResult = PayFeignService.findByIdToken(payToken);
if (!payTokenResult.getCode().equals(Constants.HTTP_RES_CODE_200)){
String msg = payTokenResult.getMsg();
return ;
}
LinkedHashMap data = (LinkedHashMap) payTokenResult.getData();
String payHtml = (String) data.get("payHtml");
writer.print(payHtml);
writer.close();
}
}