Java实现微信(主、子商户模式)及支付宝支付

一、业务需求

实现APP微信、支付宝支付,后端需要做生成预支付单,响应支付结果;微信商户采用子商户模式

二、参考官方文档

微信普通商户:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1

微信服务商:https://pay.weixin.qq.com/wiki/doc/api/app/app_sl.php?chapter=9_1

支付宝API开发文档:https://docs.open.alipay.com/api_1/alipay.trade.app.pay

三、准备工作

在公众平台完成入驻等操作,拿到以下信息

普通商户:

参数API参数名详细说明
APPIDappidappid是微信公众账号或开放平台APP的唯一标识,在公众平台申请公众账号或者在开放平台申请APP账号后,微信会自动分配对应的appid,用于标识该应用。可在微信公众平台-->开发者中心查看,商户的微信支付审核通过邮件中也会包含该字段值。
微信支付商户号mch_id商户申请微信支付后,由微信支付分配的商户收款账号。
API密钥key交易过程生成签名的密钥,仅保留在商户系统和微信支付后台,不会在网络中传播。商户妥善保管该Key,切勿在网络中传输,不能在其他客户端中存储,保证key不会被泄漏。商户可根据邮件提示登录微信商户平台进行设置。也可按以下路径设置:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
AppsecretsecretAppSecret是APPID对应的接口密码,用于获取接口调用凭证access_token时使用。

服务商:

参数API参数名详细说明
受理商户的APPIDappidappid是微信公众账号或开放平台APP的唯一标识,在公众平台申请公众账号或者在开放平台申请APP账号后,微信会自动分配对应的appid,用于标识该应用。可在微信公众平台-->开发者中心查看,商户的微信支付审核通过邮件中也会包含该字段值。
受理商户的商户号mch_id受理商户申请微信支付后,由微信支付分配的商户账号。
受理商户的API密钥key交易过程生成签名的密钥,仅保留在商户系统和微信支付后台,不会在网络中传播。商户妥善保管该Key,切勿在网络中传输,不能在其他客户端中存储,保证key不会被泄漏。商户可根据邮件提示登录微信商户平台进行设置。也可按一下路径设置:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
子商户商户号sub_mch_id子商户申请的商户号或商户识别码
子商户应用APPIDsub_appid子商户在开放平台申请的应用APPID
商户号应用的AppsecretsecretAppSecret是子商户应用APPID对应的接口密码,用于获取接口调用凭证access_token时使用。

如使用子商户,需要先进行授权

支付宝参考文档获取相关参数

四、代码

--微信

API常量类

public class TransferConstants {
/** =============================================微信================================================*/

    /**统一下单API*/
    public static final String UNIFIEDORDER_PAY = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    /**企业付款API*/
    public static final String TRANSFERS_PAY = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
    /**申请退款API*/
    public static final String REFUND_PAY = "https://api.mch.weixin.qq.com/secapi/pay/refund";
    /**企业付款查询API*/
    public static final String TRANSFERS_PAY_QUERY = "https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo";
    /**公众账号appid*/
    public static final String APP_ID = ""  ;
    /**商户号*/
    public static final String MCH_ID = "";
    /**API密钥 填服务商的*/
    public static final String API_SECRET = "";
    /**子商户应用ID*/
    public static final String SUB_APPID = "";
    /**子商户号 */
    public static final String SUB_MCH_ID = "";

/** =============================================支付宝================================================*/
    /**支付宝网关(固定)*/
    public static final String URL = "https://openapi.alipay.com/gateway.do";
    /**APPID 即创建应用后生成*/
    public static final String APPID = "";
    /**开发者私钥,由开发者自己生成*/
    public static final String APP_PRIVATE_KEY =  "";
    /**参数返回格式,只支持 json*/
    public static final String FORMAT = "json";
    /**编码集,支持 GBK/UTF-8*/
    public static final String CHARSET = "UTF-8";
    /**支付宝公钥,由支付宝生成)*/
    public static final String ALIPAY_PUBLIC_KEY = "";
    /**商户生成签名字符串所使用的签名算法类型,目前支持 RSA2 和 RSA,推荐使用 RSA2*/
    public static final String SIGN_TYPE = "RSA2";

}

在线上支付时,遇到了支付后微信那边不能回调,后来写了一个主动回调的方法

首先在application.yml配置支付回调地址

wxAndAli:
    aliNotifyUrl: http://***:8082/app/appPay/aliPayNotify
    wxNotifyUrl: http://***:8082/app/appPay/wxPayNotify

获取支付回调地址

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * 支付回调地址
 */
@Component
public class PayConstants {
    public static String aliNotifyUrl;
    public static String wxNotifyUrl;

    @Value("${wxAndAli.aliNotifyUrl}")
    public void setAliNotifyUrl(String aliNotifyUrl) {
        this.aliNotifyUrl = aliNotifyUrl;
    }

    @Value("${wxAndAli.wxNotifyUrl}")
    public void setWxNotifyUrl(String wxNotifyUrl) {
        this.wxNotifyUrl = wxNotifyUrl;
    }
}

JSON数据处理类

public class JSONObject extends LinkedHashMap<String, Object>
{
    private static final long serialVersionUID = 1L;
    private static final Pattern arrayNamePattern = Pattern.compile("(\\w+)((\\[\\d+\\])+)");
    private static final ObjectMapper objectMapper = new ObjectMapper();

public static String valueAsStr(Object value)
    {
        if (value instanceof String)
        {
            return (String) value;
        }
        else if (value != null)
        {
            return value.toString();
        }
        else
        {
            return null;
        }
    }
}

