java支付宝电脑网页支付
官网文档:https://opendocs.alipay.com/open/270/105902
电脑网站支付
1.先给应用申请电脑网页支付功能
2.根据接口文档 ,请求支付宝接口
文档地址
https://opendocs.alipay.com/apis/api_1/alipay.trade.page.pay?scene=API002020081300013629
电脑网站支付需要调用的接口是
alipay.trade.page.pay(统一收单下单并支付页面接口)
上面图片是调取支付宝接口的公共参数:其中
return_url:是支付成功之后,跳转回原网站的页面地址,注意:(这是支付完成后,支付宝重定向回原网站的页面的url,是公网可以直接访问的地址)。
notify_url:支付宝服务器主动通知商户服务器里指定的页面http/https路径,这个是回调地址,就是支付成功之后,支付宝会通过这个地址(接口)发起请求,这个地址也必须是公网可访问的地址。
biz_content:除了公共请求参数之外,其余的请求参数放到这个里面,
biz_content里面有一个请求参数:
qr_pay_mode:PC 扫码支付的方式,支持前置模式和跳转模式。
前置模式分为 0,1,3,4
跳转模式为 2
前置模式是将二维码前置到商户的订单确认页的模式。需要商户在自己的页面中以 iframe 方式请求支付宝页面。具体分为以下几种:0:订单码-简约前置模式,对应 iframe 宽度不能小于600px,高度不能小于300px;1:订单码-前置模式,对应iframe 宽度不能小于 300px,高度不能小于600px;3:订单码-迷你前置模式,对应 iframe 宽度不能小于 75px,高度不能小于75px;4:订单码-可定义宽度的嵌入式二维码,商户可根据需要设定二维码的大小。 跳转模式下,用户的扫码界面是由支付宝生成的,不在商户的域名下。2:订单码-跳转模式
我的例子中使用的是跳转模式,如果要试试其他的模式,改一下参数就行,页面效果都不一样。
3.导入sdk,maven依赖
<!-- https://mvnrepository.com/artifact/com.alipay.sdk/alipay-sdk-java -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.10.167.ALL</version>
</dependency>
4.请求实例
支付宝的请求实例
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do","app_id","your private_key","json","GBK","alipay_public_key","RSA2");
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
request.setBizContent("{" +
"\"out_trade_no\":\"20150320010101001\"," +
"\"product_code\":\"FAST_INSTANT_TRADE_PAY\"," +
"\"total_amount\":88.88," +
"\"subject\":\"Iphone6 16G\"," +
"\"body\":\"Iphone6 16G\"," +
"\"time_expire\":\"2016-12-31 10:05:01\"," +
" \"goods_detail\":[{" +
" \"goods_id\":\"apple-01\"," +
"\"alipay_goods_id\":\"20010001\"," +
"\"goods_name\":\"ipad\"," +
"\"quantity\":1," +
"\"price\":2000," +
"\"goods_category\":\"34543238\"," +
"\"categories_tree\":\"124868003|126232002|126252004\"," +
"\"body\":\"特价手机\"," +
"\"show_url\":\"http://www.alipay.com/xxx.jpg\"" +
" }]," +
"\"passback_params\":\"merchantBizType%3d3C%26merchantBizNo%3d2016010101111\"," +
"\"extend_params\":{" +
"\"sys_service_provider_id\":\"2088511833207846\"," +
"\"hb_fq_num\":\"3\"," +
"\"hb_fq_seller_percent\":\"100\"," +
"\"industry_reflux_info\":\"{\\\\\\\"scene_code\\\\\\\":\\\\\\\"metro_tradeorder\\\\\\\",\\\\\\\"channel\\\\\\\":\\\\\\\"xxxx\\\\\\\",\\\\\\\"scene_data\\\\\\\":{\\\\\\\"asset_name\\\\\\\":\\\\\\\"ALIPAY\\\\\\\"}}\"," +
"\"card_type\":\"S0JP0000\"" +
" }," +
"\"goods_type\":\"0\"," +
"\"timeout_express\":\"90m\"," +
"\"promo_params\":\"{\\\"storeIdType\\\":\\\"1\\\"}\"," +
"\"royalty_info\":{" +
"\"royalty_type\":\"ROYALTY\"," +
" \"royalty_detail_infos\":[{" +
" \"serial_no\":1," +
"\"trans_in_type\":\"userId\"," +
"\"batch_no\":\"123\"," +
"\"out_relation_id\":\"20131124001\"," +
"\"trans_out_type\":\"userId\"," +
"\"trans_out\":\"2088101126765726\"," +
"\"trans_in\":\"2088101126708402\"," +
"\"amount\":0.1," +
"\"desc\":\"分账测试1\"," +
"\"amount_percentage\":\"100\"" +
" }]" +
" }," +
"\"sub_merchant\":{" +
"\"merchant_id\":\"2088000603999128\"," +
"\"merchant_type\":\"alipay: 支付宝分配的间连商户编号, merchant: 商户端的间连商户编号\"" +
" }," +
"\"merchant_order_no\":\"20161008001\"," +
"\"enable_pay_channels\":\"pcredit,moneyFund,debitCardExpress\"," +
"\"store_id\":\"NJ_001\"," +
"\"disable_pay_channels\":\"pcredit,moneyFund,debitCardExpress\"," +
"\"qr_pay_mode\":\"1\"," +
"\"qrcode_width\":100," +
"\"settle_info\":{" +
" \"settle_detail_infos\":[{" +
" \"trans_in_type\":\"cardAliasNo\"," +
"\"trans_in\":\"A0001\"," +
"\"summary_dimension\":\"A0001\"," +
"\"settle_entity_id\":\"2088xxxxx;ST_0001\"," +
"\"settle_entity_type\":\"SecondMerchant、Store\"," +
"\"amount\":0.1" +
" }]," +
"\"settle_period_time\":\"7d\"" +
" }," +
"\"invoice_info\":{" +
"\"key_info\":{" +
"\"is_support_invoice\":true," +
"\"invoice_merchant_name\":\"ABC|003\"," +
"\"tax_num\":\"1464888883494\"" +
" }," +
"\"details\":\"[{\\\"code\\\":\\\"100294400\\\",\\\"name\\\":\\\"服饰\\\",\\\"num\\\":\\\"2\\\",\\\"sumPrice\\\":\\\"200.00\\\",\\\"taxRate\\\":\\\"6%\\\"}]\"" +
" }," +
"\"agreement_sign_params\":{" +
"\"personal_product_code\":\"GENERAL_WITHHOLDING_P\"," +
"\"sign_scene\":\"INDUSTRY|CARRENTAL\"," +
"\"external_agreement_no\":\"test\"," +
"\"external_logon_id\":\"13852852877\"," +
"\"sign_validity_period\":\"2m\"," +
"\"third_party_type\":\"PARTNER\"," +
"\"buckle_app_id\":\"1001164\"," +
"\"buckle_merchant_id\":\"268820000000414397785\"," +
"\"promo_params\":\"{\\\"key\\\",\\\"value\\\"}\"" +
" }," +
"\"integration_type\":\"PCWEB\"," +
"\"request_from_url\":\"https://\"," +
"\"business_params\":\"{\\\"data\\\":\\\"123\\\"}\"," +
"\"ext_user_info\":{" +
"\"name\":\"李明\"," +
"\"mobile\":\"16587658765\"," +
"\"cert_type\":\"IDENTITY_CARD\"," +
"\"cert_no\":\"362334768769238881\"," +
"\"min_age\":\"18\"," +
"\"fix_buyer\":\"F\"," +
"\"need_check_info\":\"F\"" +
" }" +
" }");
AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
if(response.isSuccess()){
System.out.println("调用成功");
} else {
System.out.println("调用失败");
}
请求参数太多了,我们可能只需要简单的支付功能,
我们后台的请求代码
/**
* 统一收单下单并支付页面接口
* @param title 支付名称
* @param amount 支付金额 (元)
* @param outTradeNo 商户订单号 (自己生成的,不重复订单号)
* @return
* @throws Exception
*/
public static String pagePaySubmitOrder(String title, String amount, String outTradeNo) {
String str = null;
//实例化客户端
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do","app_id","your private_key","json","GBK","alipay_public_key","RSA2");
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
// //SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
AlipayTradePagePayModel model = new AlipayTradePagePayModel();
model.setBody("fireantPay");
model.setSubject(title);
// //商户订单号
model.setOutTradeNo(outTradeNo);
//销售产品码,与支付宝签约的产品码名称。注:目前电脑支付场景下仅支持FAST_INSTANT_TRADE_PAY
model.setProductCode("FAST_INSTANT_TRADE_PAY");
// //过期时间
model.setTimeoutExpress("30m");
//总价
model.setTotalAmount(amount);
//商品详情
GoodsDetail gd1 = new GoodsDetail();
gd1.setGoodsId("kc-01");
gd1.setGoodsName(title);
gd1.setQuantity(1L);
gd1.setPrice(amount);
gd1.setBody(title);
ArrayList<GoodsDetail> goodsDetails = new ArrayList<>();
goodsDetails.add(gd1);
model.setGoodsDetail(goodsDetails);
//PC 扫码支付的方式,支持前置模式和跳转模式。
//前置模式是将二维码前置到商户的订单确认页的模式。需要商户在自己的页面中以 iframe 方式请求支付宝页面。具体分为以下几种:0:订单码-简约前置模式,对应 iframe 宽度不能小于600px,高度不能小于300px;1:订单码-前置模式,对应iframe 宽度不能小于 300px,高度不能小于600px;3:订单码-迷你前置模式,对应 iframe 宽度不能小于 75px,高度不能小于75px;4:订单码-可定义宽度的嵌入式二维码,商户可根据需要设定二维码的大小。
//跳转模式下,用户的扫码界面是由支付宝生成的,不在商户的域名下。2:订单码-跳转模式
model.setQrPayMode("2");
//请求参数的集合
request.setBizModel(model);
//回调地址
request.setNotifyUrl(ALiPayConstant.NOTIFY_URL);
//支付成功 网站跳转地址
request.setReturnUrl(ALiPayConstant.RETURN_URL);
try {
//这里和普通的接口调用不同,使用的是sdkExecute
AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
str = response.getBody();
log.info("app调用支付宝参数:"+str);
System.out.println("app调用支付宝参数:"+str);
} catch (AlipayApiException e) {
e.printStackTrace();
}
return str;
}
5.将返回数据渲染到页面上,
上面方法,返回的是一段html代码,一个form表单和提交这个form表单的js代码。
返回实例
<form name="punchout_form" method="post" action="https://openapi.alipay.com/gateway.do?charset=utf-8&method=alipay.trade.page.pay&sign=sign%3D%3D&return_url=https%3A%2F%2Fm.alipay.com%2FGk8NF23¬ify_url=https%3A%2F%2Fwww.baidu%2Faaa%2Fp2p%2notify&version=1.0&app_id=2021010101010101&sign_type=RSA2×tamp=2020-10-28+14%3A00%3A41&alipay_sdk=alipay-sdk-java-4.8.73.ALL&format=json">
<input type="hidden" name="biz_content" value="{"body":"fireantPay","goods_detail":[{"body":"认证支付","goods_id":"kc-01","goods_name":"认证支付","price":"0.01","quantity":1}],"out_trade_no":"BaiDuALi20201028140041468249","product_code":"FAST_INSTANT_TRADE_PAY","qr_pay_mode":"2","subject":"认证支付","timeout_express":"30m","total_amount":"0.01"}">
<input type="submit" value="立即支付" style="display:none" >
</form>
<script>document.forms[0].submit();</script>
这段代码渲染到页面上会自动提交这个表单,因为有<script>document.forms[0].submit();</script>
这段js代码。
但是我在使用vue的时候,ajax请求之后,这个表单并没有自动提交,这样的话页面上展示的就是一个空白的界面了,所以需要在渲染页面之后,再次提交表单,加入以下代码即可
setTimeout(()=>{
document.forms[0].submit();
},100)
页面效果
这里我使用的是跳转模式,代码中 设置 :
model.setQrPayMode("2");
支付成功之后,就会根据之前设置好的return_url地址,重定向到那个页面
6.回调接口实例:
在请求参数中设置的notify_url地址,支付成功之后,支付宝会请求这个接口(直接公网能够访问,不要被拦截了),
实例:
controller层:
@RequestMapping(value = "aliPayNotify")
@ResponseBody
public String aliPayNotifyInterface(HttpServletRequest request) {
return payService.aliPayNotifyService(request);
}
service层:
public String aliPayNotifyService(HttpServletRequest request) {
//获取支付宝POST过来反馈信息
System.out.println("支付宝回调-----");
try {
Map<String, String> params = new HashMap<>();
Map requestParams = request.getParameterMap();
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);
}
//验证签名
boolean flag = ALiPayUtil.aliCheckRsa(params);
if (flag) {
String trade_status = params.get("trade_status");
//支付成功,插入微型支付记录
if (trade_status.equals("TRADE_SUCCESS")) {
//支付校验成功,这里进行业务操作
}else{
System.out.println("支付宝订单-支付状态异常订单:"+JSON.toJSONString(params));
}
return "success";
}else{
System.out.println("支付宝订单-验证失败订单:"+JSON.toJSONString(params));
}
} catch (Exception e) {
e.printStackTrace();
}
return "fail";
}
注意:支付宝回调接口返回 success,代表请求成功,返回其他的代表请求失败,支付宝会不定期再次请求这个接口(如果成功返回success即可,切勿让支付宝重复请求)。
验证签名方法:ALiPayUtil.aliCheckRsa
/**
* 异步回调验证接口
* @param params
* @return
* @throws AlipayApiException
*/
public static boolean aliCheckRsa(Map<String,String> params) throws AlipayApiException {
//切记ALI_PUBLIC_KEY是支付宝的公钥,请去open.alipay.com对应应用下查看。
return AlipaySignature.rsaCheckV1(params, ALI_PUBLIC_KEY, "utf-8", "RSA2");
}