? ? ? ? ? ? ? ?<van-cell title="訂單金額(元)" :value="plan.refillMoney" /> ? ? ? ? ? ? ? ?<van-cell ? ? ? ? ? ? ? ? ? ?title="獲得T幣" ? ? ? ? ? ? ? ? ? ?:value="plan.payMoney * (plan.isVip ? 1.5 : 1)" ? ? ? ? ? ? ? ?/> ? ? ? ? ? ?</van-cell-group> ? ? ? ? ? ?<div class="buttons"> ? ? ? ? ? ? ? ?<van-button type="primary" @click="doBuy" class="mb-1" ? ? ? ? ? ? ? ? ? ?>確認并支付</van-button ? ? ? ? ? ? ? ?> ? ? ? ? ? ? ? ?<van-button @click="$goBack()">取消</van-button> ? ? ? ? ? ?</div> ? ? ? ?</template> ? ?</div> </template> <script> export default { ? ?components: {}, ? ?data() { ? ? ? ?return { ? ? ? ? ? ?plan: {}, ? ? ? ? ? ?orderNo: "", ? ? ? ? ? ?payChannel: "wx", ? ? ? }; ? }, ? ?mounted() { ? ? ? ?this.plan = this.$route.query || {}; ? ? ? ?// 生成訂單號 ? ? ? ?this.$get("/trade/generate-order-no").then((resp) => { ? ? ? ? ? ?this.orderNo = resp; ? ? ? }); ? }, ? ?methods: { ? ? ? ?doBuy() { ? ? ? ? ? ?var platform = 0; ? ? ? ? ? ?// 0表示PC支付,1表示微信內支付; 2表示MWEB支付 ? ? ? ? ? ?// 此處是H5端的頁面,因此沒有0的情況,0的情況的PC端處理 ? ? ? ? ? ?platform = this.$isWx() ? 1 : 2; ? ? ? ? ? ?this.$post("/pay/refill", { ? ? ? ? ? ? ? ?orderNo: this.orderNo, ? ? ? ? ? ? ? ?fee: this.plan.refillMoney, ? ? ? ? ? ? ? ?channel: this.payChannel === "wx" ? 2 : 1, ? ? ? ? ? ? ? ?tbAmount: this.plan.payMoney, ? ? ? ? ? ? ? ?tbSentAmount: this.plan.isVip ? this.plan.payMoney * 0.5 : 0, ? ? ? ? ? ? ? ?platform: platform, ? ? ? ? ? }).then((resp) => { ? ? ? ? ? ? ? ?// 生成支付表單成功后跳轉支付頁面 ? ? ? ? ? ? ? ?resp.platform = platform; ? ? ? ? ? ? ? ?this.$goPath("/user/pay/wx-pay", resp); ? ? ? ? ? }); ? ? ? }, ? }, }; </script>

注意isWx是全局的判斷是否是微信瀏覽器的方法,實現如下:

// 判斷是否是微信瀏覽器 
Vue.prototype.$isWx = () => {
 ? ?let UA = navigator.userAgent.toLocaleLowerCase();
 ? ?return UA.indexOf("micromessenger") !== -1;
}

用戶在確認訂單后點擊“發起訂單并支付”,將調用后臺/pay/refill接口生成訂單。

5.2 后臺訂單生成

開發之前需要先下載微信提供的SDK(https://pay.weixin.qq.com/wiki/doc/api/wxpay/ch/pages/sdk.shtml),然后在MAVEN中進行配置:

<dependency>
 ? ?<groupId>com.github.wxpay</groupId>
 ? ?<artifactId>wxpay-sdk</artifactId>
 ? ?<version>0.0.3</version>
 ? ?<scope>system</scope>
 ? ?<systemPath>${project.basedir}/src/main/resources/jar/wxpay-sdk-0.0.3.jar</systemPath>
</dependency>

然后定義WXPayConfigImpl類如下:

package com.ttcn.front.common.config;

import com.github.wxpay.sdk.WXPayConfig;

import java.io.InputStream;

public class WXPayConfigImpl implements WXPayConfig {
 ? ?public WXPayConfigImpl() {
 ?  }

 ? ?public String getAppID() {
 ? ? ? ?return "***";
 ?  }

 ? ?public String getMchID() {
 ? ? ? ?return "***";
 ?  }

 ? ?public String getKey() {
 ? ? ? ?return "***";
 ?  }

 ? ?public InputStream getCertStream() {
 ? ? ? ?return null;
 ?  }

 ? ?public int getHttpConnectTimeoutMs() {
 ? ? ? ?return 10000;
 ?  }

 ? ?public int getHttpReadTimeoutMs() {
 ? ? ? ?return 0;
 ?  }
}

注意需要將APPID及商戶號、Key配置成從公眾平臺、商戶平臺中獲取的值。

完成后就可以繼續編碼了。

/pay/refill接口實現如下:

/**
 ? ? * 充值
 ? ? *
 ? ? * @param refill 充值
 ? ? * @return 支付相關信息
 ? ? */
 ? ?@PreAuthorize("isAuthenticated()")
 ? ?@PostMapping("/refill")
 ? ?public PayDTO refill(@RequestBody @Validated RefillDTO refill) {
 ? ? ? ?UserDTO user = this.getLoginUserOrThrow();

 ? ? ? ?if (null == tradeService.findByNo(refill.getOrderNo())) {
 ? ? ? ? ? ?// 保存訂單
 ? ? ? ? ? ?TradeDTO tradeDTO = new TradeDTO();
 ? ? ? ? ?  ...

 ? ? ? ? ? ?tradeService.save(user, tradeDTO);
 ? ? ?  }

 ? ? ? ?// 獲取支付二維碼
 ? ? ? ?String openId = user.getWxOpenId();
 ? ? ? ?if (refill.getPlatform().equals(1)) {
 ? ? ? ? ? ?openId = user.getMpOpenId();
 ? ? ?  }
 ? ? ? ?return wxPayService.getPayUrl(openId, refill.getOrderNo(), refill.getFee(), TradeType.REFILL, refill.getPlatform());
 ?  }

wxPayService.getPayUrl即用于調用微信接口生成微信端訂單,實現如下:

/**
 ? ? * 查詢支付頁面地址
 ? ? *
 ? ? * @param platform 0 : WEB端;1: 微信內支付;2: MWEB支付(即移動端非微信內支付)
 ? ? * @return 支付頁面地址
 ? ? */
 ? ?public PayDTO getPayUrl(String openId, String orderNo, double fee, TradeType tradeType, Integer platform) {
 ? ? ? ?platform = Optional.ofNullable(platform).orElse(0);
 ? ? ? ?boolean isJsPay = platform.equals(1);

 ? ? ? ?String ip;
 ? ? ? ?try {
 ? ? ? ? ? ?ip = InetAddress.getLocalHost().getHostAddress();
 ? ? ?  } catch (UnknownHostException e) {
 ? ? ? ? ? ?logger.error("獲取ip地址失敗", e);
 ? ? ? ? ? ?throw BusinessException.create("生成支付二維碼失敗,請稍后重試");
 ? ? ?  }
 ? ? ? ?String feeStr = String.format("%.0f", fee * 100D);

 ? ? ? ?Map<String, String> params = MapEnhancer.<String, String>create()
 ? ? ? ? ? ? ?  .put("body", tradeType.getName())
 ? ? ? ? ? ? ?  .put("nonce_str", orderNo)
 ? ? ? ? ? ? ?  .put("out_trade_no", orderNo)
 ? ? ? ? ? ? ?  .put("total_fee", feeStr)
 ? ? ? ? ? ? ?  .put("spbill_create_ip", ip)
 ? ? ? ? ? ? ?  .put("notify_url", notifyUrl)
 ? ? ? ? ? ? ?  .put("trade_type", isJsPay ? "JSAPI" : (platform == 0 ? "NATIVE" : "MWEB"))
 ? ? ? ? ? ? ?  .putNotNull("openid", isJsPay ? openId : null)
 ? ? ? ? ? ? ?  .put("product_id", String.valueOf(tradeType.ordinal()))
 ? ? ? ? ? ? ?  .get();

 ? ? ? ?if (logger.isDebugEnabled()) {
 ? ? ? ? ? ?logger.debug("微信支付下單參數: {}", params);
 ? ? ?  }

 ? ? ? ?Map<String, String> result;
 ? ? ? ?try {
 ? ? ? ? ? ?result = wxPay.unifiedOrder(params);
 ? ? ?  } catch (Exception e) {
 ? ? ? ? ? ?logger.error("生成微信支付二維碼失敗", e);
 ? ? ? ? ? ?throw BusinessException.create("生成微信支付二維碼失敗,請稍候重試");
 ? ? ?  }
 ? ? ? ?if (logger.isDebugEnabled()) {
 ? ? ? ? ? ?logger.debug("發送微信支付訂單結果: {}", result);
 ? ? ?  }

 ? ? ? ?String resultCode = MapUtils.getString(result, "result_code");
 ? ? ? ?if ("SUCCESS".equals(resultCode)) {
 ? ? ? ? ? ?if (logger.isDebugEnabled()) {
 ? ? ? ? ? ? ? ?logger.debug("發送訂單成功");
 ? ? ? ? ?  }

 ? ? ? ? ? ?PayDTO payDTO = new PayDTO();
 ? ? ? ? ? ?payDTO.setFee(fee);
 ? ? ? ? ? ?payDTO.setOrderNo(orderNo);

 ? ? ? ? ? ?payDTO.setCodeUrl(MapUtils.getString(result, isJsPay ? "prepay_id" : (platform == 0 ? "code_url" : "mweb_url")));

 ? ? ? ? ? ?// 如果是JSPay
 ? ? ? ? ? ?if (isJsPay) {
 ? ? ? ? ? ? ? ?// 需要組裝參數并簽名
 ? ? ? ? ? ? ? ?// 簽名
 ? ? ? ? ? ? ? ?Map<String, String> signParams = new TreeMap<>();
 ? ? ? ? ? ? ? ?signParams.put("appId", config.getAppID());
 ? ? ? ? ? ? ? ?signParams.put("timeStamp", String.valueOf(LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8))));
 ? ? ? ? ? ? ? ?signParams.put("nonceStr", UUID.randomUUID().toString().replaceAll("-", ""));
 ? ? ? ? ? ? ? ?signParams.put("package", "prepay_id=" + MapUtils.getString(result, "prepay_id"));
 ? ? ? ? ? ? ? ?signParams.put("signType", "MD5");
 ? ? ? ? ? ? ? ?try {
 ? ? ? ? ? ? ? ? ? ?String sign = WXPayUtil.generateSignature(signParams, config.getKey());
 ? ? ? ? ? ? ? ? ? ?signParams.put("paySign", sign);
 ? ? ? ? ? ? ?  } catch (Exception e) {
 ? ? ? ? ? ? ? ? ? ?logger.error("簽名失敗", e);
 ? ? ? ? ? ? ? ? ? ?throw BusinessException.create("簽名失敗");
 ? ? ? ? ? ? ?  }

 ? ? ? ? ? ? ? ?payDTO.setParams(signParams);
 ? ? ? ? ?  }

 ? ? ? ? ? ?return payDTO;
 ? ? ?  }

 ? ? ? ?logger.error("發送微信支付訂單失敗,返回結果:{}", result);
 ? ? ? ?throw BusinessException.create("生成微信支付二維碼失敗,請重試或聯系管理員");
 ?  }

可以看到上面主要是組裝參數然后調用wxPay.unifiedOrder接口生成支付表單;

涉及的參數如下:

body商品簡單描述nonce_str隨機字符串,長度要求在32位以內out_trade_no商戶系統內部訂單號,要求32個字符內,且在同一個商戶號下唯一 接收支付結果通知時會包括這個參數,因此可以將通知結果與之前的訂單關聯上; total_fee訂單總金額,單位為分spbill_create_ip支持IPV4和IPV6兩種格式的IP地址。用戶的客戶端IPnotify_url異步接收微信支付結果通知的回調地址,通知url必須為外網可訪問的url,不能攜帶參數。 需要在微信公眾平臺中配置相關域名,否則會報異常trade_type交易類型,JSAPI/NATIVE/APP/MWEB等 JSAPI用于微信內瀏覽器打開的界面支付 Native用于PC端支付 APP用于單獨的APP應用中進行的支付 MWEB用于H5在非微信瀏覽器中打開的支付openidtrade_type=JSAPI時(即JSAPI支付),此參數必傳,此參數為微信用戶在商戶對應appid下的唯一標識。 注意只有在微信瀏覽器支付中才傳輸該值,其它的不要傳,否則會報異常 product_idtrade_type=NATIVE時,此參數必傳。此參數為二維碼中包含的商品ID,商戶自行定義。

其它參數請參考:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1

生成支付表單后,注意如果是JSAPI(也就是微信內瀏覽器打開的場景)需要進行簽名,代碼參考上面。

生成的支付表單示例如下(PC):

 {nonce_str=HCF8vr2sG5XnAKFY, code_url=weixin://wxpay/bizpayurl?pr=VIarG6Jzz, appid=**, sign=***, trade_type=NATIVE, return_msg=OK, result_code=SUCCESS, mch_id=1501105441, return_code=SUCCESS, prepay_id=wx2020212657557887ba533cfa23a2be0000}

5.3 前端跳轉支付界面

在5.1中調用/pay/refill接口并返回后,會帶上返回的支付表單信息跳轉到新的界面:

this.$post("/pay/refill", {
 ? ? ? ? ? ? ? ?...
}).then((resp) => {
 ? ?resp.platform = platform;
 ? ?this.$goPath("/user/pay/wx-pay", resp);
});

跳轉后的wx-pay界面實現如下:

<template>
 ? ?<div class="wx-pay-page">
 ? ? ? ?<div class="code-image p-1 mt-2">
 ? ? ? ? ? ?<div class="bottom">
 ? ? ? ? ? ? ?  請確認支付已完成,如有異議,請在<span
 ? ? ? ? ? ? ? ? ? ?@click="$goPath('/feedback')"
 ? ? ? ? ? ? ? ? ? ?class="underline"
 ? ? ? ? ? ? ? ? ? ?>服務中心</span
 ? ? ? ? ? ? ? ?>中進行反饋
 ? ? ? ? ? ?</div>
 ? ? ? ?</div>
 ? ?</div>
</template>

 <script>
export default {
 ? ?components: {},
 ? ?props: [],
 ? ?data() {
 ? ? ? ?return {
 ? ? ? ? ? ?getResultInterval: null,
 ? ? ? ? ? ?payDialogVisible: false,
 ? ? ? ? ? ?orderNo: null,
 ? ? ? ? ? ?isWx: false,
 ? ? ? ? ? ?payInfo: null,
 ? ? ? ? ? ?params: null,
 ? ? ?  };
 ?  },
 ? ?mounted() {
 ? ? ? ?this.isWx = this.$isWx();
 ? ? ? ?this.orderNo = this.$route.query.orderNo;
 ? ? ? ?this.payInfo = this.$route.query.codeUrl;

 ? ? ? ?// 支付方式,1:微信內支付,2:MWEB支付
 ? ? ? ?this.platform = this.$route.query.platform;
 ? ? ? ?this.params = this.$route.query.params;

 ? ? ? ?this.doPay();
 ?  },
 ? ?destroyed() {
 ? ? ? ?if (this.getResultInterval) {
 ? ? ? ? ? ?clearInterval(this.getResultInterval);
 ? ? ?  }
 ?  },
 ? ?methods: {
 ? ? ? ?doPay() {
 ? ? ? ? ? ?if (this.platform === 1 || this.platform === "1") {
 ? ? ? ? ? ? ? ?// 微信內支付
 ? ? ? ? ? ? ? ?if (typeof WeixinJSBridge == "undefined") {
 ? ? ? ? ? ? ? ? ? ?if (document.addEventListener) {
 ? ? ? ? ? ? ? ? ? ? ? ?document.addEventListener(
 ? ? ? ? ? ? ? ? ? ? ? ? ? ?"WeixinJSBridgeReady",
 ? ? ? ? ? ? ? ? ? ? ? ? ? ?this.onBridgeReady,
 ? ? ? ? ? ? ? ? ? ? ? ? ? ?false
 ? ? ? ? ? ? ? ? ? ? ?  );
 ? ? ? ? ? ? ? ? ?  } else if (document.attachEvent) {
 ? ? ? ? ? ? ? ? ? ? ? ?document.attachEvent(
 ? ? ? ? ? ? ? ? ? ? ? ? ? ?"WeixinJSBridgeReady",
 ? ? ? ? ? ? ? ? ? ? ? ? ? ?this.onBridgeReady
 ? ? ? ? ? ? ? ? ? ? ?  );
 ? ? ? ? ? ? ? ? ? ? ? ?document.attachEvent(
 ? ? ? ? ? ? ? ? ? ? ? ? ? ?"onWeixinJSBridgeReady",
 ? ? ? ? ? ? ? ? ? ? ? ? ? ?this.onBridgeReady
 ? ? ? ? ? ? ? ? ? ? ?  );
 ? ? ? ? ? ? ? ? ?  }
 ? ? ? ? ? ? ?  } else {
 ? ? ? ? ? ? ? ? ? ?this.onBridgeReady();
 ? ? ? ? ? ? ?  }
 ? ? ? ? ?  } else {
 ? ? ? ? ? ? ? ? // 非微信內支付(MWEB)
 ? ? ? ? ? ? ? ?var url = this.payInfo;
 ? ? ? ? ? ? ? ?window.open(url, "_self");
 ? ? ? ? ?  }
 ? ? ?  },

 ? ? ? ?onBridgeReady() {
 ? ? ? ? ? ?let _this = this;

 ? ? ? ? ? ?window.WeixinJSBridge.invoke(
 ? ? ? ? ? ? ? ?"getBrandWCPayRequest",
 ? ? ? ? ? ? ? ?this.params,
 ? ? ? ? ? ? ? ?function (res) {
 ? ? ? ? ? ? ? ? ? ?if (res.err_msg == "get_brand_wcpay_request:ok") {
 ? ? ? ? ? ? ? ? ? ? ? ?// 使用以上方式判斷前端返回,微信團隊鄭重提示:
 ? ? ? ? ? ? ? ? ? ? ? ?//res.err_msg將在用戶支付成功后返回ok,但并不保證它絕對可靠。
 ? ? ? ? ? ? ? ? ? ? ? ?alert(
 ? ? ? ? ? ? ? ? ? ? ? ? ? ?"支付成功,您可以在賬戶中心/消費記錄中查看歷史訂單"
 ? ? ? ? ? ? ? ? ? ? ?  );
 ? ? ? ? ? ? ? ? ? ? ? ?_this.$goPath("/user");
 ? ? ? ? ? ? ? ? ?  }
 ? ? ? ? ? ? ?  }
 ? ? ? ? ?  );
 ? ? ?  },
 ?  },
};
</script> 

對于微信內支付,可以通過其瀏覽器的WeixinJSBridge對象調起微信支付界面。

非微信瀏覽器,將表單中codeURL(二維碼)加載到頁面中即可。 (目前我的項目代碼在移動端非微信瀏覽器中展示的仍舊是二維碼,暫未做改造,所以上面非微信瀏覽器的與PC端處理基本一樣,后續會對這部分進行改造)

到此就等用戶支付完成即可。

5.4 接收支付結果

當用戶支付完成后,會跳轉到支付前的頁面,這個時候可以在這個頁面中做一些操作,來查詢訂單狀態并展示給用戶。

在5.2生成訂單的參數中,我們指定了notify_url,那么在支付成功后微信也會同時往這個所配的地址推送支付結果,代碼實現如下:

/**
 ? ? * 接收微信支付結果通知
 ? ? *
 ? ? * @param body 微信支付結果
 ? ? */
@PostMapping("wx-notify")
public String wxNotify(@RequestBody String body) {
 ? ?wxPayService.parseAndSaveTradeResult(body);
 ? ?return "success";
}
/**
 ? ? * 支付結果解析
 ? ? */
public void parseAndSaveTradeResult(String body) {
 ? ?try {
 ? ? ? ?if (logger.isDebugEnabled()) {
 ? ? ? ? ? ?logger.debug("接收到微信支付結果通知: {}", body);
 ? ? ?  }

 ? ? ? ?Map<String, String> map = WXPayUtil.xmlToMap(body);
 ? ? ? ?String tradeNo = MapUtils.getString(map, "out_trade_no");
 ? ? ? ?if (StringUtils.isEmpty(tradeNo)) {
 ? ? ? ? ? ?logger.warn("微信通知消息中訂單號為空");
 ? ? ? ? ? ?return;
 ? ? ?  }

 ? ? ? ?TradeDTO trade = tradeService.findByNo(tradeNo);
 ? ? ? ?if (null == trade) {
 ? ? ? ? ? ?logger.warn("交易不存在,通知消息:{}", body);
 ? ? ? ? ? ?return;
 ? ? ?  }

 ? ? ? ?if (trade.getState() == 1) {
 ? ? ? ? ? ?if (logger.isDebugEnabled()) {
 ? ? ? ? ? ? ? ?logger.debug("訂單已成功: {}", body);
 ? ? ? ? ?  }

 ? ? ? ? ? ?return;
 ? ? ?  }

 ? ? ? ?String result = MapUtils.getString(map, "result_code", "");
 ? ? ? ?if ("SUCCESS".equals(result)) {
 ? ? ? ? ? ?// 支付成功
 ? ? ? ? ? ?tradeService.tradeSuccess(trade);
 ? ? ?  } else {
 ? ? ? ? ? ?logger.warn("支付失敗,返回消息:{}", body);

 ? ? ? ? ? ?tradeService.tradeFailed(trade);
 ? ? ?  }
 ?  } catch (Exception e) {
 ? ? ? ?logger.error("返回結果:{}", body);
 ? ? ? ?logger.error("XML轉換成Map異常", e);
 ?  }
}

接收到消息后更新訂單狀態,并進行其它一些如賬戶余額修改等處理。

(0)

相關推薦

版權聲明:本文內容由互聯網用戶自發貢獻,該文觀點僅代表作者本人。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如發現本站有涉嫌抄襲侵權/違法違規的內容, 請發送郵件至 舉報,一經查實,本站將立刻刪除。

發表評論

登錄后才能評論
国产精品区一区二区免费