支付工具类

import com.github.pagehelper.util.StringUtil;
import com.spa.common.utils.security.Md5Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.*;

/**
 * @author JC
 * @version 创建时间:2019/5/6 14:13
 */
public class PayUtil {

    private static final Logger logger = LoggerFactory.getLogger(PayUtil.class);
    /**
     * 生成订单号
     *
     * @return
     */
    public static String getOrderNo() {
        // 自增8位数 00000001
        return "ONO" + DateUtils.dateTimeNow() + "00000001";
    }

    /**
     * 退款单号
     *
     * @return
     */
    public static String getRefundNo() {
        // 自增8位数 00000001

        return "RNO" + DateUtils.dateTimeNow() + "00000001";
    }

    /**
     * 交易单号
     *
     * @return
     */
    public static String getTransferNo() {
        // 自增8位数 00000001
        return "TNO" + DateUtils.dateTimeNow() + "00000001";
    }

    /**
     * 支付单号
     *
     * @return
     */
    public static String getPayNo() {
        // 自增8位数 00000001
        return "PNO" + DateUtils.dateTimeNow() + "00000001";
    }

    /**
     * 门店进账单号
     *
     * @return
     */
    public static String getReceiptNo() {
        // 自增8位数 00000001
        return "SRNO" + DateUtils.dateTimeNow() + "00000001";
    }

    /**
     * 参团编号
     *
     * @return
     */
    public static String getGroupNo() {
        // 自增8位数 00000001
        return "GNO" + DateUtils.dateTimeNow() + "00000001";
    }

    /**
     * 返回客户端ip
     *
     * @param request
     * @return
     */
    public static String getRemoteAddrIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (StringUtil.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个ip值,第一个ip才是真实ip
            int index = ip.indexOf(",");
            if (index != -1) {
                return ip.substring(0, index);
            } else {
                return ip;
            }
        }
        ip = request.getHeader("X-Real-IP");
        if (StringUtil.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
            return ip;
        }
        return request.getRemoteAddr();
    }

    /**
     * 获取服务器的ip地址
     *
     * @param request
     * @return
     */
    public static String getLocalIp(HttpServletRequest request) {
        return request.getLocalAddr();
    }

    /**
     * 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)
     * @param params
     * @return
     */
    public static Map<Object,Object> formatASCIIMap(Map<String, String> params){
        Map<String, String> tmpMap = params;
        Map<Object, Object> params1 = new LinkedHashMap<>();
        List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(tmpMap.entrySet());


        //对所有传入参数按照字段名的ASCII码从小到大排序(字典序)
        Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>() {
            public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
                return (o1.getKey()).toString().compareTo(o2.getKey());
            }
        });


        for (Map.Entry<String, String> item : infoIds) {
            if (StringUtils.isNotBlank((CharSequence) item.getKey())) {
                String key = item.getKey();
                String value = item.getValue();
                params1.put(key,value);
            }
        }

        return params1;
    }

    public static String getSign(Map<Object, Object> params, String paternerKey) throws UnsupportedEncodingException {

        return Md5Utils.MD5Encode(createSign(params, false) + "&key=" + paternerKey).toUpperCase();
    }

    /**
     * 退款时生成签名
     * @param params
     * @param paternerKey
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String getRefundSign(Map<Object, Object> params, String paternerKey) throws UnsupportedEncodingException {
        return Md5Utils.MD5Encode(createRefundSign(params, false) + "&key=" + paternerKey).toUpperCase();
    }

    /**
     * 构造签名
     *
     * @param params
     * @param encode
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String createSign(Map<Object, Object> params, boolean encode) throws UnsupportedEncodingException {
        Set<Object> keysSet = params.keySet();
        Object[] keys = keysSet.toArray();
        Arrays.sort(keys);
        StringBuffer temp = new StringBuffer();
        boolean first = true;
        for (Object key : keys) {
            if (key == null || StringUtils.isNull(params.get(key))) // 参数为空不参与签名
                continue;
            if (first) {
                first = false;
            } else {
                temp.append("&");
            }
            temp.append(key).append("=");
            Object value = params.get(key);
            String valueStr = "";
            if (null != value) {
                valueStr = value.toString();
            }
            if (encode) {
                temp.append(URLEncoder.encode(valueStr, "UTF-8"));
            } else {
                temp.append(valueStr);
            }
        }
        return temp.toString();
    }

    /**
     * 构造退款时签名
     *
     * @param params
     * @param encode
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String createRefundSign(Map<Object, Object> params, boolean encode) throws UnsupportedEncodingException {
        Set<Object> keysSet = params.keySet();
        Object[] keys = keysSet.toArray();
        Arrays.sort(keys);
        StringBuffer temp = new StringBuffer();
        boolean first = true;
        for (Object key : keys) {
            if (key == null || StringUtils.isNull(params.get(key))) // 参数为空不参与签名
                continue;
            if (first) {
                first = false;
            } else {
                temp.append("&");
            }
            temp.append(key).append("=");
            Object value = params.get(key);
            String valueStr = "";
            if (null != value) {
                valueStr = value.toString();
            }
            if (encode) {
                temp.append(URLEncoder.encode(valueStr, "UTF-8"));
            } else {
                temp.append(valueStr);
            }
        }
        return temp.toString();
    }

    /**
     * 创建支付随机字符串
     *
     * @return
     */
    public static String getNonceStr() {
        return StringUtils.getRandomString(32);
    }

    /**
     * 支付时间戳
     *
     * @return
     */
    public static String payTimestamp() {
        return Long.toString(System.currentTimeMillis() / 1000);
    }

}

