微信 h5 開發(fā) 使用 best-pay-sdk

前言

說實話最近才接了點項目.項目里面需要使用微信支付進(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ù)到類中就不用我說了.

前期配置類

* PayConfig.java sdk前期初始化

***************以上的代碼實際上不用修改啥********************************

支付代碼編寫

注意這里是關(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ā)會不會有問題

文章作者:惡搞大王

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末未檩,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子粟焊,更是在濱河造成了極大的恐慌冤狡,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,561評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件项棠,死亡現(xiàn)場離奇詭異悲雳,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)香追,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評論 3 385
  • 文/潘曉璐 我一進(jìn)店門合瓢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人透典,你說我怎么就攤上這事歪玲。” “怎么了掷匠?”我有些...
    開封第一講書人閱讀 157,162評論 0 348
  • 文/不壞的土叔 我叫張陵滥崩,是天一觀的道長。 經(jīng)常有香客問我讹语,道長钙皮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,470評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮短条,結(jié)果婚禮上导匣,老公的妹妹穿的比我還像新娘。我一直安慰自己茸时,他們只是感情好贡定,可當(dāng)我...
    茶點故事閱讀 65,550評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著可都,像睡著了一般缓待。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上渠牲,一...
    開封第一講書人閱讀 49,806評論 1 290
  • 那天旋炒,我揣著相機(jī)與錄音,去河邊找鬼签杈。 笑死瘫镇,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的答姥。 我是一名探鬼主播铣除,決...
    沈念sama閱讀 38,951評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼鹦付!你這毒婦竟也來了尚粘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,712評論 0 266
  • 序言:老撾萬榮一對情侶失蹤睁壁,失蹤者是張志新(化名)和其女友劉穎背苦,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體潘明,經(jīng)...
    沈念sama閱讀 44,166評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡行剂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,510評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了钳降。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片厚宰。...
    茶點故事閱讀 38,643評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖遂填,靈堂內(nèi)的尸體忽然破棺而出铲觉,到底是詐尸還是另有隱情,我是刑警寧澤吓坚,帶...
    沈念sama閱讀 34,306評論 4 330
  • 正文 年R本政府宣布撵幽,位于F島的核電站,受9級特大地震影響礁击,放射性物質(zhì)發(fā)生泄漏盐杂。R本人自食惡果不足惜逗载,卻給世界環(huán)境...
    茶點故事閱讀 39,930評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望链烈。 院中可真熱鬧厉斟,春花似錦、人聲如沸强衡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽漩勤。三九已至感挥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間锯七,已是汗流浹背链快。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評論 1 266
  • 我被黑心中介騙來泰國打工誉己, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留眉尸,地道東北人。 一個月前我還...
    沈念sama閱讀 46,351評論 2 360
  • 正文 我出身青樓巨双,卻偏偏與公主長得像噪猾,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子袱蜡,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,509評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 關(guān)于微信支付 生活中的微信支付 目前我們?nèi)粘I钪薪佑|得比較多的線上電子支付方式主要有兩種慢宗,一種是支付寶,另一種就...
    積_漸閱讀 3,914評論 3 26
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,770評論 25 707
  • 人潮擁擠 我卻只能看其消失在人海 他說的再見镜沽,我出口的告別 那份不合理的禮物 是你深情的告白 你微微一笑 我用假笑...
    qieyiyongri閱讀 252評論 0 0
  • YSL的圣誕限量版口紅“星辰”一夜之間紅遍大江南北嘴脾,各大公眾號都在用這只口紅大做文章蔬墩,而我呀译打,也蹭了個熱點的尾巴,...
    松糕娃娃閱讀 2,911評論 0 1
  • 對于電視劇拇颅,我總是不能與時俱進(jìn)奏司。 在《大軍師司馬懿之軍師聯(lián)盟 》大火了半年之后,我才在這兩天出差中樟插,蹭著同事對它的...
    榮悅閱讀 225評論 2 3