java整合微信支付功能詳細(xì)示例
微信服務(wù)商戶號(hào)和普通商戶號(hào)的區(qū)別
1. 適用范圍不同:
微信服務(wù)商戶號(hào)適用于代理商、第三方服務(wù)商等,也就是說(shuō),只有中間商才可以成功創(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),管理起來(lái)比較繁瑣。
3. 申請(qǐng)及審核方式不同:
申請(qǐng)微信服務(wù)商戶號(hào)需要提供相關(guān)商業(yè)資質(zhì)證明,如公司注冊(cè)證明、公司經(jīng)營(yíng)資質(zhì)證明等,審核通過(guò)后方可獲得認(rèn)證。
普通商戶號(hào)的申請(qǐng)相對(duì)較為簡(jiǎn)單,只需要提供公司營(yíng)業(yè)執(zhí)照、結(jié)算銀行賬號(hào)等資料,審核通過(guò)后即可開通商戶號(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)通過(guò)
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-09
java 使用idea將工程打成jar并創(chuàng)建成exe文件類型執(zhí)行的方法詳解
這篇文章主要介紹了java 使用idea將工程打成jar并創(chuàng)建成exe文件類型執(zhí)行,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2020-09-09
SpringSessionRedis配置及發(fā)現(xiàn)的問題講解
今天小編就為大家分享一篇關(guān)于SpringSessionRedis配置及發(fā)現(xiàn)的問題講解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03
基于IDEA查看maven依賴結(jié)構(gòu)流程解析
這篇文章主要介紹了基于IDEA查看maven依賴結(jié)構(gòu)流程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09
Kotlin與java8的SAM轉(zhuǎn)換對(duì)比(進(jìn)階)
這篇文章主要介紹了Kotlin與java8的SAM轉(zhuǎn)換對(duì)比,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05