xml、map转换工具类

public class XmlUtil {

    private static final String PREFIX_XML = "<xml>";

    private static final String SUFFIX_XML = "</xml>";

    private static final String PREFIX_CDATA = "<![CDATA[";

    private static final String SUFFIX_CDATA = "]]>";

    /**
     * 转化成xml, 单层无嵌套
     *
     * @param parm
     * @param isAddCDATA
     * @return
     */
    public static String xmlFormat(Map<String, String> parm, boolean isAddCDATA) {

        StringBuffer strbuff = new StringBuffer(PREFIX_XML);
        if (CollectionUtil.isNotEmpty(parm)) {
            for (Entry<String, String> entry : parm.entrySet()) {
                strbuff.append("<").append(entry.getKey()).append(">");
                if (isAddCDATA) {
                    strbuff.append(PREFIX_CDATA);
                    if (StringUtil.isNotEmpty(entry.getValue())) {
                        strbuff.append(entry.getValue());
                    }
                    strbuff.append(SUFFIX_CDATA);
                } else {
                    if (StringUtil.isNotEmpty(entry.getValue())) {
                        strbuff.append(entry.getValue());
                    }
                }
                strbuff.append("</").append(entry.getKey()).append(">");
            }
        }
        return strbuff.append(SUFFIX_XML).toString();
    }

    /**
     * Object,转化成xml
     * @param parm
     * @param isAddCDATA
     * @return
     */
    public static String xmlFormatObject(Map<Object, Object> parm, boolean isAddCDATA) {

        StringBuffer strbuff = new StringBuffer(PREFIX_XML);
        if (CollectionUtil.isNotEmpty(parm)) {
            for (Entry<Object, Object> entry : parm.entrySet()) {
                strbuff.append("<").append(entry.getKey()).append(">");
                if (isAddCDATA) {
                    strbuff.append(PREFIX_CDATA);
                    if (StringUtils.isNotNull(entry.getValue())) {
                        strbuff.append(entry.getValue());
                    }
                    strbuff.append(SUFFIX_CDATA);
                } else {
                    if (StringUtils.isNotNull(entry.getValue())) {
                        strbuff.append(entry.getValue());
                    }
                }
                strbuff.append("</").append(entry.getKey()).append(">");
            }
        }
        return strbuff.append(SUFFIX_XML).toString();
    }

    /**
     * 解析xml
     *
     * @param xml
     * @return
     * @throws XmlPullParserException
     * @throws IOException
     */
    public static Map<String, String> xmlParse(String xml) throws XmlPullParserException, IOException {
        Map<String, String> map = null;
        if (StringUtil.isNotEmpty(xml)) {
            InputStream inputStream = new ByteArrayInputStream(xml.getBytes());
            XmlPullParser pullParser = XmlPullParserFactory.newInstance().newPullParser();
            pullParser.setInput(inputStream, "UTF-8"); // 为xml设置要解析的xml数据
            int eventType = pullParser.getEventType();

            while (eventType != XmlPullParser.END_DOCUMENT) {
                switch (eventType) {
                    case XmlPullParser.START_DOCUMENT:
                        map = new HashMap<String, String>();
                        break;
                    case XmlPullParser.START_TAG:
                        String key = pullParser.getName();
                        if (key.equals("xml"))
                            break;
                        String value = pullParser.nextText().trim();
                        map.put(key, value);
                        break;
                    case XmlPullParser.END_TAG:
                        break;
                }
                eventType = pullParser.next();
            }
        }
        return map;
    }

    /**
     * Object,解析xml
     * @param xml
     * @return
     * @throws XmlPullParserException
     * @throws IOException
     */
    public static Map<Object, Object> xmlParseObject(String xml) throws XmlPullParserException, IOException {
        Map<Object, Object> map = null;
        if (StringUtil.isNotEmpty(xml)) {
            InputStream inputStream = new ByteArrayInputStream(xml.getBytes());
            XmlPullParser pullParser = XmlPullParserFactory.newInstance().newPullParser();
            pullParser.setInput(inputStream, "UTF-8"); // 为xml设置要解析的xml数据
            int eventType = pullParser.getEventType();

            while (eventType != XmlPullParser.END_DOCUMENT) {
                switch (eventType) {
                    case XmlPullParser.START_DOCUMENT:
                        map = new HashMap<Object, Object>();
                        break;
                    case XmlPullParser.START_TAG:
                        String key = pullParser.getName();
                        if (key.equals("xml"))
                            break;
                        String value = pullParser.nextText().trim();
                        map.put(key, value);
                        break;
                    case XmlPullParser.END_TAG:
                        break;
                }
                eventType = pullParser.next();
            }
        }
        return map;
    }
}

 Md5加密工具类

public class Md5Utils
{
    private static final Logger log = LoggerFactory.getLogger(Md5Utils.class);

    private static byte[] md5(String s)
    {
        MessageDigest algorithm;
        try
        {
            algorithm = MessageDigest.getInstance("MD5");
            algorithm.reset();
            algorithm.update(s.getBytes("UTF-8"));
            byte[] messageDigest = algorithm.digest();
            return messageDigest;
        }
        catch (Exception e)
        {
            log.error("MD5 Error...", e);
        }
        return null;
    }

    private static final String toHex(byte hash[])
    {
        if (hash == null)
        {
            return null;
        }
        StringBuffer buf = new StringBuffer(hash.length * 2);
        int i;

        for (i = 0; i < hash.length; i++)
        {
            if ((hash[i] & 0xff) < 0x10)
            {
                buf.append("0");
            }
            buf.append(Long.toString(hash[i] & 0xff, 16));
        }
        return buf.toString();
    }

