前言
說實話最近才接了點項目.項目里面需要使用微信支付進(jìn)行付款.然后我遇到不少問題.這里總結(jié)下.
為什么使用best-pay-sdk?
額..看到微信官方的頭就痛了.不想看了.微信搞了一套又一套.太麻煩,然后發(fā)現(xiàn)這個第三方的sdk.最終確實可以使用了.
第三方sdk的Github地址
如何使用
要求
需要在Jdk版本>1.8上運行.不滿足的請自行繞道.
導(dǎo)入maven依賴
<dependency>
<groupId>cn.springboot</groupId>
<artifactId>best-pay-sdk</artifactId>
<version>1.2.0</version>
</dependency>
配置
這里介紹一下參數(shù)都是代表什么意思
- mpAppId : appid(在微信公眾平臺查看)
- mchId:商戶號(在微信支付平臺查看)
- mchKey:密匙(在微信支付平臺自行設(shè)置,要求32位.建議使用隨機(jī)密碼)
- keyPath:退款密匙(應(yīng)該這么叫吧.需要去微信支付平臺下載.指定密匙的絕對地址)
- notifyUrl:微信支付完成的通知地址
前期代碼編寫
注意.大部分的代碼請參照官方的demo.比如如何從配置文件數(shù)據(jù)到類中就不用我說了.
***************以上的代碼實際上不用修改啥********************************
支付代碼編寫
注意這里是關(guān)鍵
先看我的代碼,可能比較復(fù)雜一些.
/**
* 支付相關(guān)
*
* @version 1.0 2017/3/2
* @auther <a href="mailto:lly835@163.com">廖師兄</a>
* @since 1.0
* 不需要進(jìn)行過濾
*/
@Controller
@Slf4j
public class PayController {
@Autowired
private BestPayServiceImpl bestPayService;
@Autowired
private OrderDao orderDao;
@Autowired
private OrderService orderService;
@Autowired
private AddMoneyDao addMoneyDao;
@Autowired
private AddMoneyService addMoneyService;
@Autowired
private UserDao userDao;
@Autowired
private AddMoneyComboDao addMoneyComboDao;
/**
* 發(fā)起支付
*
* @param orderid 訂單id
* @param addmoneyid 充值訂單
*/
@RequestMapping(value = "/pay", method = RequestMethod.GET)
public ModelAndView pay(
Map<String, Object> map, HttpSession session, @RequestParam(required = false) String orderid, @RequestParam(required = false) String addmoneyid) {
User user = (User) session.getAttribute(Constants.CURRENTUSER);
// User user = userDao.findOne("4028fb82647f166d01647f167cbe0000");
log.info("請求訂單:獲取到的訂單id為:" + orderid);
log.info("請求訂單:獲取到的充值id為:" + addmoneyid);
if (orderid == null && addmoneyid == null) {
//不能出現(xiàn)兩個參數(shù)同時不存在
return new ModelAndView("wxpay/error", map);
}
if (orderid != null && addmoneyid != null) {
//不能出現(xiàn)兩個參數(shù)同時存在
return new ModelAndView("wxpay/error", map);
}
PayResponse payResponse = null;
if (orderid != null) {
payResponse = handlerOrder(orderid, user);
} else if (addmoneyid != null) {
payResponse = handlerAddMoney(addmoneyid, user);
} else {
return new ModelAndView("wxpay/error", map);
}
map.put("payResponse", payResponse);
log.info("放入的payResponse對象為:" + payResponse);
if (payResponse == null) {
return new ModelAndView("wxpay/error", map);
}
return new ModelAndView("wxpay/index", map);
}
/**
* 異步回調(diào)
*/
@RequestMapping(value = "/notify", method = RequestMethod.POST)
public ModelAndView notify(@RequestBody String notifyData) throws Exception {
Order order = null;
AddMoney addMoney = null;
try {
log.info("【異步回調(diào)】request={}", notifyData);
PayResponse response = bestPayService.asyncNotify(notifyData);
log.info("【異步回調(diào)】response={}", JsonUtil.toJson(response));
String orderId = response.getOrderId();
order = orderDao.findOne(orderId);
addMoney = addMoneyDao.findOne(orderId);
if (order != null && addMoney != null) {
//那就很尷尬了- -.還是退款吧.我可以去買彩票了.
log.info("普通訂單和充值訂單uuid居然相同了..." + response);
orderService.moneyback(orderId);
addMoneyService.moneyBack(orderId);
} else {
if (order != null && !order.isIsmoneyback()) {
BigDecimal bigDecimal = new BigDecimal(order.getNeedpay());
BigDecimal subtract = bigDecimal.subtract(new BigDecimal(String.valueOf(response.getOrderAmount()))).abs();
if (subtract.doubleValue() < 0.01) {
log.info("普通訂單完成" + order);
order.setPayd(true);
order.setCompletedate(new Date());
try {
orderService.setPay(order);
} catch (Exception e) {
log.warn(e.getMessage() + " " + order + " 出現(xiàn)支付異常.進(jìn)行退款");
}
//說明訂單支付成功了
} else {
orderService.moneyback(orderId);
}
} else {
//判斷addmoney不等于null 并且不是已經(jīng)退款的訂單
if (addMoney != null && !addMoney.isIsbackmoney()){
BigDecimal bigDecimal = new BigDecimal(addMoney.getMoney());
BigDecimal subtract = bigDecimal.subtract(new BigDecimal(String.valueOf(response.getOrderAmount()))).abs();
if (subtract.doubleValue() < 0.01) {
if (!addMoney.isComplete()) {
log.info("充值訂單完成" + addMoney);
addMoney.setComplete(true);
addMoney.setCompletedate(new Date());
addMoneyService.setcomplete(addMoney);
}
//說明訂單支付成功了
} else {
addMoneyService.moneyBack(orderId);
}
}
}
}
} catch (Exception e) {
if (order != null) {
orderService.moneyback(order.getId());
} else if (addMoney != null) {
addMoneyService.moneyBack(addMoney.getId());
}
}
return new ModelAndView("wxpay/success");
}
/**
* 處理訂單付款請求
*
* @param orderid
* @param user
* @return
*/
private PayResponse handlerOrder(String orderid, User user) {
if (user != null) {
Order order = orderDao.findOne(orderid);
if (order == null || order.isPayd() || order.isIssend()) {
log.debug("沒有找到訂單");
return null;
}
log.debug("獲取到的訂單為:" + order);
PayRequest request = new PayRequest();
//支付請求參數(shù)
request.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5);
request.setOrderId(order.getId());
//把String類型進(jìn)行轉(zhuǎn)換
request.setOrderAmount(new BigDecimal(order.getNeedpay()).doubleValue());
request.setOrderName(order.getOwnercleaner().getName() + order.getOrderyype());
log.info("當(dāng)前支付的用戶為:" + user);
request.setOpenid(user.getOpenid());
log.info("【發(fā)起支付】request={}", JsonUtil.toJson(request));
PayResponse payResponse = bestPayService.pay(request);
log.info("【發(fā)起支付】response={}", JsonUtil.toJson(payResponse));
return payResponse;
} else {
return null;
}
}
private PayResponse handlerAddMoney(String addmoneyid, User user) {
if (user != null) {
AddMoney addMoney = addMoneyDao.findOne(addmoneyid);
log.info("查詢出的充值類為:" + addMoney);
if (addMoney == null || addMoney.isComplete()) return null;
String machineid = addMoney.getMachineid();
List<Order> machineidEquals = orderDao.findOrderByMachineid(machineid);
log.info("查出的機(jī)器類為;" + machineidEquals);
if (machineidEquals == null || machineidEquals.size() == 0) {
return null;
}
boolean issend = machineidEquals.get(0).isIssend();
//判斷租借機(jī)器是否已經(jīng)發(fā)出.如果已經(jīng)發(fā)貨才能夠開始進(jìn)行充值
if (!issend) {
//說明還沒有發(fā)貨.
return null;
}
PayRequest request = new PayRequest();
//支付請求參數(shù)
request.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5);
request.setOrderId(addMoney.getId());
//把String類型進(jìn)行轉(zhuǎn)換
request.setOrderAmount(new BigDecimal(addMoney.getMoney()).doubleValue());
request.setOrderName(addMoney.getMachineid() + " 充值" + addMoney.getCombodesc());
request.setOpenid(user.getOpenid());
log.info("【發(fā)起支付】request={}", JsonUtil.toJson(request));
PayResponse payResponse = bestPayService.pay(request);
log.info("【發(fā)起支付】response={}", JsonUtil.toJson(payResponse));
return payResponse;
}
return null;
}
@RequestMapping(value = "success.do", method = RequestMethod.GET)
public String success() {
return "wxpay/success";
}
@RequestMapping(value = "error.do", method = RequestMethod.GET)
public String error() {
return "wxpay/error";
}
}
我們在看一下官方的代碼,我的代碼里殘渣了太多自己的東西了.但是官方的代碼又簡略了一些.我又加了一些注釋
@Autowired
private BestPayServiceImpl bestPayService;
/**
* 發(fā)起支付
*/
@GetMapping(value = "/pay")
public ModelAndView pay(@RequestParam("openid") String openid,
Map<String, Object> map) {
PayRequest request = new PayRequest();
Random random = new Random();
//支付請求參數(shù)
request.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5);
//這里你需要你的唯一的訂單號
request.setOrderId(String.valueOf(random.nextInt(1000000000)));
//支付的金額
request.setOrderAmount(0.01);
//商品的名稱
request.setOrderName("最好的支付sdk");
//h5獲取用戶登錄信息中獲取到openid
request.setOpenid(openid);
log.info("【發(fā)起支付】request={}", JsonUtil.toJson(request));
PayResponse payResponse = bestPayService.pay(request);
log.info("【發(fā)起支付】response={}", JsonUtil.toJson(payResponse));
map.put("payResponse", payResponse);
return new ModelAndView("pay/create", map);
}
/**
* 異步回調(diào)
*/
@PostMapping(value = "/notify")
public ModelAndView notify(@RequestBody String notifyData) throws Exception {
log.info("【異步回調(diào)】request={}", notifyData);
PayResponse response = bestPayService.asyncNotify(notifyData);
log.info("【異步回調(diào)】response={}", JsonUtil.toJson(response));
return new ModelAndView("pay/success");
}
創(chuàng)建訂單時候首先你需要先調(diào)用pay這個函數(shù).然后支付成功之后會調(diào)用notify這個函數(shù).然后根據(jù)需要判斷訂單是否完成.
細(xì)心的同學(xué)會發(fā)現(xiàn)代碼返回到了一個視圖pay/create.這里是需要調(diào)用微信的接口的.我們看官方的ftl文件是怎么樣的.
在這里.我們其實不需要修改代碼.這里的參數(shù)傳入的都是第三方的best-pay-sdk生成好的.我們不需要關(guān)心細(xì)節(jié).
<script>
function onBridgeReady(){
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId":"${payResponse.appId}", //公眾號名稱婆硬,由商戶傳入
"timeStamp":"${payResponse.timeStamp}", //時間戳仑濒,自1970年以來的秒數(shù)
"nonceStr":"${payResponse.nonceStr}", //隨機(jī)串
"package":"${payResponse.packAge}",
"signType":"MD5", //微信簽名方式:
"paySign":"${payResponse.paySign}" //微信簽名
},
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ) {
alert('支付成功');
}else if(res.err_msg == "get_brand_wcpay_request:cancel") {
alert('支付過程中用戶取消');
}else if(res.err_msg == "get_brand_wcpay_request:fail") {
alert('支付失敗');
}else {
alert('未知異常');
}
}
);
}
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}else{
onBridgeReady();
}
</script>
順便附帶一份我寫的thymelaf的頁面代碼.效果和上面一樣
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<script th:inline="javascript">
/*<![CDATA[*/
function onBridgeReady() {
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId": [[${payResponse.appId}]], //公眾號名稱空骚,由商戶傳入
"timeStamp": [[${payResponse.timeStamp}]], //時間戳,自1970年以來的秒數(shù)
"nonceStr": [[${payResponse.nonceStr}]], //隨機(jī)串
"package": [[${payResponse.packAge}]],
"signType": "MD5", //微信簽名方式:
"paySign": [[${payResponse.paySign}]] //微信簽名
},
function (res) {
if (res.err_msg == "get_brand_wcpay_request:ok") {
// alert('支付成功');
window.location.href = "/ac/success.do";
} else if (res.err_msg == "get_brand_wcpay_request:cancel") {
window.location.href = "/ac/buydetails.so";
} else if (res.err_msg == "get_brand_wcpay_request:fail") {
window.location.href = "/ac/error.do";
} else {
alert('未知異常');
}
}
);
}
if (typeof WeixinJSBridge == "undefined") {
if (document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
} else if (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
} else {
onBridgeReady();
}
/*]]>*/
</script>
</html>
到這里.通過判斷就可以知道訂單是否支付完成了.有興趣的仔細(xì)看看我上面的我打的代碼.我這里有兩種支付.一種是普通訂單支付.一種是續(xù)費充值訂單.所以我寫了一些判斷.
退款操作
退款操作時需要確定自己的退款秘鑰是不是已經(jīng)下載好了.并且配置完成了.否則會有異常.
官方退款代碼:
@Autowired
private BestPayServiceImpl bestPayService;
//就這么幾句
RefundRequest refundRequest = new RefundRequest();
//這里傳入唯一的訂單號
refundRequest.setOrderId(addMoney.getId());
refundRequest.setOrderAmount(new BigDecimal(addMoney.getMoney()).doubleValue());
refundRequest.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5);
log.info("【微信退款】request={}", JsonUtil.toJson(refundRequest));
RefundResponse refundResponse = bestPayService.refund(refundRequest);
log.info("[微信退款]" + refundResponse);
我的退款代碼:
尤其需要注意安全問題.不能萬一已經(jīng)退了款某個商品數(shù)據(jù)庫還是正常顯示這樣子...
@Transactional
public AddMoney moneyBack(String addmoneyid) {
AddMoney addMoney = addMoneyDao.findOne(addmoneyid);
if (addMoney == null) {
return null;
}
if (addMoney.isComplete()) {
//撤銷付款記錄
addMoney.setComplete(false);
//設(shè)置成已經(jīng)退款
addMoney.setIsbackmoney(true);
// addMoney.setCompletedate(null);
//獲取充值訂單完成時候的日期
Date completedate = addMoney.getCompletedate();
//獲取機(jī)器碼
String machineid = addMoney.getMachineid();
LeaseMachine leaseMachine = leaseMachineDao.findByMachineid(machineid);
if (leaseMachine == null) {
//機(jī)器已經(jīng)不存在了.不能進(jìn)行退款
throw new RuntimeException("退款失敗.機(jī)器不存在");
}
Calendar calendar = Calendar.getInstance();
//獲取到過期的時間
Date date = leaseMachine.getExpirationtime();
if (date != null && date.getTime() < System.currentTimeMillis()) {
//如果現(xiàn)在的時間已經(jīng)超過了過期的時間的話.就不能退款了.他已經(jīng)把錢都用掉了.
return null;
}
if (date != null) {
calendar.setTime(date);
//退款之后減去相應(yīng)的天數(shù)
calendar.add(Calendar.MONTH, (0 - Math.abs(addMoney.getPaymonth())));
Date calendarTime = calendar.getTime();
leaseMachine.setExpirationtime(calendarTime);
leaseMachineDao.save(leaseMachine);
}
// leaseMachine.setExpirationtime();
//撤銷訂單.
AddMoney money = addMoneyDao.save(addMoney);
//只有已經(jīng)支付的人才能夠退款
RefundRequest refundRequest = new RefundRequest();
refundRequest.setOrderId(addMoney.getId());
refundRequest.setOrderAmount(new BigDecimal(addMoney.getMoney()).doubleValue());
refundRequest.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5);
log.info("【微信退款】request={}", JsonUtil.toJson(refundRequest));
RefundResponse refundResponse = bestPayService.refund(refundRequest);
log.info("[微信退款]" + refundResponse);
if (refundResponse == null) {
throw new RuntimeException("退款出現(xiàn)問題");
}
return money;
}
return null;
}
總結(jié)
涉及錢的一定要小心 涉及錢的一定要小心 涉及錢的一定要小心
考慮考慮并發(fā)會不會有問題
文章作者:惡搞大王