微信App支付源码&坑注释
部分的代码,因为代码是copy的我自己代码,然后再进行部分的编辑和注释,所以在使用的时候有可能有欠缺,不过整体来说,应该不影响使用的.如果有疑问,可以留言.在微信App支付开发中,流程大致分为:1.调用统一下单接口,生成预支付订单,并且进行二次加签,返回给客户端进行调起微信.2:回调逻辑.遇见问题,多多百度,多尝试.
Controller:
/**
* @Description: 订单微信支付
*/
@PostMapping(value = "/payRequest")
@ApiOperation("统一下单微信支付")
public Result payRequest(@RequestBody MallOrderPayDto mallOrderPayDto, HttpServletRequest request){
Map<String, String> map = new HashMap<String, String>();
try {
map = MallTenPayService.payRequest(mallOrderPayDto);
return super.success(map);
} catch (Exception e) {
e.printStackTrace();
}
return super.error();
}
/**
* @Description: 订单微信支付回调
*/
@PostMapping(value = "/tenPayOrderCallBack")
@ApiOperation("微信支付回调")
public Map<String, String> tenPayOrderCallBack(HttpServletRequest request){
Map<String, String> map = new HashMap<String, String>();
try {
TenPayVO tenPayVO = MallTenPayService.tenPayCallBack(request);
friendsMallTenPayService.tenPayOrderCallBack(tenPayVO);
map.put("return_code", "SUCCESS");
map.put("return_msg", "OK");
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
Service::
public interface MallTenPayService {
public Map<String, String> payRequest(OrderPayDto orderPayDto) throws Exception ;
public TenPayVO tenPayCallBack(HttpServletRequest request) throws Exception;
public void tenPayOrderCallBack(TenPayVO tenPayVO) throws Exception;
}
ServiceImpl:
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class FriendsMallTenPayServiceImpl implements FriendsMallTenPayService {
//订单支付微信回调地址
public static String TENPAY_ORDER_CALLBACK = "http://127.0.0.1:8080/api/tenpay/tenPayOrderCallBack";
@Override
public Map<String, String> payRequest(MallOrderPayDto mallOrderPayDto) throws Exception {
/**
* 本处两个map,在代码上是不应该出现的,但是在调试的时候,遇见了坑,为了代码更
* 清晰一点,就new 了两个Map,一个是加签使用,一个是返回给客户端使用.
* newMap:返回给服务端的
* newData:二次加签的
**/
Map<String,String> newMap = Maps.newHashMap();
Map<String, String> newData = new HashMap<String, String>();
TenpayConfig tenpayConfig = new TenpayConfig();
//AppId:在微信开发平台就可以获取的
tenpayConfig.setAppId("wx111111111111");
/**
* Key:特别声明下,此处是个坑,官方文档也没有具体的描述,很多第一次使用的同学
* 会直接使用开发平台的key,然后加签失败.该key需要登录商户平台进行设置的.
* 一定一定要登录商户平台去设置哦,不是开发平台.
**/
tenpayConfig.setKey("XJEJWchdjqfjlwjfwpq13ndo");
//商户号,开发平台可以获取
tenpayConfig.setMchId("3213131212");
//固定写死,不要问为什么,官方要求的
tenpayConfig.setPackageValue("Sign=WXPay");
MyPayConfig myPayConfig = new MyPayConfig(tenpayConfig);
// WXPay是目前可以使用的官方jar.
WXPay wxPay = new WXPay(myPayConfig);
log.info("封装wxpay支付请求参数==================");
// 开始第一次加签
Map<String, String> paramsMap = new HashMap<>();
//随机字符串
String nonce_str = WXPayUtil.generateNonceStr();
paramsMap.put("nonce_str", nonce_str);
//描述
paramsMap.put("body", "支付描述");
//商户订单号(支付编号)
//支付编号
String orderNum = PayUtils.getOrderNumber();
paramsMap.put("out_trade_no", orderNum);
//支付金额,金额单位为 分
double price = monery.doubleValue();
int totalFee = (int) (price * 100);
paramsMap.put("total_fee", String.valueOf(totalFee));
//回调地址
paramsMap.put("notify_url", TENPAY_ORDER_CALLBACK);
//交易类型
paramsMap.put("trade_type", "APP");
//用户端ip
String spbillCreateIp = "";
InetAddress inetAddress = InetAddress.getLocalHost();
if (inetAddress != null) {
spbillCreateIp = inetAddress.getHostAddress();
}
paramsMap.put("spbill_create_ip", spbillCreateIp);
Map<String,String> resp = wxPay.unifiedOrder(paramsMap);
log.info("reponseMap:{}",resp);
if (resp.get("result_code") != null && "SUCCESS".equals(resp.get("result_code"))) {
String prepayid = resp.get("prepay_id");
nonce_str = resp.get("nonce_str");
log.info("nonce_str:{}",nonce_str);
// 二次加签开始
/**
* 巨坑描述: 微信的二次加签是真的超级坑的,文档的描述很简单,我加签要那
* 些那些参数,具体怎么要,你猜,参数key? 你继续猜.我猜了..不,你猜错了.
* 官方文档上写的是驼峰要求的,实际编码却需要这种全小写的,也是坑
* 死人不偿命的那种.这么大公司,缺这维护文档的五毛钱吗?
* 如果从上看下来的,就会发现不同.一次加签,使用的是下划线,
* 二次加签必须使用全小写的key,要不也可以加签成功,并且使用sign验证工具
* 检查也是可以通过验证的.这就有点麻爪了.本人就被此处的问题,卡了6个小
* 时,一脸懵逼.很自信的对客户端的同学说,我的sign没问题,我在官网都验证了
* ,但是客户端同学就是调不起来微信,一直返回-1.
* 另外,需要注意的一个点就是,最终给客户端返回的Sign不能是第一次加签
* 返回的sign值,是必须进行二次加签后的Sign.
*
**/
//AppId和上面的一致
newData.put("appid", tenpayConfig.getAppId());
//订单号和上面的一致
newData.put("noncestr", nonce_str);
//还是要写死
newData.put("package","Sign=WXPay");
//商户号和上面的一致
newData.put("partnerid",tenpayConfig.getMchId());
//这个是微信返回的预支付订单号
newData.put("prepayid",prepayid );
//新的时间戳:要10位的长度.切记
newData.put("timestamp", System.currentTimeMillis() / 1000 + "");
log.info("newData:{}",newData);
//和上面的签名方式一样,也是调用微信支付的一个工具类直接生成的
String wxUyilSign = WXPayUtil.generateSignature(newData, tenpayConfig.getKey(), WXPayConstants.SignType.MD5);
log.info("paySign:{}",wxUyilSign);
//开始封装给客户端返回的结果
newMap.put("appId", tenpayConfig.getAppId());
newMap.put("partnerId",tenpayConfig.getMchId());
newMap.put("timeStamp", System.currentTimeMillis() / 1000 + "");
newMap.put("nonceStr", nonce_str);
newMap.put("prepayId", prepayid);
newMap.put("package","Sign=WXPay");
newMap.put("paySign",wxUyilSign);
} else {
throw new Exception("签名错误");
}
log.info("weixin 参数封装完成,开始入库流水=================订单号:{}",orderNum);
return newMap;
}
@Override
public TenPayVO tenPayCallBack(HttpServletRequest request) throws Exception {
log.info("weixinpay callBack start ==============================");
InputStream inputStream = request.getInputStream();
StringBuffer resXml = new StringBuffer();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
String str;
while ((str = bufferedReader.readLine()) != null) {
resXml.append(str);
}
bufferedReader.close();
inputStream.close();
log.info("微信回调报文: " + resXml);
TenPayVO tenPayVO = this.tenPayCallBackInfo(resXml.toString(), "xml", "");
return tenPayVO;
}
/**
* @Description: 微信支付回调返回结果
* @Param: [xml, rootName, rowName]
*/
private TenPayVO tenPayCallBackInfo(String xml, String rootName, String rowName) throws Exception {
Map<String, Object> resHashMap = (Map<String, Object>) TenPayUtils.readXml(xml, "xml", "");
SortedMap<String, Object> resMap = new TreeMap<String, Object>(resHashMap);
//微信返回状态码
if (!resMap.get("return_code").equals("SUCCESS")) {
log.error("微信支付回调连接失败: " + resMap.get("return_msg"));
throw new Exception("微信支付回调连接失败");
}
//业务结果
if (!resMap.get("result_code").equals("SUCCESS")) {
log.error("err_code: " + resMap.get("err_code"), "err_code_des: " + resMap.get("err_code_des"));
throw new Exception(resMap.get("err_code_des").toString());
}
TenPayUtils tenPayUtils = new TenPayUtils();
//校验签名
String sign = tenPayUtils.createSign(resMap, "UTF-8");
if (!sign.equals(resMap.get("sign"))) {
log.error("微信支付回调签名不正确");
throw new Exception("微信支付回调签名不正确");
}
TenPayVO tenPayVO = new TenPayVO();
//商户订单号
tenPayVO.setOutTradeNo((String) resMap.get("out_trade_no"));
//业务结果
tenPayVO.setResultCode((String) resMap.get("result_code"));
//签名方式
tenPayVO.setSignType("ASCII");
//签名
tenPayVO.setSign((String) resMap.get("sign"));
//交易类型
tenPayVO.setTradeType("APP");
//交易状态
tenPayVO.setTradeState((String) resMap.get("trade_state"));
//商户号
tenPayVO.setMchId((String) resMap.get("mch_id"));
//付款银行
tenPayVO.setBankType((String) resMap.get("bank_type"));
//交易金额
BigDecimal totalFee = new BigDecimal((String) resMap.get("total_fee"));
totalFee = totalFee.divide(new BigDecimal(100));
tenPayVO.setTotalFee(totalFee);
//币种
if (resMap.containsKey("fee_type")) {
tenPayVO.setFeeType((String) resMap.get("fee_type"));
}
//微信支付订单号
tenPayVO.setTransactionId((String) resMap.get("transaction_id"));
//支付完成时间
tenPayVO.setTimeEnd((String) resMap.get("time_end"));
return tenPayVO;
}
/**
* @Description: 微信支付订单回调
* @Param: [tenPayVO]
*/
@Override
public void tenPayOrderCallBack(TenPayVO tenPayVO) throws Exception {
if (tenPayVO != null && tenPayVO.getResultCode().equals("SUCCESS") && tenPayVO.getTradeState().equals("SUCCESS")) {
//根据交易编号加锁,处理高并发
synchronized (tenPayVO.getOutTradeNo()) {
log.info("1.微信支付 回调开始============:{}",tenPayVO);
if (tenPayVO != null && StringUtil.isNotEmpty(tenPayVO.getOutTradeNo())) {
synchronized (tenPayVO.getOutTradeNo()) {
MallUserRechargeDetail order = friendsMallUserRechargeDetailService.selectByOrderId(tenPayVO.getOutTradeNo());
if (order.getStatus().equals(0)) {
//交易支付成功
log.info("2.支付状态成功:TRADE_SUCCESS");
//修改订单状态
log.info("9.订单状态修改结果,订单号:{},订单状态:{}",tenPayVO.getOutTradeNo(),orderFlag);
//后续的业务逻辑就自己来吧
} else {
log.info("24.该订单已支付处理,交易编号为: " + tenPayVO.getOutTradeNo());
}
}
}
}
}
}
}
实体类:
public class MyPayConfig implements WXPayConfig {
private TenpayConfig payConfig;
private byte[] certData;
public MyPayConfig(TenpayConfig config) throws IOException {
this.payConfig = config;
// File file = new File(payConfig.getCertPath());
// InputStream certStream = new FileInputStream(file);
// this.certData = new byte[(int) file.length()];
// certStream.read(this.certData);
// certStream.close();
}
@Override
public String getAppID() {
return payConfig.getAppId();
}
@Override
public String getMchID() {
return payConfig.getMchId();
}
@Override
public String getKey() {
return payConfig.getKey();
}
@Override
public InputStream getCertStream() {
return null;
}
// @Override
// public InputStream getCertStream() {
// ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);
// return certBis;
// }
@Override
public int getHttpConnectTimeoutMs() {
return 8000;
}
@Override
public int getHttpReadTimeoutMs() {
return 10000;
}
}
/**
* 微信支付配置类
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class TenpayConfig {
//appId
private String appId;
//商户号
private String mchId;
//商户的key(API密匙)
private String key;
//API支付请求地址
private String payUrl;
//API查询请求地址
private String queryUrl;
//Sign=WXPay
private String packageValue;
}
**依赖:**
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>