    public static String hash(String s)
    {
        try
        {
            return new String(toHex(md5(s)).getBytes("UTF-8"), "UTF-8");
        }
        catch (Exception e)
        {
            log.error("not supported charset...{}", e);
            return s;
        }
    }

    public static String MD5Encode(String s) {

        try {
            // 得到一个信息摘要器
            MessageDigest digest = MessageDigest.getInstance("md5");
            byte[] result = digest.digest(s.getBytes());
            StringBuffer buffer = new StringBuffer();
            // 把每一个byte 做一个与运算 0xff;
            for (byte b : result) {
                // 与运算
                int number = b & 0xff;// 加盐
                String str = Integer.toHexString(number);
                if (str.length() == 1) {
                    buffer.append("0");
                }
                buffer.append(str);
            }

            // 标准的md5加密后的结果
            return buffer.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return "";
        }

    }

    private static String byteArrayToHexString(byte b[]) {
        StringBuffer resultSb = new StringBuffer();
        for (int i = 0; i < b.length; i++)
            resultSb.append(byteToHexString(b[i]));

        return resultSb.toString();
    }

    private static String byteToHexString(byte b) {
        int n = b;
        if (n < 0)
            n += 256;
        int d1 = n / 16;
        int d2 = n % 16;
        return hexDigits[d1] + hexDigits[d2];
    }

    public static String MD5Encode(String origin, String charsetname) {
        String resultString = null;
        try {
            resultString = new String(origin);
            MessageDigest md = MessageDigest.getInstance("MD5");
            if (charsetname == null || "".equals(charsetname))
                resultString = byteArrayToHexString(md.digest(resultString
                        .getBytes()));
            else
                resultString = byteArrayToHexString(md.digest(resultString
                        .getBytes(charsetname)));
        } catch (Exception exception) {
        }
        return resultString;
    }

    private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
            "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
}

微信签名相关工具类

@Slf4j
public class WXPayUtil1 {
    private static final Logger log = LoggerFactory.getLogger(WXPayUtil1.class);

