微信公众号H5页面使用支付包手机网站支付!!!
这两天根据公司的需求写了一个从微信公众号的H5(公众号中的网站)页面跳转支付宝支付,大家应该都知道微信和支付宝这两家 谁也不让这谁。想在微信中用支付宝那微信肯定不愿意,但是需求这就是这样的,很庆幸支付宝的文档太棒了说的很明白比微信温柔太多,接下来我会详细的讲解一下,已经帮助大家避坑。
首先先看文档,如果直接看代码那没有注释的情况下是完全迷糊的,
文档
我们先去找一下沙箱环境,支付宝这种大佬级别的公司肯定是有的
https://openhome.alipay.com/platform/appDaily.htm
这里可以看到支付宝提供的测试使用的APPID和UID
把这个下载了,这个是沙箱环境的支付宝,用于后期测试使用。
下载后这里有支付宝提供的账号密码,登陆买家的。
然后这个沙箱环境的搞完了,然后去看一下支付宝提供的付款链条工具
这个蚂蚁技术支持中心
https://opensupport.alipay.com/support/tools?ant_source=antsupport
这下可以看到Java源码了,别激动慢慢来,看到了不一定可以用
这下感觉好简单好轻松,别介这个坑我已经替你们踩了,难为了我半个小时头都快炸了
代码粘走然后去maven仓库拉个jar
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
好了,先去看下接口文档的要求,一步一步来不然你会踩很多坑而且很难受,不好排查的坑
https://opendocs.alipay.com/apis/api_1/alipay.trade.wap.pay
进去看参数
一个公共参数就是你请求网关的时候需要的参数
另一个是请求参数,为了测试方便先把需要必传的参数传了,后边在根据自己的需求去加
这个才是成功和失败的响应其他的响应没有。
成功的事例就是一个from表单。
然后这样做了个大概了解。
代码
上代码
公用参数类
package com.dg_apl_xnbi.model.payment;
/**
* @author: LiJia
* @date: 2021-09-26 16:59
**/
public class aliPayParam {
// 支付宝网关 测试
public static String ALIPAY ="https://openapi.alipaydev.com/gateway.do";
// 应用appId 测试
public static String APPID = "";
// 支付宝公钥 测试
public static String PUBLICKEY = "";
// 支付宝商户uID
public static String UID = "";
// 返回格式JSON
public static String FORMAT ="json";
// REA2
public static String STGUTYPE = "RSA2";
// 支付宝同步跳转地址
public static String RETURN_URL="";
// 支付宝异步跳转地址
public static String NOTIFYURL = "";
// 编码格式
public static String CHARSET = "UTF-8";
// 用户取消支付回跳地址
public static String QUITURL = "";
}
这里边空着的根据你自己的填写上去,‘
这里边有几个比较容易踩坑的地方,
第一个
同步地址:
意思是用户支付完后跳转的,大多数会跳转到订单或者商城页面,
第二个
异步地址:
这个是你和支付宝异步回调要使用的,就是支付完成后支付宝给你发的支付成功的地址,以及支付时间等等,这个地方直接使用你的接口地址,后边会说到 现在可以随岁随便写一个,
第三个
公钥和私钥
拿到这个两个地址在
https://openhome.alipay.com/platform/appDaily.htm
这个是支付接口
我这里用的logger比较多为了防止上线了不知道哪里出问题
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.*;
import com.alipay.api.request.AlipayTradeWapPayRequest;
import com.alipay.api.response.AlipayTradeWapPayResponse;
import com.dg_apl_xnbi.model.payment.DGPayInfo;
import com.dg_apl_xnbi.model.payment.aliPayParam;
import com.dg_apl_xnbi.service.payment.DGPayInfoService;
import com.dg_apl_xnbi.util.PayUtil;
import org.springframework.util.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.Timestamp;
import static java.lang.System.out;
/**
* @author: LiJia
* @date: 2021-09-26 16:59
**/
@RestController
@RequestMapping("/xnbi/aliPay")
public class aliPayOneController {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(aliPayOneController.class);
@RequestMapping(value = "/restAliPay",method = RequestMethod.GET)
@ResponseBody
public void restAliPay(HttpServletRequest request, HttpServletResponse response) throws AlipayApiException, IOException, NullPointerException {
logger.info("**********************开始支付**********************");
logger.info("**********************检查参数**********************");
// 订单编号
String outTradeNo =request.getParameter("outTradeNo");
logger.info("*************我是outTradeNo=" +outTradeNo+"*************");
// 订单总价
String totalAmount = request.getParameter("totalAmount");
logger.info("*************我是totalAmount=" +totalAmount+"*************");
// 订单标题
String subject = request.getParameter("subject");
logger.info("*************我是subject=" +subject+"*************");
// 商品内容
String body = request.getParameter("body");
logger.info("*************我是body=" +body+"*************");
// 支付平台
String applyName = request.getParameter("applyName");
logger.info("*************我是applyName=" +applyName+"*************");
// 产品码 默认QUICK_WAP_PAY
String productCode = "QUICK_WAP_PAY";
// 本地回调地址
String notifyUrl = request.getParameter("notifyUrl");
logger.info("*************我是notifyUrl=" +notifyUrl+"*************");
// 本地跳转地址
String returnUrl = request.getParameter("returnUrl");
logger.info("*************我是returnUrl=" +returnUrl+"*************");
// 场景信息
String sceneInfo = request.getParameter("sceneInfo");
logger.info("*************我是sceneInfo=" +sceneInfo+"*************");
logger.info("**********************验证参数**********************");
if(StringUtils.isBlank(outTradeNo)){
logger.info("restAliPay :outTradeNo not null 65 line" + "outTradeNo=" +outTradeNo);
}
if(StringUtils.isBlank(totalAmount)){
logger.info("restAliPay :totalAmount not null 68 line" +"totalAmount=" + totalAmount);
}
if(StringUtils.isBlank(subject)){
logger.info("restAliPay :subject not null 71 line "+"subject=" + subject);
}
if(StringUtils.isBlank(body)){
logger.info("restAliPay :body not null 74 line "+"body=" + body);
}
if(StringUtils.isBlank(applyName)){
logger.info("restAliPay :applyName not null 77 line "+"applyName=" + applyName);
}
if(StringUtils.isBlank(notifyUrl)){
logger.info("restAliPay :notifyUrl not null 80 line "+"notifyUrl=" + notifyUrl);
}
if(StringUtils.isBlank(returnUrl)){
logger.info("restAliPay :returnUrl not null 93 line "+"returnUrl=" + returnUrl);
}
if(StringUtils.isBlank(returnUrl)){
logger.info("restAliPay :returnUrl not null 93 line "+"returnUrl=" + returnUrl);
}
// 这里编写你的业务代码添加数据库啊或者修改你的订单表啊
/**
*增删改查
*/
// 这里就看你的业务需求了
logger.info("**********************开始调用支付宝**********************");
// 这里是容易踩坑的地方
AlipayClient alipayClient = new DefaultAlipayClient(aliPayParam.ALIPAY,aliPayParam.APPID,
aliPayParam.PRIVATEKEY,aliPayParam.FORMAT,aliPayParam.CHARSET,aliPayParam.PUBLICKEY,aliPayParam.STGUTYPE);
AlipayTradeWapPayRequest payRequest = new AlipayTradeWapPayRequest();
// 支付同步返回地址
payRequest.setReturnUrl(returnUrl);
// 支付异步通知地址
payRequest.setNotifyUrl(aliPayParam.NOTIFYURL);
// 这个地方是请求支付的参数订单一类的 订单号和金额等等。
JSONObject jsonObject = new JSONObject();
// 订单编号
jsonObject.put("out_trade_no",outTradeNo);
// 订单总金额
jsonObject.put("total_amount",totalAmount);
// 订单标题
jsonObject.put("subject",subject);
// 产品码
jsonObject.put("product_code",productCode);
// 订单取消支付跳转链接
jsonObject.put("quit_url",aliPayParam.QUITURL);
// 商户Id
jsonObject.put("seller_id",aliPayParam.UID);
// 将订单所有内容放入
payRequest.setBizContent(jsonObject.toString());
out.println(payRequest.getBizContent());
logger.info("**********************返回支付调用结果结果**********************");
// 这里是容易踩坑的地方
AlipayTradeWapPayResponse payResponse = alipayClient.pageExecute(payRequest,"get");
out.println("我在这里"+payResponse.getBody());
logger.info("**********************"+"我在这里"+payResponse.getBody()+"**********************");
// 判断返回结果返回相应值
if(payResponse.isSuccess()){
logger.info("**********************接口调用成功信息**********************");
} else {
logger.info("**********************接口调用失败失败信息**********************");
logger.info("restAliPay : The payment interface call failed 153 line");
}
String form = "";
try {
// 调用SDK生成表单
form = alipayClient.pageExecute(payRequest).getBody();
} catch (AlipayApiException e) {
logger.info("**********************调用SDK生成表单异常抛出 160line**********************");
logger.info("restAliPay : Call the SDK to generate the form error 160 line");
e.printStackTrace();
}
response.setContentType("text/html;charset=" + aliPayParam.CHARSET);
// 将完整的表单html输出到页面
response.getWriter().write(form);
response.getWriter().close();
response.getWriter().flush();
}
}
这个代码可以直接拿走用不会出问题,说一下容易踩坑的地方
避坑
AlipayClient alipayClient = new DefaultAlipayClient(aliPayParam.ALIPAY,aliPayParam.APPID,
aliPayParam.PRIVATEKEY,aliPayParam.FORMAT,aliPayParam.CHARSET,aliPayParam.PUBLICKEY,aliPayParam.STGUTYPE);
// 这个东西特别特别容易踩坑
// 这个我们点进去看一下源码
// 这个是里边的源码 根据这里边的看着 看不懂的明白的去翻译一波,顺序一定不要错,
// 这个顺序错了可不好排查
public DefaultAlipayClient(java.lang.String serverUrl, java.lang.String appId, java.lang.String privateKey, java.lang.String format, java.lang.String charset, java.lang.String alipayPublicKey, java.lang.String signType) { /* compiled code */ }
// 可以看到里边 需要的参数有很多我们用第五个就可以了
// serverUrl --支付宝网关
// appId --商户APPID
// privateKey --你的私钥
// format --返回格式 默认只有 json
// charset --编码格式
// alipayPublicKey --支付宝公钥
// signType --验签方式 推荐RSA2
第二个地方
上边这个是沙箱环境提供的代码 看着比较全面实则是个坑 ,这个问题我倒是没清楚是什么愿意,但是就是不可以使用阿里提供的AlipayTradeCreateModel,那边的要求是需要一个json对象,所以我们直接传一个json对象就OK了
第三个地方
// 这个地方如果你的项目有AOP异常拦截的会出一个空指针异常。
// 这个没事因为void的接口没有返回值 使用了HttpServletResponse response 返回了from表单 所以他会出一个 空指针异常 不用管
// 根据文档可以看到这段代码是有相应的返回值和返回的参数
// 但是你会发现在Dubug的时候里边所有的值都是null
// 我后来也是询问客服才知道 这个地方是没有相应的参数
// 他只会给你提供一个生成的支付链接复制到浏览器然后就可以跳转支付宝
AlipayTradeWapPayResponse payResponse = alipayClient.pageExecute(payRequest,"get");
这个几个是大坑不好排查,一定要仔细,一定要仔细,一定要仔细
调试
通过这个`
AlipayTradeWapPayResponse payResponse = alipayClient.pageExecute(payRequest,"get");
out.println("我在这里"+payResponse.getBody());
logger.info("**********************"+"我在这里"+payResponse.getBody()+"**********************");
你可以获得一个链接一个http的网站就是测试需要使用的 但是在正式的操作的时候这个没什么用主要就用来测试,把打印的地址粘贴到浏览器然后就可以打开这个页面,然后使用支付宝的沙箱钱包软件去付款支付宝沙箱后台提供的有账号密码。
在这里你可能会碰到问题也是最常见的就是这个,不要慌。去检查代码
这个问题大多数的会在你构建订单金额相关的参数时候出错,你可以吧所有的都打印一下,然后再去看一下官网提供参数事例是否哪里不对,如果按照我上边所说的是用了JSONObject那应该不会出现这个问题 所以好好排查一下
前端H5
新建一个H5 的页面,这个你也可以交给你们前端坐,前端的代码我就全部说了 因为每个公司使用的东西都不一样
这个H5页面什么有没有,因为需要通过微信跳转所以你需要准备一张图片
下边我是实用vue的一个判定
这个图的用途就是让用户从微信跳转支付宝
这个基本没什么问题了
下一篇文章会讲解一下异常通知,这个只是一个单独拉起支付宝可以正常支付,但是支付结果是没有的,可以去看下一篇会讲解支付宝的异常通知和同步通知。