微信小程序中实现微信支付

最近在做微信小程序,今天刚好做到小程序里的微信支付这块,踩过不少坑,特此写个博客记录下,希望能帮到其它人吧。

我总结了一下,小程序中的微信支付和之前其它的公众号里的微信支付有两个区别,第一就是小程序必须要Https接口,第二服务器在返回给小程序之前需要二次签名,记住要二次签名。

我发现网上的相关例子很多,但是也很乱,各种封装类,五花八门的,在这里我用的是微信官方的方法,简单,无脑,高效。

1,我们需要在Maven坐标系里导入官方封装好的方法

<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency> 2,写一个MyConfig类继承WXPayConfig
import com.baibeiyun.robot.base.util.PropertiesUtils;
import com.github.wxpay.sdk.WXPayConfig; import java.io.InputStream; /**
* 配置类
*
* @author liyuan
* @date 2018/3/30
**/
public class MyConfig implements WXPayConfig { @Override
public String getAppID() {
     // 放你的appid
return PropertiesUtils.getInstace("conf/webService.properties").getProperty("appId");
} @Override
public String getMchID() {
     // 商户号
return PropertiesUtils.getInstace("conf/webService.properties").getProperty("mchId");
} @Override
public String getKey() {
     // 你的商户号里的key
return PropertiesUtils.getInstace("conf/webService.properties").getProperty("key");
} @Override
public InputStream getCertStream() {
return null;
} @Override
public int getHttpConnectTimeoutMs() {
return 8000;
} @Override
public int getHttpReadTimeoutMs() {
return 10000;
}
}

3. 在服务器端充值接口里获取到那5个关键参数传给小程序

    @ApiOperation("小程序充值接口")
@RequestMapping(value = "saveMoney", method = RequestMethod.POST)
public String saveMoney(@ModelAttribute SaveMoney1Dto dto, HttpServletRequest request) throws Exception {
if (!dto.checkParam()) {
throw new MyParameterException(400, "请求参数异常");
} // 获取用户userId
Integer userId = Integer.valueOf(redisService.get(dto.getUuidKey()).substring(52));
// 生成交易订单号
String orderNum = CommonUtils.getOrderNumber();
MoneyTransaction moneyTransaction = new MoneyTransaction();
moneyTransaction.setPayUserId(userId);
moneyTransaction.setOrderNum(orderNum);
moneyTransaction.setMoney(dto.getMoney());
// 交易时间
moneyTransaction.setGmtCreate(new Date());
// 将交易订单存入数据库
iMoneyTransactionService.insertSelective(moneyTransaction); // 给微信服务器统一下单
MyConfig config = new MyConfig();
WXPay wxpay = new WXPay(config);
// 封装入参字典
Map<String, String> data = new HashMap<>(16);
// 商品描述
data.put("body", "机器人服务充值");
// 商户订单号
data.put("out_trade_no", orderNum);
// 随机字符串
data.put("nonce_str", WXPayUtil.generateNonceStr());
// 总金额 分 将BigDecimal乘以100再转为字符串
String totalFee = (dto.getMoney().multiply(new BigDecimal("100")).setScale(0, BigDecimal.ROUND_HALF_UP)).toString();
data.put("total_fee", totalFee);
// 终端IP
data.put("spbill_create_ip", request.getRemoteAddr());
// 接收微信支付异步通知回调地址
data.put("notify_url", PropertiesUtils.getInstace("conf/webService.properties").getProperty("notifyUrl"));
// 交易类型
data.put("trade_type", "JSAPI");
// 获取opneid
String openid = redisService.get(dto.getUuidKey()).substring(24, 52);
data.put("openid", openid); // 和微信服务器交互,统一下单
Map<String, String> resp = wxpay.unifiedOrder(data);
log.info("微信返回: " + resp); // 小程序再次签名
Map<String, String> data2 = new HashMap<>(16);
data2.put("appId", resp.get("appid"));
String timeStamp = System.currentTimeMillis()/1000 + "";
data2.put("timeStamp", timeStamp);
String nonceStr = WXPayUtil.generateNonceStr();
data2.put("nonceStr", nonceStr);
String packages = "prepay_id="+resp.get("prepay_id");
data2.put("package", packages);
data2.put("signType", "MD5");
String sign2 = WXPayUtil.generateSignature(data2, PropertiesUtils.getInstace("conf/webService.properties").getProperty("key")); // 封装返回给前端map
Map<String, String> result = new HashMap<>(16);
result.put("timeStamp", timeStamp);
result.put("nonceStr", nonceStr);
result.put("package", packages);
result.put("signType", "MD5");
result.put("paySign", sign2); log.info("返回给前端: " + result);
return YuanResultUtil.successWithDataAndMsg(result, "OK");
}

在这里直接用微信的工具类,微信的方法比较高效,直接用map字典比较直观,犯不上像网上大多数人那样造一个封装类转来转去的麻烦

4 微信通知回调类

    @ApiOperation("小程序充值结果(自动从微信服务器获取,前端用不到)")
@RequestMapping(value = "saveMoneyResult", method = RequestMethod.POST)
public String saveMoneyResult(HttpServletRequest request) throws Exception {
// 获取支付结果,XML格式
String notifyData = StreamUtil.read(request.getInputStream());
log.info("支付结果: " + notifyData); MyConfig config = new MyConfig();
WXPay wxPay = new WXPay(config);
// 支付结果转成map
Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyData);
if (wxPay.isPayResultNotifySignatureValid(notifyMap)) {
// 签名正确
log.info("签名正确: " + notifyMap);
// 获取订单号
String orderNum = notifyMap.get("out_trade_no");
log.info("订单号:" + orderNum);
// 获取金额(分)
String totalFee = notifyMap.get("total_fee");
// 转为BigDecimal
BigDecimal totalFee2 = new BigDecimal(totalFee);
totalFee2 = totalFee2.divide(new BigDecimal("100"), 2, BigDecimal.ROUND_HALF_UP); // 根据订单号获取交易记录
MoneyTransaction moneyTransaction = iMoneyTransactionService.getMoneyTransactionByOrderNum(orderNum);
if (moneyTransaction != null && moneyTransaction.getStatus() == 0 && moneyTransaction.getMoney().equals(totalFee2)) {
// 设置状态为支付成功
moneyTransaction.setStatus(1);
moneyTransaction.setGmtCreate(new Date());
log.info("修改成功");
iMoneyTransactionService.updateByPrimaryKeySelective(moneyTransaction); return "<xml><return_code>SUCCESS</return_code><return_msg>OK</return_msg></xml>";
} else {
log.info("修改失败");
}
} else {
// 签名错误
throw new BuinessException(501, "签名错误");
}
return "<xml><return_code>FALL</return_code><return_msg>失败</return_msg></xml>";
}
上一篇:Windows网络通信(一):socket同步编程


下一篇:微信小程序中rpx与rem单位使用