    public static Map<Object, Object> wxPay(Map map) {
        Map<Object, Object> resultMap = new HashMap<>();
        Map<String, String> parameters = null;
        try {
            parameters = new LinkedHashMap<>();
            parameters.put("appid", TransferConstants.APP_ID); //公众账号appid
            parameters.put("mch_id", TransferConstants.MCH_ID); //商户号
            parameters.put("sub_appid", TransferConstants.SUB_APPID); //子商户应用ID
            parameters.put("sub_mch_id", TransferConstants.SUB_MCH_ID); //子商户号
            parameters.put("nonce_str", PayUtil.getNonceStr()); //随机字符串
            parameters.put("out_trade_no", (String) map.get("transferNo")); //商户订单号
            parameters.put("total_fee", (String)map.get("money")); //转账金额
            parameters.put("body", (String) map.get("desc")); //企业付款描述信息
            parameters.put("spbill_create_ip", (String) map.get("ip")); //Ip地址
            parameters.put("notify_url", PayConstants.wxNotifyUrl);              //回调通知地址
            parameters.put("trade_type", "APP");
//            String sign = PayUtil.getSign(parameters, TransferConstants.WX_KEY);
            Map<String, String> tmpMap = parameters;
            Map<Object,Object> params1 = PayUtil.formatASCIIMap(tmpMap);
            //生成sign
            String sign = PayUtil.getSign(params1, TransferConstants.API_SECRET);
            parameters.put("sign", sign);
           Map<String, String> tmpMap2 = parameters;
           Map<Object,Object> params2 = PayUtil.formatASCIIMap(tmpMap2);
            //生成签名
            resultMap = createSecondSign(params2);

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return resultMap;
    }

    /**
     * 微信支付签名算法sign
     *
     * @param parameters
     * @return
     */
    public static Map<Object, Object> createSecondSign(Map<Object, Object> parameters) {
        Map<Object, Object> resultMap = new HashMap<>();
        //微信支付
        String xml = XmlUtil.xmlFormatObject(parameters, false);
        log.info("----签名-------"+ xml);
        String restxml = HttpUtilA.posts(TransferConstants.UNIFIEDORDER_PAY, XmlUtil.xmlFormatObject(parameters, false));
        log.info("----发送签名-------"+ restxml);
        Map<String, String> returnMap = null;
        try {
            returnMap = XmlUtil.xmlParse(restxml);
            log.info("-----------"+ JSONObject.valueAsStr(returnMap));
            // 通信标识和交易标识都为SUCCESS
            if (CollectionUtil.isNotEmpty(returnMap) && "SUCCESS".equals(returnMap.get("return_code")) && "SUCCESS".equals(returnMap.get("result_code"))) {
                SortedMap<Object,Object> parameterMap = new TreeMap<Object,Object>();
                String prepayid = returnMap.get("prepay_id");// 预支付交易会话标识
                String noncestr = returnMap.get("nonce_str");// 微信返回的随机字符串
                parameterMap.put("appid", TransferConstants.SUB_APPID);
                parameterMap.put("partnerid", TransferConstants.SUB_MCH_ID);
                parameterMap.put("prepayid", prepayid);
                parameterMap.put("package", "Sign=WXPay");
                parameterMap.put("noncestr", noncestr);
                parameterMap.put("timestamp", PayUtil.payTimestamp());
                //生成sign
                String sign = PayUtil.getSign(parameterMap, TransferConstants.API_SECRET);
                parameterMap.put("sign", sign);
                log.info("-----------"+ JSONObject.valueAsStr(parameterMap));
                resultMap.put("code", "200");
                resultMap.put("msg", parameterMap);
            }
        } catch (XmlPullParserException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return resultMap;
    }

     /**
     * <p>统一下单</p>
     *
     * @return
     * @throws Exception
     */
    public Map<Object, Object> pay(Map map) {
        Map<Object, Object> resultMap = new HashMap<>();
        Map<String, String> returnMap = null;
        try {
            returnMap = wxUnifieldOrder(map);
            // 通信标识和交易标识都为SUCCESS
            if (CollectionUtil.isNotEmpty(returnMap) && "SUCCESS".equals(returnMap.get("return_code")) && "SUCCESS".equals(returnMap.get("result_code"))) {
                resultMap.put("returnCode", returnMap.get("return_code"));
                resultMap.put("returnMsg", "OK");
                resultMap.put("codeUrl", returnMap.get("code_url"));
                resultMap.put("payMethod", "微信扫码支付");
            } else {
                resultMap.put("returnCode", returnMap.get("return_code"));
                resultMap.put("returnMsg", returnMap.get("return_msg"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultMap;
    }

    /**
     * <p>微信支付统一下单</p>
     *
     * @return
     * @throws Exception
     */
    private Map<String, String> wxUnifieldOrder(Map map) throws Exception {
        //封装参数
        SortedMap<String, String> parameters = new TreeMap<String, String>();
        parameters.put("mch_appid", TransferConstants.APP_ID); //公众账号appid
        parameters.put("mchid", TransferConstants.MCH_ID); //商户号
        parameters.put("nonce_str", PayUtil.getNonceStr()); //随机字符串
        parameters.put("partner_trade_no", (String) map.get("transferNo")); //商户订单号
        parameters.put("openid", (String) map.get("openId")); //用户openid oCVr20N2YLH9VQztnkZTaCj2aYYY
        parameters.put("check_name", "NO_CHECK"); //校验用户姓名选项 OPTION_CHECK
        //parameters.put("re_user_name", "安迪"); //check_name设置为FORCE_CHECK或OPTION_CHECK,则必填
        parameters.put("amount", (String) map.get("money")); //转账金额
        parameters.put("desc", (String) map.get("desc")); //企业付款描述信息
        parameters.put("spbill_create_ip", (String) map.get("ip")); //Ip地址
//            parameters.put("notify_url", notifyUrl);              //回调通知地址
        parameters.put("trade_type", "APP");                  //支付类型
        parameters.put("sign", createSign(parameters, ""));
        //转换为xml
        String xmlData = mapToXml(parameters);
        //请求微信后台
        String resXml = HttpUtilA.posts(TransferConstants.UNIFIEDORDER_PAY, xmlData);
        log.info("【微信支付】 统一下单响应:\n" + resXml);
        return xmlStrToMap(resXml);
    }

    /**
     * Map转换为 Xml
     *
     * @return Xml
     * @throws Exception
     */
    public static String mapToXml(SortedMap<String, String> map) throws Exception {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        //防止XXE攻击
        documentBuilderFactory.setXIncludeAware(false);
        documentBuilderFactory.setExpandEntityReferences(false);
        DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
        org.w3c.dom.Document document = documentBuilder.newDocument();
        org.w3c.dom.Element root = document.createElement("xml");
        document.appendChild(root);
        for (String key : map.keySet()) {
            String value = map.get(key);
            if (value == null) {
                value = "";
            }
            value = value.trim();
            org.w3c.dom.Element filed = document.createElement(key);
            filed.appendChild(document.createTextNode(value));
            root.appendChild(filed);
        }
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        DOMSource source = new DOMSource(document);
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        StringWriter writer = new StringWriter();
        StreamResult result = new StreamResult(writer);
        transformer.transform(source, result);
        String output = writer.getBuffer().toString();
        try {
            writer.close();
        } catch (Exception ex) {
        }
        return output;
    }

    /**
     * 创建签名Sign
     *
     * @param key
     * @param parameters
     * @return
     */
    public static String createSign(SortedMap<String, String> parameters, String key) {
        StringBuffer sb = new StringBuffer();
        Set es = parameters.entrySet();
        Iterator<?> it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            if (entry.getValue() != null || !"".equals(entry.getValue())) {
                String v = String.valueOf(entry.getValue());
                if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
                    sb.append(k + "=" + v + "&");
                }
            }
        }
        sb.append("key=" + key);
        String sign = MD5Util.MD5Encode(sb.toString(), "UTF-8").toUpperCase();
        return sign;
    }

    /**
     * Xml字符串转换为Map
     *
     * @param xmlStr
     * @return
     */
    public static Map<String, String> xmlStrToMap(String xmlStr) {
        Map<String, String> map = new HashMap<String, String>();
        Document doc;
        try {
            doc = DocumentHelper.parseText(xmlStr);
            Element root = doc.getRootElement();
            List children = root.elements();
            if (children != null && children.size() > 0) {
                for (int i = 0; i < children.size(); i++) {
                    Element child = (Element) children.get(i);
                    map.put(child.getName(), child.getTextTrim());
                }
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return map;
    }
}

 微信异步请求处理

/**
     * 微信异步请求逻辑处理
     *
     * @param inStream
     * @return
     */
    @Override
    public ResultData wxNotify(InputStream inStream) {
        int code = -1;
        Map<String, String> return_data = new HashMap<String, String>();
        try {
            ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = inStream.read(buffer)) != -1) {
                outSteam.write(buffer, 0, len);
            }
            String resultxml = new String(outSteam.toByteArray(), "utf-8");
            Map<String, String> params = XmlUtil.xmlParse(resultxml);
            outSteam.close();
            inStream.close();

            if (!PayCommonUtil.isTenpaySign("utf-8", params)) {
                // 支付失败
                return_data.put("return_code", "FAIL");
                return_data.put("return_msg", "return_code不正确");
                code = 4;
            } else {
                System.out.println("===============付款成功==============");
                String outTradeNo = String.valueOf(Long.parseLong(params.get("out_trade_no").split("O")[0]));
                // 查询订单
                AppOrders appOrders = appOrdersService.selectAppOrdersByOrderNo(outTradeNo);
                if ("0".equals(appOrders.getStatus())){
                    log.info("-----微信异步回调,修改订单状态--------------"+appOrders.toString());
                    String totalFee = params.get("total_fee");
                    Map map = new HashMap();
                    map.put("orderNo", appOrders.getOrderNo());
                    map.put("payIntegral", appOrders.getPayIntegral());
                    map.put("payMoney", totalFee);
                    map.put("payWay", "2");
                    map.put("discountMoney", appOrders.getDiscountMoney());
                    appOrdersService.updateOrderStatusToPay(map);
                }

                String transactionId = params.get("transaction_id");
                // 只处理支付成功的订单: 修改交易表状态,支付成功
                //更新交易表中状态
                int returnResult = appTransactionHistoryService.updateTransactionHistoryByTransactionNo(outTradeNo, transactionId);
                log.info("==================returnResult:" + returnResult);
                if (returnResult > 0) {
                    log.info("==================更新交易表中状态成功:" + transactionId);
                    return_data.put("return_code", "SUCCESS");
                    return_data.put("return_msg", "OK");
                    code = 0;
                } else {
                    return new ResultData(4, "更新交易表失败 !");
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (XmlPullParserException e) {
            e.printStackTrace();
        }
        return new ResultData(code, XmlUtil.xmlFormat(return_data, false));
    }

--支付宝

支付宝实体类

public class AlipaymentOrderDto {
    //支付宝分配给开发者的应用Id
    private String appId;
    //通知时间:yyyy-MM-dd HH:mm:ss
    private String notifyTime;
    //交易创建时间:yyyy-MM-dd HH:mm:ss
    private String gmtCreate;
    //交易付款时间
    private String gmtPayment;
    //交易退款时间
    private String gmtRefund;
    //交易结束时间
    private String gmtClose;
    //支付宝的交易号
    private String tradeNo;
    //获取商户之前传给支付宝的订单号(商户系统的唯一订单号)
    private String outTradeNo;
    //商户业务号(商户业务ID,主要是退款通知中返回退款申请的流水号)
    private String outBizNo;
    //买家支付宝账号
    private String buyerLogonId;
    //卖家支付宝用户号
    private String sellerId;
    //卖家支付宝账号
    private String sellerEmail;
    //订单金额:本次交易支付的订单金额,单位为人民币(元)
    private Double totalAmount;
    //实收金额:商家在交易中实际收到的款项,单位为元
    private Double receiptAmount;
    //开票金额:用户在交易中支付的可开发票的金额
    private Double invoiceAmount;
    //付款金额:用户在交易中支付的金额
    private Double buyerPayAmount;
    // 获取交易状态
    private String tradeStatus;

    public String getAppId() {
        return appId;
    }

    public void setAppId(String appId) {
        this.appId = appId;
    }

    public String getNotifyTime() {
        return notifyTime;
    }

    public void setNotifyTime(String notifyTime) {
        this.notifyTime = notifyTime;
    }

    public String getGmtCreate() {
        return gmtCreate;
    }

    public void setGmtCreate(String gmtCreate) {
        this.gmtCreate = gmtCreate;
    }

    public String getGmtPayment() {
        return gmtPayment;
    }

    public void setGmtPayment(String gmtPayment) {
        this.gmtPayment = gmtPayment;
    }

    public String getGmtRefund() {
        return gmtRefund;
    }

    public void setGmtRefund(String gmtRefund) {
        this.gmtRefund = gmtRefund;
    }

    public String getGmtClose() {
        return gmtClose;
    }

    public void setGmtClose(String gmtClose) {
        this.gmtClose = gmtClose;
    }

    public String getTradeNo() {
        return tradeNo;
    }

    public void setTradeNo(String tradeNo) {
        this.tradeNo = tradeNo;
    }

    public String getOutTradeNo() {
        return outTradeNo;
    }

    public void setOutTradeNo(String outTradeNo) {
        this.outTradeNo = outTradeNo;
    }

    public String getOutBizNo() {
        return outBizNo;
    }

    public void setOutBizNo(String outBizNo) {
        this.outBizNo = outBizNo;
    }

    public String getBuyerLogonId() {
        return buyerLogonId;
    }

    public void setBuyerLogonId(String buyerLogonId) {
        this.buyerLogonId = buyerLogonId;
    }

    public String getSellerId() {
        return sellerId;
    }

    public void setSellerId(String sellerId) {
        this.sellerId = sellerId;
    }

    public String getSellerEmail() {
        return sellerEmail;
    }

    public void setSellerEmail(String sellerEmail) {
        this.sellerEmail = sellerEmail;
    }

    public Double getTotalAmount() {
        return totalAmount;
    }

    public void setTotalAmount(Double totalAmount) {
        this.totalAmount = totalAmount;
    }

    public Double getReceiptAmount() {
        return receiptAmount;
    }

    public void setReceiptAmount(Double receiptAmount) {
        this.receiptAmount = receiptAmount;
    }

    public Double getInvoiceAmount() {
        return invoiceAmount;
    }

    public void setInvoiceAmount(Double invoiceAmount) {
        this.invoiceAmount = invoiceAmount;
    }

    public Double getBuyerPayAmount() {
        return buyerPayAmount;
    }

    public void setBuyerPayAmount(Double buyerPayAmount) {
        this.buyerPayAmount = buyerPayAmount;
    }

    public String getTradeStatus() {
        return tradeStatus;
    }

    public void setTradeStatus(String tradeStatus) {
        this.tradeStatus = tradeStatus;
    }
}

支付宝支付工具类

import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.spa.app.controller.app.constants.PayConstants;
import com.spa.app.controller.app.dto.AlipaymentOrderDto;
import com.spa.common.constant.TransferConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;

public class AliPayUtil {
    private static final Logger log = LoggerFactory.getLogger(AliPayUtil.class);

    // 服务器异步通知页面路径  需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
//    public static String notify_url = "http://工程公网访问地址/alipay.trade.page.pay-JAVA-UTF-8/notify_url.jsp";
    // 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
//    public static String return_url = "http://工程公网访问地址/alipay.trade.page.pay-JAVA-UTF-8/return_url.jsp";
    // 字符编码格式
    public static String charset = "utf-8";

    /**
     * 支付
     *
     * @param map
     * @return
     */
    public static String pay(Map map) {
//    public static JSONObject pay(Map map) {
        //最终返回加签之后的,app需要传给支付宝app的订单信息字符串
        String result = "";
//        JSONObject result = new JSONObject();
        try {
            //商户订单号,商户网站订单系统中唯一订单号,必填
            String out_trade_no = (String) map.get("orderNo");
            //付款金额,必填
            String total_amount = (String) map.get("money");
            //订单名称,必填
            String subject = (String) map.get("orderName");

            //实例化客户端(参数:网关地址、商户appid、商户私钥、格式、编码、支付宝公钥、加密类型),为了取得预付订单信息
            AlipayClient alipayClient = new DefaultAlipayClient(TransferConstants.URL, TransferConstants.APPID, TransferConstants.APP_PRIVATE_KEY, "json", charset, TransferConstants.ALIPAY_PUBLIC_KEY, TransferConstants.SIGN_TYPE);

            //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
            AlipayTradeAppPayRequest ali_request = new AlipayTradeAppPayRequest();

            //SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式
            AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();

            //业务参数传入,可以传很多,参考API
            //model.setPassbackParams(URLEncoder.encode(request.getBody().toString())); //公用参数(附加数据)
//            model.setBody(orderTest.getBody());                       //对一笔交易的具体描述信息。如果是多种商品,请将商品描述字符串累加传给body。

            model.setSubject(subject);                 //商品名称
            log.info("====================商户订单号:" + out_trade_no);
            model.setOutTradeNo(out_trade_no);           //商户订单号(自动生成)
            // model.setTimeoutExpress("30m");     			  //交易超时时间
            model.setTotalAmount(total_amount);         //支付金额
//            model.setProductCode("QUICK_MSECURITY_PAY");              //销售产品码(固定值)
            ali_request.setBizModel(model);
            log.info("====================异步通知的地址为:" + PayConstants.aliNotifyUrl);
            ali_request.setNotifyUrl(PayConstants.aliNotifyUrl);        //异步回调地址(后台)
//            ali_request.setReturnUrl(return_url);        //同步回调地址(APP)

            // 这里和普通的接口调用不同,使用的是sdkExecute
            AlipayTradeAppPayResponse alipayTradeAppPayResponse = alipayClient.sdkExecute(ali_request); //返回支付宝订单信息(预处理)
            log.info("-------------" + JSONObject.toJSONString(alipayTradeAppPayResponse));
            result = alipayTradeAppPayResponse.getBody();//就是orderString 可以直接给APP请求,无需再做处理。
            if (alipayTradeAppPayResponse.isSuccess()) {
//                log.info("调用成功");
//                String sign = DigestUtils.md5Hex(out_trade_no).toUpperCase();
//                result.put("code", "SUCCESS");
//                result.put("out_trade_no", out_trade_no);
//                result.put("sign", sign);
//                result.put("orderStr", result);
//                log.info(result.toJSONString());
//                log.info(result.toString());
            } else {
                log.info("与支付宝交互出错,未能生成订单,请检查代码!");
                return null;
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
            log.info("与支付宝交互出错,未能生成订单,请检查代码!");
        }
        return result;
    }

    /**
     * 支付宝异步请求逻辑处理
     *
     * @return
     */
    public static AlipaymentOrderDto notify(Map<String, String> conversionParams) {
        AlipaymentOrderDto alipaymentOrder = new AlipaymentOrderDto();
        log.info("==================支付宝异步请求逻辑处理");
        //签名验证(对支付宝返回的数据验证,确定是支付宝返回的)
        boolean signVerified = false;
        try {
            //调用SDK验证签名
            signVerified = AlipaySignature.rsaCheckV1(conversionParams, TransferConstants.ALIPAY_PUBLIC_KEY, TransferConstants.CHARSET, TransferConstants.SIGN_TYPE);

        } catch (AlipayApiException e) {
            log.info("==================验签失败 !");
            e.printStackTrace();
        }
        //对验签进行处理
        if (signVerified) {
            //验签通过
            //获取需要保存的数据
            String appId = conversionParams.get("app_id");//支付宝分配给开发者的应用Id
            String notifyTime = conversionParams.get("notify_time");//通知时间:yyyy-MM-dd HH:mm:ss
            String gmtCreate = conversionParams.get("gmt_create");//交易创建时间:yyyy-MM-dd HH:mm:ss
            String gmtPayment = conversionParams.get("gmt_payment");//交易付款时间
            String gmtRefund = conversionParams.get("gmt_refund");//交易退款时间
            String gmtClose = conversionParams.get("gmt_close");//交易结束时间
            String tradeNo = conversionParams.get("trade_no");//支付宝的交易号
            String outTradeNo = conversionParams.get("out_trade_no");//获取商户之前传给支付宝的订单号(商户系统的唯一订单号)
            String outBizNo = conversionParams.get("out_biz_no");//商户业务号(商户业务ID,主要是退款通知中返回退款申请的流水号)
            String buyerLogonId = conversionParams.get("buyer_logon_id");//买家支付宝账号
            String sellerId = conversionParams.get("seller_id");//卖家支付宝用户号
            String sellerEmail = conversionParams.get("seller_email");//卖家支付宝账号
            String totalAmount = conversionParams.get("total_amount");//订单金额:本次交易支付的订单金额,单位为人民币(元)
            String receiptAmount = conversionParams.get("receipt_amount");//实收金额:商家在交易中实际收到的款项,单位为元
            String invoiceAmount = conversionParams.get("invoice_amount");//开票金额:用户在交易中支付的可开发票的金额
            String buyerPayAmount = conversionParams.get("buyer_pay_amount");//付款金额:用户在交易中支付的金额
            String tradeStatus = conversionParams.get("trade_status");// 获取交易状态
            //赋值
            alipaymentOrder.setNotifyTime(notifyTime);
            alipaymentOrder.setGmtCreate(gmtCreate);
            alipaymentOrder.setGmtPayment(gmtPayment);
            alipaymentOrder.setGmtRefund(gmtRefund);
            alipaymentOrder.setGmtClose(gmtClose);
            alipaymentOrder.setTradeNo(tradeNo);
            alipaymentOrder.setOutTradeNo(outTradeNo);
            alipaymentOrder.setOutBizNo(outBizNo);
            alipaymentOrder.setBuyerLogonId(buyerLogonId);
            alipaymentOrder.setSellerId(sellerId);
            alipaymentOrder.setSellerEmail(sellerEmail);
            alipaymentOrder.setTotalAmount(Double.parseDouble(totalAmount));
            alipaymentOrder.setReceiptAmount(Double.parseDouble(receiptAmount));
            alipaymentOrder.setInvoiceAmount(Double.parseDouble(invoiceAmount));
            alipaymentOrder.setBuyerPayAmount(Double.parseDouble(buyerPayAmount));
            alipaymentOrder.setTradeStatus(tradeStatus);
            //支付宝官方建议校验的值(out_trade_no、total_amount、sellerId、app_id)
            if (!TransferConstants.APPID.equals(appId)) {
                return null;
            }
        }
        return alipaymentOrder;
    }
}

支付宝异步请求处理

/**
     * 支付宝异步请求逻辑处理
     *
     * @param aliParams
     * @return
     */
    @Transactional
    @Override
    public ResultData notify(Map<String, String[]> aliParams) {
        //用以存放转化后的参数集合
        Map<String, String> conversionParams = new HashMap<String, String>();
        for (Iterator<String> iter = aliParams.keySet().iterator(); iter.hasNext(); ) {
            String key = iter.next();
            String[] values = aliParams.get(key);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
            }
            // 乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
//             valueStr = new String(valueStr.getBytes("ISO-8859-1"), "uft-8");
            log.info("---------------------" + valueStr);
            conversionParams.put(key, valueStr);
        }

        log.info("==================返回参数集合:" + conversionParams);
        AlipaymentOrderDto alipaymentOrder = AliPayUtil.notify(conversionParams);
        if (alipaymentOrder != null) {
            // 查询订单
            AppOrders appOrders = appOrdersService.selectAppOrdersByOrderNo(alipaymentOrder.getOutTradeNo());
            if ("0".equals(appOrders.getStatus())){
                log.info("-----微信异步回调,修改订单状态--------------"+appOrders.toString());
                Double totalFee = alipaymentOrder.getTotalAmount();
                Map map = new HashMap();
                map.put("orderNo", appOrders.getOrderNo());
                map.put("payIntegral", appOrders.getPayIntegral());
                map.put("payMoney", totalFee);
                map.put("payWay", "1");
                map.put("discountMoney", appOrders.getDiscountMoney());
                appOrdersService.updateOrderStatusToPay(map);
            }
            //支付宝官方建议校验的值(out_trade_no、total_amount、sellerId、app_id)
            if (alipaymentOrder.getTradeStatus().equals("TRADE_SUCCESS")) {
                //只处理支付成功的订单: 修改交易表状态,支付成功
                //更新交易表中状态
                int returnResult = appTransactionHistoryService.updateTransactionHistoryByTransactionNo(alipaymentOrder.getOutTradeNo(), alipaymentOrder.getTradeNo());
                log.info("==================returnResult:" + returnResult);
                if (returnResult > 0) {
                    log.info("==================更新交易表中状态成功:" + alipaymentOrder.getTradeNo());
                    return ResultData.success();
                } else {
                    return new ResultData(4, "更新交易表失败 !");
                }
            } else {
                return new ResultData(4, "验签失败 !");
            }
        } else {
            return new ResultData(4, "验签失败 !");
        }
    }

以上是支付的步骤,欢迎大家提供好的建议或问题!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值