java整合微信支付功能詳細(xì)示例
微信服務(wù)商戶號(hào)和普通商戶號(hào)的區(qū)別
1. 適用范圍不同:
微信服務(wù)商戶號(hào)適用于代理商、第三方服務(wù)商等,也就是說,只有中間商才可以成功創(chuàng)建微信服務(wù)商戶號(hào)。而普通商戶號(hào)適用于普通商家。
2. 支付方式不同:
微信服務(wù)商戶號(hào)支持多個(gè)子商戶從屬于一個(gè)服務(wù)商,實(shí)現(xiàn)統(tǒng)一線上收款和資金結(jié)算,同時(shí)支持分賬等多種支付方式,方便代理商為子商戶提供中間擔(dān)保結(jié)算、派單抽成、資金歸集、單一入口、風(fēng)控應(yīng)用等服務(wù)。
普通商戶號(hào)只能被單獨(dú)占有,支持一種支付方式,即從客戶直接收款,對(duì)于多個(gè)門店的商家,需要分別開通每個(gè)門店的商戶號(hào),管理起來比較繁瑣。
3. 申請(qǐng)及審核方式不同:
申請(qǐng)微信服務(wù)商戶號(hào)需要提供相關(guān)商業(yè)資質(zhì)證明,如公司注冊(cè)證明、公司經(jīng)營(yíng)資質(zhì)證明等,審核通過后方可獲得認(rèn)證。
普通商戶號(hào)的申請(qǐng)相對(duì)較為簡(jiǎn)單,只需要提供公司營(yíng)業(yè)執(zhí)照、結(jié)算銀行賬號(hào)等資料,審核通過后即可開通商戶號(hào)。
總之,微信服務(wù)商戶號(hào)適合代理商、第三方服務(wù)商等機(jī)構(gòu)場(chǎng)景,可以實(shí)現(xiàn)代理收款、分賬結(jié)算等多種支付服務(wù),方便代理商為子商戶提供完整的商業(yè)解決方案;而普通商戶號(hào)適合單個(gè)商家接受在線收款,管理更為簡(jiǎn)單。
使用
這里我們就先配置服務(wù)商號(hào)
微信商戶號(hào)中配置信息
- 需要提供企業(yè)相關(guān)的資料進(jìn)行等待審核大概在一周左右
- 開通商戶號(hào)
- 創(chuàng)建子商戶號(hào)
- 生成API3私鑰和密鑰
- 授權(quán)支付回調(diào)目錄
- 在微信開發(fā)者平臺(tái)中小程序關(guān)聯(lián)服務(wù)商戶號(hào)
代碼
Jar依賴
<dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-csv</artifactId> <version>2.11.2</version> </dependency> <dependency> <groupId>com.github.wechatpay-apiv3</groupId> <artifactId>wechatpay-apache-httpclient</artifactId> <version>0.2.2</version> </dependency>
配置微信API
#微信配置 wx-applet: url: https://api.mch.weixin.qq.com/papay/querycontract payUrl: https://api.mch.weixin.qq.com/pay/pappayapply notifyUrl: https://chillman.com.cn/applet/applyPayNotify qureyOrderUrl: https://chillman.com.cn/pay/paporderquery appId: 小程序應(yīng)用ID appSecret: 小程序密鑰 mchId: 子商戶id #微信支付配置 wechat-pay: url: https://api.mch.weixin.qq.com appId: 小程序Id mchId: 商戶號(hào) mchSerialNo: apiV3Key: 私鑰key # 商戶key mchPrivateKey: 私鑰 notifyUrl: 回調(diào)地址 apiClientKey: 私鑰 certificatesUrl: https://api.mch.weixin.qq.com/v3/certificates
微信API工具類
package com.snow.param; import lombok.Data; /** * @Author: zhaohaoxin * @Date: 2023-06-02-16:10 */ @Data public class PlaceAnOrderParam2 { private String sp_appid; private String sp_mchid; private String sub_appid; private String sub_mchid; private String description; private String out_trade_no; private String notify_url; private AmountParam amount; private PayerParam payer; public PlaceAnOrderParam2(String sp_appid, String sp_mchid, String sub_appid, String sub_mchid, String description, String out_trade_no, String notify_url, AmountParam amount, PayerParam payer) { this.sp_appid = sp_appid; this.sp_mchid = sp_mchid; this.sub_appid = sub_appid; this.sub_mchid = sub_mchid; this.description = description; this.out_trade_no = out_trade_no; this.notify_url = notify_url; this.amount = amount; this.payer = payer; } }
package com.snow.service.imple; import com.fasterxml.jackson.databind.ObjectMapper; import com.snow.api.WXPayRequest; import com.snow.constant.PathConstant; import com.snow.dto.WXPayDto; import com.snow.param.OrderStatusParam; import com.snow.param.PlaceAnOrderParam; import com.snow.param.PlaceAnOrderParam2; import com.snow.param.SingParam; import com.snow.service.WXPayService; import com.snow.sgin.NewPaySignUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service @Slf4j public class WXPayServiceImpl implements WXPayService { @Autowired private WXPayRequest wxPayRequest; @Autowired private NewPaySignUtil newPaySignUtil; public static final String SING_TYPE = "RSA_1_256"; public static final String PATH_JSAPI = "/v3/pay/partner/transactions/jsapi"; @Override public String WXPayUnifyOrder(PlaceAnOrderParam placeAnOrderParam) { try { String reqBody = ""; if (placeAnOrderParam != null) { ObjectMapper objectMapper = new ObjectMapper(); reqBody = objectMapper.writeValueAsString(placeAnOrderParam); } return wxPayRequest.requestOnce(PATH_JSAPI, reqBody); }catch (Exception e){ log.error("調(diào)用微信支付統(tǒng)一下單失敗",e); return StringUtils.EMPTY; } } @Override public String WXPayUnifyOrder(PlaceAnOrderParam2 placeAnOrderParam) { try { String reqBody = ""; if (placeAnOrderParam != null) { ObjectMapper objectMapper = new ObjectMapper(); reqBody = objectMapper.writeValueAsString(placeAnOrderParam); } return wxPayRequest.requestOnce(PathConstant.PATH_JSAPI, reqBody); }catch (Exception e){ log.error("調(diào)用微信支付統(tǒng)一下單失敗",e); return StringUtils.EMPTY; } } @Override public String getOrderStatus(OrderStatusParam orderStatusParam) { try { String reqBody = ""; String url = PathConstant.PATH_GET_ORDER + orderStatusParam.getOutTradeNo() + "?mchid="+orderStatusParam.getMchid(); return wxPayRequest.requestOnce(url,reqBody); }catch (Exception e){ log.error("調(diào)用查詢訂單狀態(tài)接口失敗",e); return StringUtils.EMPTY; } } @Override public WXPayDto getWXPaySing(SingParam singParam) { String appid = singParam.getAppid(); long timeStamp = System.currentTimeMillis() / 1000; String nonceStr = RandomStringUtils.randomAlphanumeric(32); String prepayId = "prepay_id=" + singParam.getPrepayId(); String preStr = appid+"\n" +timeStamp+"\n" +nonceStr+"\n" +prepayId+"\n"; String paySign = newPaySignUtil.getSign(SING_TYPE, preStr); WXPayDto wxPayDto = new WXPayDto(timeStamp,prepayId,nonceStr,paySign); return wxPayDto; } }
發(fā)起支付后獲取微信小程序根據(jù)prepayId參數(shù)調(diào)用支付方法彈出支付會(huì)話框
微信回調(diào)
package com.snow.controller; import com.alibaba.fastjson.JSON; import com.snow.annotation.SystemLog; import com.snow.business.domain.XqSnowOrder; import com.snow.business.domain.XqSnowOrderExample; import com.snow.business.service.XqSnowOrderService; import com.snow.common.exception.RequestWechatException; import com.snow.common.utils.DateUtils; import com.snow.enumz.OrderStateEnum; import com.snow.pem.PayResponseUtils; import com.snow.pem.StaticParameter; import com.wechat.pay.contrib.apache.httpclient.util.AesUtil; import lombok.extern.slf4j.Slf4j; import net.sf.json.JSONArray; import net.sf.json.JSONObject; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.util.Base64Utils; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.math.BigDecimal; import java.security.GeneralSecurityException; import java.security.Signature; import java.security.cert.X509Certificate; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; @Controller @RestController @RequestMapping("/wxPay/callBack") @Slf4j public class WXPayCallBackController { @Value("${wechat-pay.apiV3Key}") private String apiV3Key; @Autowired private XqSnowOrderService orderService; @SystemLog(menu = "微信支付支付結(jié)果通知回調(diào)") @PostMapping("wxPayRequest") public String wxPayRequest(HttpServletRequest request, HttpServletResponse response){ log.info("==微信回調(diào)開始=="); Map<String, String> map = new HashMap<>(); try { InputStream in = request.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8")); StringBuffer param = new StringBuffer(); String line; while ((line = br.readLine()) != null) { param.append(line); } String callBackParam = param.toString(); JSONObject object = JSONObject.fromObject(callBackParam); log.info("微信回調(diào)參數(shù)={}" + callBackParam); //回調(diào)摘要 String summary = object.getString("summary"); //如果驗(yàn)證簽名序列號(hào)通過 if (verifiedSign(request,callBackParam)){ JSONObject resource = object.getJSONObject("resource"); String associatedData = resource.getString("associated_data"); String nonce = resource.getString("nonce"); String ciphertext = resource.getString("ciphertext"); AesUtil aesUtil = new AesUtil(apiV3Key.getBytes()); String res = aesUtil.decryptToString(associatedData.getBytes(), nonce.getBytes(), ciphertext); JSONObject resObj = JSONObject.fromObject(res); log.info("微信通知支付成功通知參數(shù)={}",resObj); //交易狀態(tài) String tradeState = resObj.getString("trade_state"); //訂單號(hào) String outTradeNo = resObj.getString("out_trade_no"); //支付完成時(shí)間 String time_end = resObj.getString("success_time"); //微信交易訂單號(hào) String transactionId = resObj.getString("transaction_id"); //訂單金額 com.alibaba.fastjson.JSONObject amount = JSON.parseObject(resObj.getString("amount")); //總金額 String total = amount.getString("total"); //用戶支付金額 String payerTotal = amount.getString("payer_total"); OrderStateEnum orderStateEnum = OrderStateEnum.OK; if (tradeState.equals("SUCCESS")){ //查詢訂單狀態(tài) XqSnowOrderExample snowOrderExample = new XqSnowOrderExample(); snowOrderExample.createCriteria().andorderNoEqualTo(outTradeNo); XqSnowOrder snowOrder = orderService.selectFirstByExample(snowOrderExample); //訂單已經(jīng)處理 if(snowOrder.getPayStatus().equals(orderStateEnum.getState()) ){ log.info("扣款失敗訂單已經(jīng)處理,訂單號(hào):"+outTradeNo+",訂單狀態(tài):"+snowOrder.getPayStatus()); map.put("code", "SUCCESS"); map.put("message", "成功"); return JSON.toJSONString(map); } Date timeEnd = null; try { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"); timeEnd = sdf.parse(time_end); } catch (ParseException e) { e.printStackTrace(); } BigDecimal cashFee = new BigDecimal(payerTotal).divide(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_DOWN); XqSnowOrder snowOrderEdition = new XqSnowOrder(); //微信支付交易號(hào) snowOrderEdition.setWxOrderNo(transactionId); //修改訂單狀態(tài)為已支付 snowOrderEdition.setPayStatus(orderStateEnum.getState()); snowOrderEdition.setPayTime(timeEnd); if (Integer.valueOf(total)-Integer.valueOf(payerTotal)>0){ //有優(yōu)惠 //代金券或立減優(yōu)惠金額 //優(yōu)惠功能 if(resObj.containsKey("promotion_detail")){ JSONArray promotionDetails = JSONArray.fromObject(resObj.getString("promotion_detail")); if(promotionDetails != null){ JSONObject promotionDetail = promotionDetails.getJSONObject(0); String coupon_fee = promotionDetail.getString("amount"); if (!StringUtils.isEmpty(coupon_fee)){ BigDecimal couponFee = new BigDecimal(coupon_fee).divide(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_DOWN); //tMoheOrder.setWxCouponFee(couponFee); log.info("訂單號(hào):{},有優(yōu)惠-->couponFee:,{}",outTradeNo,coupon_fee); } //代金券或立減優(yōu)惠ID String coupon_id_$n = promotionDetail.getString("coupon_id"); if (!StringUtils.isEmpty(coupon_id_$n)){ //tMoheOrder.setWxCouponId(coupon_id_$n); log.info("訂單號(hào):{},,代金券或立減優(yōu)惠ID:{}",outTradeNo,coupon_id_$n); } } } log.info("此訂單有優(yōu)惠,訂單號(hào):"+outTradeNo); } //沒有優(yōu)惠 XqSnowOrderExample snowOrderExampleEdit = new XqSnowOrderExample(); snowOrderExampleEdit.createCriteria().andorderNoEqualTo(outTradeNo); orderService.updateByExampleSelective(snowOrderEdition,snowOrderExampleEdit); log.info("扣款訂單微信支付處理成功,此訂單已經(jīng)支付,訂單號(hào)碼:"+outTradeNo); }else{ log.info("微信返回支付錯(cuò)誤摘要:"+summary); } map.put("code", "SUCCESS"); map.put("message", "成功"); }else{ log.info("扣款回調(diào)簽名驗(yàn)證失敗"); } } catch (IOException e) { log.error("微信支付支付結(jié)果通知回調(diào)接口異常:",e); } catch (ParseException e) { log.error("微信支付支付結(jié)果通知回調(diào)接口異常:",e); } catch (RequestWechatException e) { log.error("微信支付支付結(jié)果通知回調(diào)接口異常:",e); } catch (GeneralSecurityException e) { log.error("微信支付支付結(jié)果通知回調(diào)接口異常:",e); } return JSON.toJSONString(map); } /** * 驗(yàn)證微信簽名 * @param request * @param body * @return * @throws GeneralSecurityException * @throws IOException * @throws InstantiationException * @throws IllegalAccessException * @throws ParseException */ private boolean verifiedSign(HttpServletRequest request,String body) throws GeneralSecurityException, ParseException, RequestWechatException { //微信返回的證書序列號(hào) String serialNo = request.getHeader("Wechatpay-Serial"); //微信返回的隨機(jī)字符串 String nonceStr = request.getHeader("Wechatpay-Nonce"); //微信返回的時(shí)間戳 String timestamp = request.getHeader("Wechatpay-Timestamp"); //微信返回的簽名 String wechatSign = request.getHeader("Wechatpay-Signature"); //組裝簽名字符串 String signStr = Stream.of(timestamp, nonceStr, body) .collect(Collectors.joining("\n", "", "\n")); //當(dāng)證書容器為空 或者 響應(yīng)提供的證書序列號(hào)不在容器中時(shí) 就應(yīng)該刷新了 if (StaticParameter.certificateMap.isEmpty() || !StaticParameter.certificateMap.containsKey(serialNo)) { PayResponseUtils.refreshCertificate(); } //根據(jù)序列號(hào)獲取平臺(tái)證書 X509Certificate certificate = StaticParameter.certificateMap.get(serialNo); //獲取失敗 驗(yàn)證失敗 if (certificate == null){ return false; } //SHA256withRSA簽名 Signature signature = Signature.getInstance("SHA256withRSA"); signature.initVerify(certificate); signature.update(signStr.getBytes()); //返回驗(yàn)簽結(jié)果 return signature.verify(Base64Utils.decodeFromString(wechatSign)); } }
效果
以上就是小程序支付的整個(gè)流程
總結(jié)
到此這篇關(guān)于java整合微信支付功能的文章就介紹到這了,更多相關(guān)java整合微信支付內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實(shí)現(xiàn)的簡(jiǎn)單圖片上傳功能示例
這篇文章主要介紹了Java實(shí)現(xiàn)的簡(jiǎn)單圖片上傳功能,結(jié)合實(shí)例形式分析了java圖片傳輸相關(guān)的檢驗(yàn)、傳輸、接收等相關(guān)操作技巧,需要的朋友可以參考下2017-09-09java 使用idea將工程打成jar并創(chuàng)建成exe文件類型執(zhí)行的方法詳解
這篇文章主要介紹了java 使用idea將工程打成jar并創(chuàng)建成exe文件類型執(zhí)行,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2020-09-09SpringSessionRedis配置及發(fā)現(xiàn)的問題講解
今天小編就為大家分享一篇關(guān)于SpringSessionRedis配置及發(fā)現(xiàn)的問題講解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-03-03基于IDEA查看maven依賴結(jié)構(gòu)流程解析
這篇文章主要介紹了基于IDEA查看maven依賴結(jié)構(gòu)流程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09Kotlin與java8的SAM轉(zhuǎn)換對(duì)比(進(jìn)階)
這篇文章主要介紹了Kotlin與java8的SAM轉(zhuǎn)換對(duì)比,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05