微信支付

java接入微信app支付

首先创建SortedMap<String, String> parameters = new TreeMap<String, String>();把所需的参数放到map里面
例如:
parameters.put(“appid”, myWXPayConfig.appID);//APPID(appID)
parameters.put(“mch_id”,myWXPayConfig.mchID);//商户号(mchID)
parameters.put(“nonce_str”, PayCommonUtil.CreateNoncestr());//随机字符串,不长于32位
parameters.put(“fee_type”, “CNY”);//货币类型
parameters.put(“notify_url”, myWXPayConfig.notifyUrl);//回调地址
parameters.put(“trade_type”, “APP”);//支付类型
parameters.put(“body”,“充值”+ amount+“个探心币”);//商品描述
parameters.put(“attach”,token + “;” + device + “;” + txmVisitorId);//自定义字段,此字段的值回调时原封返回
String outtradeno= PayCommonUtil.getDateStr();
String ip = request.getHeader(“x-forwarded-for”);
if(ip == null || ip.length() == 0 || “unknown”.equalsIgnoreCase(ip)) {
ip = request.getHeader(“Proxy-Client-IP”);
}
if(ip == null || ip.length() == 0 || “unknown”.equalsIgnoreCase(ip)) {
ip = request.getHeader(“WL-Proxy-Client-IP”);
}
if(ip == null || ip.length() == 0 || “unknown”.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
parameters.put(“spbill_create_ip”, ip);//支持IPV4和IPV6两种格式的IP地址。调用微信支付API的机器IP(就是用户的ip)
parameters.put(“out_trade_no”,outtradeno); // 订单id这里我的订单id生成规则是订单id+时间
amount=String.valueOf(Double.parseDouble(amount)*100);
amount=amount.substring(0,amount.indexOf("."));
parameters.put(“total_fee”, amount); // 订单总金额,单位为分 测试时,每次支付一分钱,微信支付所传的金额是以分为单位的,因此实际开发中需要x100
利用String sign = WXPayUtil.generateSignature(parameters, myWXPayConfig.key);得到sign,把sign页放到map里面
parameters.put(“sign”, sign);
把map集合转成xml格式String xml=WXPayUtil.mapToXml(parameters).replace("<?xml version=\"1.0\" endoding=\"1.0\" standalone=\"no\"?>","");
得到的结果是xml字符串,接下来把xml字符串转成map对象,得到prepay_id
Map<String,String> map=WXPayUtil.xmlToMap(xmlstr);

public void payNotify(HttpServletRequest request, HttpServletResponse response) throws Exception{
Map<String, String> reqData = wxPayClient.getNotifyParameter(request);
log.info(“支付成功回调”);
log.info(reqData.toString());
String returnCode = reqData.get(“return_code”);
String resultCode = reqData.get(“result_code”);
try{
if (WXPayConstants.SUCCESS.equals(returnCode) && WXPayConstants.SUCCESS.equals(resultCode)) {
//验证签名
boolean signatureValid = wxPay.isPayResultNotifySignatureValid(reqData);
//签名正确
if (signatureValid) {
//业务处理
Map<String, String> responseMap = new HashMap<>(2);
responseMap.put(“return_code”, “SUCCESS”);
responseMap.put(“return_msg”, “OK”);
//设置成功确认内容
String responseXml = WXPayUtil.mapToXml(responseMap);
/*
* 写入数据库内容在这里
*/
//获取用户数据
String attachStr =reqData.get(“attach”);
String token = attachStr.split(";")[0];
String device = attachStr.split(";")[1];
String txmVisitorId = attachStr.split(";")[2];
double money = Double.parseDouble(reqData.get(“cash_fee”))/100;
String outTradeNo = reqData.get(“out_trade_no”);
String transactionId = reqData.get(“transaction_id”);
if (!StringUtils.isEmpty(token)) {
Session session = (Session) MySessionContext.getInstance().getMymap().get(token);
if (Objects.nonNull(session)) {
TxmUser txmUser = (TxmUser) session.getAttribute(“session_user”);
if (Objects.nonNull(txmUser) && !StringUtils.isEmpty(txmUser.getTxmuserid())) {
String txmUserId = txmUser.getTxmuserid();
DecimalFormat df1 = new DecimalFormat(“0.00”);
TxmRecharge txmRecharge=new TxmRecharge();
txmRecharge.setTxmrechargemoney(String.valueOf(money));
txmRecharge.setTxmrechargeouttradeno(outTradeNo);
txmRecharge.setTxmrechargewxtradeno(transactionId);
txmRecharge.setTxmuserid(txmUserId);
txmRecharge.setTxmrechargetime(new Date());
// Android微信充值
if (StringUtils.isEmpty(device) || “null”.equals(device)) {
txmRecharge.setTxmrechargetype(“1”);
// 新增数据到充值记录表
txmRecahrgeService.insertRechargeByWx(txmRecharge);
// 查询该用户的原有余额
String txmuserbalance=txmUserService.selectMoneyByTxmuserid(txmUserId);
String allBalance=String.valueOf(df1.format(Double.parseDouble(txmuserbalance) + money));
//更新余额
int i= txmUserService.updateMoneyByTxmuserid(txmUserId,allBalance);
// 把新的余额添加到session里面去
txmUser.setTxmuserbalance(allBalance);
Subject subject = SecurityUtils.getSubject();
subject.getSession().setAttribute(“session_user”,txmUser);
} else {
// ios微信充值
txmRecharge.setTxmrechargetype(“1” + device);
// 新增数据到充值记录表
txmRecahrgeService.insertRechargeByWx(txmRecharge);
String allBalance = String.valueOf(df1.format(Double.parseDouble(txmUser.getTxmUserIOSBalance()) + money));
// 更新余额
int i = txmUserService.updateMoneyWithIOSByTxmUserId(txmUserId, allBalance);
// 查询是否与游客账户绑定,若绑定,则将用户账户余额同步【一个游客账户只能绑定一个正式账户,反之也是一样】
// 根据用户ID查询游客账户
TxmVisitor txmVisitor = txmVisitorMapper.selectTxmVisitorByUserId(txmUserId);
if (txmVisitor != null) {
// txmVisitor.getTxmVisitorBalance()不会为null,因为默认是0.00
String newTxmVisitorBalance = df1.format(Double.parseDouble(txmVisitor.getTxmVisitorBalance()) + money);
txmVisitorMapper.updateTxmVisitorBalanceByVisitorId(newTxmVisitorBalance, txmVisitor.getTxmVisitorId());
}
// 把新的余额添加到session里面去
txmUser.setTxmUserIOSBalance(allBalance);
Subject subject = SecurityUtils.getSubject();
subject.getSession().setAttribute(“session_user”,txmUser);
}
} else {
// 游客账户登录【不正常操作会走这里,基本不会发生】
updateAccountBalance(txmVisitorId, outTradeNo, transactionId, String.valueOf(money));
}
} else {
// 游客账户登录【当用户已经登录,但是服务器重启时会走这里】
updateAccountBalance(txmVisitorId, outTradeNo, transactionId, String.valueOf(money));
}
} else {
// 游客账户登录【正常退出登录token被清空会走这里】
updateAccountBalance(txmVisitorId, outTradeNo, transactionId, String.valueOf(money));
}
// 发送通知
response.setContentType(“text/xml”);
response.getWriter().write(responseXml);
response.flushBuffer();
}
}
}catch (Exception e){
e.printStackTrace();
}
}
用到的工具类
package com.twenty.eight.star.config.wxpayconfig;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import sun.misc.BASE64Decoder;

import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayConfig;
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;

import lombok.extern.slf4j.Slf4j;
/*
*
* @ClassName: WXPayClient
* @Description: 对WXPay的简单封装,处理支付密切相关的逻辑.
* @author:sarahacao
* @date 2018年11月13日
*
*/
@Slf4j
public class WXPayClient extends WXPay {

/** 密钥算法 */
private static final String ALGORITHM = "AES";
/** 加解密算法/工作模式/填充方式 */
private static final String ALGORITHM_MODE_PADDING = "AES/ECB/PKCS5Padding";
/** 用户支付中,需要输入密码 */
private static final String ERR_CODE_USERPAYING = "USERPAYING";
private static final String ERR_CODE_AUTHCODEEXPIRE = "AUTHCODEEXPIRE";
/** 交易状态: 未支付 */
private static final String TRADE_STATE_NOTPAY = "NOTPAY";

/** 用户输入密码,尝试30秒内去查询支付结果 */
private static Integer remainingTimeMs = 10000;

private WXPayConfig config;

public WXPayClient(WXPayConfig config, WXPayConstants.SignType signType, boolean useSandbox) {
    super(config, signType, useSandbox);
    this.config = config;
}
/**
 * 刷卡支付
 * 对WXPay#microPay(Map)增加了当支付结果为USERPAYING时去轮询查询支付结果的逻辑处理
 * 注意:该方法没有处理return_code=FAIL的情况,暂时不考虑网络问题,这种情况直接返回错误
 * @param reqData
 * @return
 * @throws Exception
 */
public Map<String, String> microPayWithPOS(Map<String, String> reqData) throws Exception {
    // 开始时间(毫秒)
    long startTimestampMs = System.currentTimeMillis();
    Map<String, String> responseMapForPay = super.microPay(reqData);
    log.info(responseMapForPay.toString());
    //先判断 协议字段返回(return_code),再判断 业务返回,最后判断 交易状态(trade_state)
    //通信标识,非交易标识
    String returnCode = responseMapForPay.get("return_code");
    if (WXPayConstants.SUCCESS.equals(returnCode)) {
        String errCode = responseMapForPay.get("err_code");
        // 余额不足,信用卡失效
        if (ERR_CODE_USERPAYING.equals(errCode) || "SYSTEMERROR".equals(errCode) || "BANKERROR".equals(errCode)) {
            Map<String, String> orderQueryMap = null;
            Map<String, String> requestData = new HashMap<>();
            requestData.put("out_trade_no", reqData.get("out_trade_no"));
            // 用户支付中,需要输入密码或系统错误则去重新查询订单API err_code, result_code, err_code_des
            // 每次循环时的当前系统时间 - 开始时记录的时间 > 设定的30秒时间就退出
            while (System.currentTimeMillis() - startTimestampMs < remainingTimeMs) {
                // 商户收银台得到USERPAYING状态后,经过商户后台系统调用【查询订单API】查询实际支付结果。
                orderQueryMap = super.orderQuery(requestData);
                String returnCodeForQuery = orderQueryMap.get("return_code");
                if (WXPayConstants.SUCCESS.equals(returnCodeForQuery)) {
                    // 通讯成功
                    String tradeState = orderQueryMap.get("trade_state");
                    if (WXPayConstants.SUCCESS.equals(tradeState)) {
                        // 如果成功了直接将查询结果返回
                        return orderQueryMap;
                    }
                    // 如果支付结果仍为USERPAYING,则每隔5秒循环调用【查询订单API】判断实际支付结果
                    Thread.sleep(1000);
                }
            }
            // 如果用户取消支付或累计30秒用户都未支付,商户收银台退出查询流程后继续调用【撤销订单API】撤销支付交易。
            String tradeState = orderQueryMap.get("trade_state");
            if (TRADE_STATE_NOTPAY.equals(tradeState) || ERR_CODE_USERPAYING.equals(tradeState) || ERR_CODE_AUTHCODEEXPIRE.equals(tradeState)) {
                Map<String, String> reverseMap = this.reverse(requestData);
                String returnCodeForReverse = reverseMap.get("return_code");
                String resultCode = reverseMap.get("result_code");
                if (WXPayConstants.SUCCESS.equals(returnCodeForReverse) && WXPayConstants.SUCCESS.equals(resultCode)) {
                    // 如果撤销成功,需要告诉客户端已经撤销订单了
                    responseMapForPay.put("err_code_des", "用户取消支付或尚未支付,后台已经撤销该订单,请重新支付!");
                }
            }
        }
    }
    return responseMapForPay;
}
/**
 * 从request的inputStream中获取参数
 * @param request
 * @return
 * @throws Exception
 */
public Map<String, String> getNotifyParameter(HttpServletRequest request) throws Exception {
    InputStream inputStream = request.getInputStream();
    ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
    byte[] buffer = new byte[1024];
    int length = 0;
    while ((length = inputStream.read(buffer)) != -1) {
        outSteam.write(buffer, 0, length);
    }
    outSteam.close();
    inputStream.close();
    // 获取微信调用我们notify_url的返回信息
    String resultXml = new String(outSteam.toByteArray(), "utf-8");
    Map<String, String> notifyMap = WXPayUtil.xmlToMap(resultXml);
    return notifyMap;
}
/**
 * 解密退款通知
 * <a href="https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_16&index=11>退款结果通知文档</a>
 * @return
 * @throws Exception
 */
public Map<String, String> decodeRefundNotify(HttpServletRequest request) throws Exception {
    // 从request的流中获取参数
    Map<String, String> notifyMap = this.getNotifyParameter(request);
    log.info(notifyMap.toString());
    String reqInfo = notifyMap.get("req_info");
    //(1)对加密串A做base64解码,得到加密串B
    byte[] bytes = new BASE64Decoder().decodeBuffer(reqInfo);
    //(2)对商户key做md5,得到32位小写key* ( key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置 )
    Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING);
    SecretKeySpec key = new SecretKeySpec(WXPayUtil.MD5(config.getKey()).toLowerCase().getBytes(), ALGORITHM);
    cipher.init(Cipher.DECRYPT_MODE, key);
    //(3)用key*对加密串B做AES-256-ECB解密(PKCS7Padding)
    // java.security.InvalidKeyException: Illegal key size or default parameters
    // https://www.cnblogs.com/yaks/p/5608358.html
    String responseXml = new String(cipher.doFinal(bytes),"UTF-8");
    Map<String, String> responseMap = WXPayUtil.xmlToMap(responseXml);
    return responseMap;
}
/**
 * 获取沙箱环境验签秘钥API
 * <a href="https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=23_1">获取验签秘钥API文档</a>
 * @return
 * @throws Exception
 */
public Map<String, String> getSignKey() throws Exception {
    Map<String, String> reqData = new HashMap<>();
    reqData.put("mch_id", config.getMchID());
    reqData.put("nonce_str", WXPayUtil.generateNonceStr());
    String sign = WXPayUtil.generateSignature(reqData, config.getKey(), WXPayConstants.SignType.MD5);
    reqData.put("sign", sign);
    String responseXml = this.requestWithoutCert("https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey", reqData,
            config.getHttpConnectTimeoutMs(), config.getHttpReadTimeoutMs());
    Map<String, String> responseMap = WXPayUtil.xmlToMap(responseXml);
    return responseMap;
}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值