一、前期准备
- 准备一份主域名(需要备案)
- 准备一台云服务器
- 认证一个微信小程序
- 申请一下小程序微信支付功能
- SSL证书获取与配置(HTTPS)
- 配置小程序访问url许可
- Java-Springboot-Swagger2整合
万事开头难,找准方向,定点突破!
1. 域名
这个好办。
这里以腾讯云为例子。
1.注册腾讯云(都懂都懂。)
2.登陆腾讯云 https://cloud.tencent.com/login
3.进入控制台。
4.找到域名入口
5.注册域名
6.对域名进行备案(因为只有备案过后的域名才有高级功能、微信80、443等)
7.注册备案域名下子域名。用于细致开发。
所需注册的域名如下、分别用于。例如博主的名称:lanbingyin.cn
域名 | 用途 |
---|---|
backend.lanbingyin.cn | 用于部署后台管理界面(前端) |
protal.lanbingyin.cn | 用于部署前台管理界面、小程序不需要(前端) |
java.lanbingyin.cn | 用于部署SpringBoot-jar (后端) |
ws.lanbingyin.cn | 用于webSocket长连接 (后端) |
2. 购买云服务器
自己想办法哦。
3. 认证微信小程序
1.注册小程序。
2.认证小程序
3.做开发配置
4.1那么能得到的数据是:AppID(小程序ID)、AppSecret(小程序密钥)
嗯~ o( ̄▽ ̄)o
4.2后面需要配置服务器域名、
分别是 request请求、wss聊天协议、upload上传以及download下载。
5.服务器需要部署SSL证书、达到HTTPS的请求条件。
可以看到、为了安全性起见,小程序全部采用HTTPS协议。似乎有点高级哦。不过别怕。HTTPS只是在HTTP的基础上,多了一个SSL证书而已。
详细参见另一篇教程。
Nginx部署SSL证书: https://www.baidu.com(还没来得及写教程呢~)
6.进入微信支付、做相关配置。
进入商户中心
完善基本信息
完善支付信息,可以拿到商户号
7 安装微信本地证书,非SSL、
进入安全中心,该配置的进行配置。避免后期掉坑。
4. 开始整合Springboot
1 目前我们可以得到的信息。在Java建造一个微信支付常量值,做上SwaggerAPI
首先,在springboot创建一个WechatPaySDK的文件夹。便于管理。
public class WeChatConfig {
//appid
public static String appid = "appid ";
//appid密钥
public static String appkey = "appid密钥 ";
//微信支付32密钥密码key
public static String key32 = "微信支付32密钥密码key";
//商户号 1532518271
public static String mchId = "商户号";
//设置请求参数(通知地址)
//http://系统的ip和端口/wxPay/notify
public static String notify_url = "https://域名/wxPay/notify";
}
5. 最后一步!统一下单。
微信官方文档:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1
官方提供的信息
-
应用场景
商户在小程序中先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易后调起支付。 -
是否需要证书
否 -
请求参数 (详细见官方文档)
5.1后端Controller创建(定义类)
直接上代码、创建交互Controller-WeChatPayController
@RestController
@CrossOrigin(origins = "*", maxAge = 3600) //跨域请求
@RequestMapping("/wxPay")
@Api(value = "微信controller", tags = {"微信操作接口"})
public class WecahtController
5.2后端封装返回值(Ajax请求返回Json)
@ApiModel(value = "微信支付封装JSON")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AjaxJson implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private boolean success = true;// 是否成功
private String msg = "操作成功";// 提示信息
private Object obj = null;// 其他信息
private ConcurrentMap<String, Object> attributes;// 其他参数
private String errorCode;// 错误码
private Integer totalSize;// 错误码
}
5.2后端核心接口代码(统一下单)
/**
* 小程序微信支付的第一步,统一下单
*
* @return
* @author lantian
* @time 2019年4月23日17:36:35
*/
@ApiOperation(value = "小程序微信支付的第一步", notes = "统一下单")
@ApiImplicitParams({
@ApiImplicitParam(name = "openid", value = "微信唯一标识", paramType = "path", required = true, dataType = "String"),
@ApiImplicitParam(name = "orderId", value = "订单id", paramType = "path", required = true, dataType = "String")
})
@GetMapping("/createUnifiedOrder/{openid}/{orderId}")
public AjaxJson createUnifiedOrder(
@PathVariable String openid,
@PathVariable String orderId
) {
AjaxJson aj = new AjaxJson();
aj.setSuccess(false);
//接受参数(openid)
if (StringUtils.isAnyBlank(orderId, openid)) {
aj.setMsg("支付失败,支付所需参数缺失");
return aj;
}
//这里调用service层根据订单id获取订单数据,这里省略不表
// Map<String, String> mapBasic = getOrderInfoById(orderId);
Orders orders = ordersServicer.selectByPrimaryKey(orderId);
if (orders == null) {
aj.setMsg("支付失败,暂时无法获取到您的订单数据,请稍后再试");
return aj;
}
//String return_msg = "统一订单失败";
try {
//支付金额 **金额不能有小数点,单位是分!!**
BigDecimal price = new BigDecimal(orders.getPrice());
BigDecimal beishu = new BigDecimal("100");
BigDecimal priceFee = price.multiply(beishu);
//商家订单号
String orderNo = String.valueOf(orders.getId());
//创建 时间戳
String timeStamp = Long.valueOf(System.currentTimeMillis()).toString();
//生成32位随机数
UUID uuid = UUID.randomUUID();
String nonceStr = uuid.toString().replaceAll("-", "");
//商品描述
String body = "拼单商城-支付订单";
//创建hashmap(用户获得签名)
SortedMap<String, String> paraMap = new TreeMap<String, String>();
//设置请求参数(小程序ID)
paraMap.put("appid", WeChatConfig.appid);
//设置请求参数(商户号)
paraMap.put("mch_id", WeChatConfig.mchId);
//设置请求参数(随机字符串)
paraMap.put("nonce_str", nonceStr);
//设置请求参数(商品描述)
paraMap.put("body", body);
//设置请求参数(商户订单号)
paraMap.put("out_trade_no", orderNo);
//设置请求参数(总金额)
paraMap.put("total_fee", priceFee.toBigInteger().toString());
设置请求参数(终端IP) 如果是springmvc,或者能获取到request的servlet,用下面这种
//paraMap.put("spbill_create_ip", request.getRemoteAddr());
//设置请求参数(通知地址)
paraMap.put("notify_url", WeChatConfig.notify_url);
//设置请求参数(交易类型)
paraMap.put("trade_type", String.valueOf(WxPayApi.TradeType.JSAPI));
//设置请求参数(openid)(在接口文档中 该参数 是否必填项 但是一定要注意 如果交易类型设置成'JSAPI'则必须传入openid)
paraMap.put("openid", openid);
//MD5运算生成签名,这里是第一次签名,用于调用统一下单接口
String sign = PaymentKit.createSign(paraMap, WeChatConfig.key32);
paraMap.put("sign", sign);
//统一下单,向微信api发送数据
// logger.info("微信小程序统一下单发送的数据: " + paraMap.toString());
String xmlResult = WxPayApi.pushOrder(false, paraMap);
// logger.info("微信小程序统一下单接受返回的结果: " + xmlResult);
System.out.println(xmlResult);
//转成xml
Map<String, String> map = PaymentKit.xmlToMap(xmlResult);
System.out.println(map);
//返回状态码
String return_code = map.get("return_code");
//返回给小程序端需要的参数
Map<String, String> returnMap = new HashMap<String, String>();
if ("SUCCESS".equals(return_code)) {
//返回的预付单信息
returnMap.put("appId", map.get("appid"));
// returnMap.put("mch_id", map.get("mch_id"));
returnMap.put("nonceStr", nonceStr);
String prepay_id = map.get("prepay_id");
returnMap.put("package", "prepay_id=" + prepay_id);
returnMap.put("signType", "MD5");
//这边要将返回的时间戳转化成字符串,不然小程序端调用wx.requestPayment方法会报签名错误
returnMap.put("timeStamp", timeStamp);
//拼接签名需要的参数
//再次签名,这个签名用于小程序端调用wx.requesetPayment方法
String paySign = PaymentKit.createSign(returnMap, WeChatConfig.key32).toUpperCase();
returnMap.put("paySign", paySign);
System.out.println(returnMap);
aj.setObj(returnMap);
aj.setMsg("操作成功");
aj.setSuccess(true);
return aj;
} else {
aj.setMsg(getMsgByCode(return_code));
//logger.error(Thread.currentThread().getStackTrace()[1].getMethodName() + ">>>" + return_msg);
}
} catch (Exception e) {
// logger.error(Thread.currentThread().getStackTrace()[1].getMethodName() + "发生的异常是: ", e);
aj.setMsg("微信支付下单失败,请稍后再试");
}
return aj;
}
5.3后端核心代码(下单成功后回调接口)
@PostMapping("/notify")
public void notify(HttpServletRequest request) {
//获取所有的参数
StringBuffer sbf = new StringBuffer();
// 支付结果通用通知文档: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7
String xmlMsg = HttpKit.readData(request);
System.out.println("支付通知=" + xmlMsg);
Map<String, String> params = PaymentKit.xmlToMap(xmlMsg);
// System.out.println(params);
String result_code = params.get("result_code");
//校验返回来的支付结果,根据已经配置的密钥
if (PaymentKit.verifyNotify(params, WeChatConfig.key32)) {
//Constants.SUCCESS="SUCCESS"
if (("SUCCESS").equals(result_code)) {
//校验通过. 更改订单状态为已支付, 修改库存
Orders orders = new Orders();
String out_trade_no = params.get("out_trade_no");
orders.setId(out_trade_no);
orders.setOrdersstatus("下单成功");
System.out.println("校验通过. 更改订单状态为下单成功=" + orders);
ordersServicer.update(orders);
}
}
}
5.4后端核心代码(前端返回提示信息封装)
/**
* 判断返回的return_code,给前端相应的提示
*
* @param return_code
* @return 提示信息
*/
private String getMsgByCode(String return_code) {
switch (return_code) {
case "NOTENOUGH":
return "您的账户余额不足";
case "ORDERPAID":
return "该订单已支付完成,请勿重复支付";
case "ORDERCLOSED":
return "当前订单已关闭,请重新下单";
case "SYSTEMERROR":
return "系统超时,请重新支付";
case "OUT_TRADE_NO_USED":
return "请勿重复提交该订单";
default:
return "网络正在开小差,请稍后再试";
}
}
6. 最后一步系列!前端统一下单。
前端代码。