最近在做微信小程序,今天剛好做到小程序里的微信支付這塊,踩過不少坑,特此寫個博客記錄下,希望能幫到其它人吧。
我總結了一下,小程序中的微信支付和之前其它的公眾號里的微信支付有兩個區別,第一就是小程序必須要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>";
}
